Announcement

Collapse

Looking for a User App or Add-On built by the NinjaTrader community?

Visit NinjaTrader EcoSystem and our free User App Share!

Have a question for the NinjaScript developer community? Open a new thread in our NinjaScript File Sharing Discussion Forum!
See more
See less

Partner 728x90

Collapse

Clarification on indicator instances

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Clarification on indicator instances

    Hi,

    I have come across this once before - Fortunately, after 30 minutes of scratching my head, I remembered and put in the fix which made it all work. I would appreciate it if someone could explain to me why this is the case so I can understand this better.

    I have some code which calls another indicator. Nothing complex, so indie1 = SMA(50) for example.

    Exact code below

    i = pjsRSI(Input,false);
    Print("wins1="+i.tradestat_wins);
    Print(i.tradestat_ready);
    Print("wins2="+i.tradestat_wins);
    Print(i.tradestat_ticks);
    Print("Result : Total trades = "+ (i.tradestat_wins+i.tradestat_losses).ToString()+" , w/l = "+i.tradestat_wins.ToString()+"/"+i.tradestat_losses.ToString()+", MFETotal = "+i. tradestat_mfe.ToString()+", MFEAvg = "+(i.tradesta t_mfe/(i.tradestat_wins+i.tradestat_losses)).ToString("N 2")+", Maxticks = " + i.tradestat_ticks +", r = " + i.tradestat_r +", wr = %" + i.tradestat_wr +", i dx #" +d.ToString());
    Print("wins3="+i.tradestat_wins);


    You will see in the above code, I am printing the same variable i.tradestat_wins 3 times, wins1, wins2, and wins3, e.g. Print("wins1="+i.tradestat_wins);

    This is the full code example. Bizarrely (to me!), this produces this output

    wins1=0
    False
    wins2=0
    -364
    Result : Total trades = 88, w/l = 47/41, MFETotal = 418, MFEAvg = 4.75, Maxticks = -364, r = 0.6, wr = %53.4, idx #49
    wins3=47

    Note - The first 2 times I try to print the variable i.tradestat_wins, which is a publicly exposed value on the indicator, it reports a zero value. This had me confused! I recalled I'd had this before. The solution was simple.

    Change the above code to this

    i = pjsRSI(Input,false);
    double dummy = i[0];
    Print(dummy);
    Print("wins1="+i.tradestat_wins);
    Print(i.tradestat_ready);
    Print("wins2="+i.tradestat_wins);
    Print(i.tradestat_ticks);
    PPrint("Result : Total trades = "+ (i.tradestat_wins+i.tradestat_losses).ToString()+" , w/l = "+i.tradestat_wins.ToString()+"/"+i.tradestat_losses.ToString()+", MFETotal = "+i. tradestat_mfe.ToString()+", MFEAvg = "+(i.tradesta t_mfe/(i.tradestat_wins+i.tradestat_losses)).ToString("N 2")+", Maxticks = " + i.tradestat_ticks +", r = " + i.tradestat_r +", wr = %" + i.tradestat_wr +", i dx #" +d.ToString());
    Print("wins3="+i.tradestat_wins);

    Note the addition of the 2nd and 3rd lines. Then, the output changes to this, as you would expect.

    wins1=47
    False
    wins2=47
    -364
    Result : Total trades = 88, w/l = 47/41, MFETotal = 418, MFEAvg = 4.75, Maxticks = -364, r = 0.6, wr = %53.4, idx #49
    wins3=47

    I can recall I had this once before (which is why I tried the fix). It seems the instance of the indicator is perhaps not fully initialised until you try to access a series from it? In any case, I am sure I am missing some fundamental concept of how Ninja is working here, so if you could explain this seemingly bizarre behaviour to me or point me to some reference material, I would appreciate it.

    Thanks

    #2
    From where is this code being executed? Inside OnBarUpdate?
    Since 'i' is a reference to an indicator, does calling 'i.Update();' help any?

    Shouldn't 'i = pjsRSI(Input, false);' be separate from this code?
    Like, say, executed earlier in State.DataLoaded?

    Are you able to contrive a simple example script that illustrates the problem?

    Comment


      #3
      Hi - Thanks for the suggestions. I will test further, but a quick test with i.update() instead of accessing the series per the 'dummy', seems to also do the job. I guess forcing onBarUpdate also does the trick. I've never used Update in that manner before.

      I'm calling the indicator with different parameters several times, so it is actually being called from within OnBarUpDate once all bars in the local side are loaded. I don't have a simple example, but I came across this same issue, maybe a year ago. It's just sod's law I stumbled on it again, but lucky to remember the workaround. Intrigued to know why it happens.
      Last edited by pjsmith; 07-03-2021, 02:12 PM.

      Comment


        #4
        Originally posted by pjsmith View Post
        Hi - Thanks for the suggestions. I will test further, but a quick test with i.update() instead of accessing the series per the 'dummy', seems to also do the job. I guess forcing onBarUpdate also does the trick. I've never used Update in that manner before.

        I'm calling the indicator with different parameters several times, so it is actually being called from within OnBarUpDate once all bars in the local side are loaded. I don't have a simple example, but I came across this same issue, maybe a year ago. It's just sod's law I stumbled on it again, but lucky to remember the workaround. Intrigued to know why it happens.
        Different parameters?
        Well, that's your problem right there.

        If you call the indicator with different parameters, you will get a different instance,
        one for for each unique set of parameters.

        This is expected, due to NinjaTrader's internal cache.

        [Recommend you study NT7 generated code at bottom of indicator, the
        NT8 generated code hides a lot more of the cache details.]
        Last edited by bltdavid; 07-03-2021, 10:52 PM.

        Comment


          #5
          Hi David,
          Thanks. I knew about the internal cache, but to be honest, not a lot about how it works. Even though I was calling the indicator again with different parameters, I was doing the assignment to the variable again and set in null when done, as I would in other projects, assuming this would work. I'll dig a bit deeper and try to expand my knowledge in this area. Thanks for your help.

          Comment


            #6
            Originally posted by pjsmith View Post
            Hi David,
            Thanks. I knew about the internal cache, but to be honest, not a lot about how it works. Even though I was calling the indicator again with different parameters, I was doing the assignment to the variable again and set in null when done, as I would in other projects, assuming this would work. I'll dig a bit deeper and try to expand my knowledge in this area. Thanks for your help.
            That shouldn't make any difference to the cache, since nothing
            is ever deleted from the cache.

            What I mean is, just because you set your indicator instance
            variable to null (I presume you're trying to be nice to the garbage
            collector?), doesn't matter -- the cache will always have its reference,
            so the GC never releases the memory of any indicator ... ever.

            Once instantiated, an indicator instance never goes away.

            Comment


              #7
              That'll also explain the memory guzzling Yes - I'd figured this one out whilst looking at it. A bit of Ninja unsupported code hacking seems to do the trick!

              Comment


                #8
                Originally posted by pjsmith View Post
                Hi David,
                Thanks. I knew about the internal cache, but to be honest, not a lot about how it works. Even though I was calling the indicator again with different parameters, I was doing the assignment to the variable again and set in null when done, as I would in other projects, assuming this would work. I'll dig a bit deeper and try to expand my knowledge in this area. Thanks for your help.
                The NT7 generated code is much easier to understand, because a
                lot of the indicator setup details were relatively exposed.

                The NT8 has a new internal helper class called CacheIndicator<T>
                that hides all the (presumably new and expanded) gory details.

                What does the generated code do?
                The gist is that when you call EMA(31), you're actually calling a
                method named 'EMA' that takes one int argument -- this method
                performs the actual 'new EMA()' and saves this reference to an
                internal list of references, and, yes, returns that reference. One
                purpose of this EMA wrapper method is to supply a middle-man,
                so that the indicator can be properly inserted into all the internal
                mechanisms needed for the event callback system (such as
                OnStateChange, OnBarUpdate, OnRender, etc etc).

                But, there's more ... the 'cache' purpose ...

                If a previous reference exists which also has a matching argument
                of '31', that reference is returned instead, and the 'new EMA()' is
                skipped. All arguments are checked. So, for ex, the call to MACD()
                will have generated code that validates all 3 arguments.

                There is an extra argument called 'Input' which represents the underlying
                input data series (usually the bars of the primary instrument -- but not
                always) the indicator runs against. This argument is also checked.

                Now that I've mentioned 'Input', you can see the generated code actually
                creates two signatures for the EMA method. The first is always just a
                wrapper for the second.

                Code:
                  public EMA EMA(int period)
                  public EMA EMA(ISeries<double> input, int period)
                What about Strategies?
                In both NT7 and NT8 look for @Strategy.cs. This factory installed file
                declares a class wide variable that is used by the indicator's generated
                code. You'll find the same class variable in @MarketAnalyzerColumn.cs
                (previously known as @Column.cs in NT7).

                This class wide variable is used to allow the MarkeyAnalyzerColumn and
                Strategy classes access to the One True internal cache maintained in the
                Indicator class generated code.

                I found the wrappers in the generated code very fascinating.
                The software glue that it represents is actually quite elegant.

                Good luck!




                Last edited by bltdavid; 07-04-2021, 10:56 AM. Reason: fix typo

                Comment

                Latest Posts

                Collapse

                Topics Statistics Last Post
                Started by Waxavi, Today, 02:10 AM
                0 responses
                4 views
                0 likes
                Last Post Waxavi
                by Waxavi
                 
                Started by TradeForge, Today, 02:09 AM
                0 responses
                10 views
                0 likes
                Last Post TradeForge  
                Started by Waxavi, Today, 02:00 AM
                0 responses
                2 views
                0 likes
                Last Post Waxavi
                by Waxavi
                 
                Started by elirion, Today, 01:36 AM
                0 responses
                4 views
                0 likes
                Last Post elirion
                by elirion
                 
                Started by gentlebenthebear, Today, 01:30 AM
                0 responses
                4 views
                0 likes
                Last Post gentlebenthebear  
                Working...
                X