Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

Countif() Condition as separate variables

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

    #16
    Hello Brandon and many thanks for the explanation. I'll review asa and be back. Thanks!

    Comment


      #17
      Thanks Brandon and Chelsea. So I figured out that for the MAX() function to work, as hinted in the doc (but contrary to the use for the Countif() )

      Syntax (notice the index is not mentioned, while it is required for it not to be added)
      Syntax


      MAX(int period)
      MAX(ISeries<double> input, int period)



      Returns default value
      MAX(int period)[int barsAgo]
      MAX(ISeries<double> input, int period)[int barsAgo]
      Example (notice no index required)
      // Prints the highest high value over the last 20 periods
      double value = MAX(High, 20)[0];
      Print("The current MAX value is " + value.ToString());



      // Note the above call with a barsAgo of 0 includes the current MAX of the input high series in the value. If we want to check for example for a break of this value, storing the last bar's MAX would be needed.
      double value = MAX(High, 20)[1];

      if (High[0] > value)
      Draw.ArrowUp(this, CurrentBar.ToString(), true, 0, Low[0] - TickSize, Brushes.Blue);
      The Series double variable needs to be passed as argument for the MAX() function without the index ("[0]")
      PHP Code:
      
      namespace NinjaTrader.NinjaScript.Indicators
      {
           public class Testa : Indicator
           {
                private Series<double> myDoubleSeries;
                private Series<double> varH;
                private Series<double> varO;
      
           protected override void OnStateChange()
           {
                if (State == State.SetDefaults)
                {
                     Description = @"Enter the description for your new custom Indicator here.";
                     Name = "Testa";
                     Calculate = Calculate.OnBarClose;
                     IsOverlay = true;
                     DisplayInDataBox = true;
                     DrawOnPricePanel = true;
                     DrawHorizontalGridLines = true;
                     DrawVerticalGridLines = true;
                     PaintPriceMarkers = true;
                     ScaleJustification = NinjaTrader.Gui.Chart.ScaleJustification.Right;
                     //Disable this property if your indicator requires custom values that cumulate with each new market data event.
                     //See Help Guide for additional information.
                     IsSuspendedWhileInactive = true;
                     BarsBack = 5;
                }
                else if (State == State.Configure)
                {
                }
                else if (State == State.DataLoaded)
                {
                     myDoubleSeries = new Series<double>(this, MaximumBarsLookBack.Infinite);
                     varH = new Series<double>(this, MaximumBarsLookBack.Infinite);
                     varO = new Series<double>(this, MaximumBarsLookBack.Infinite);
                }
      
                protected override void OnBarUpdate()
                {
      
                     varH[0] = High[0];
                     varO[0] = Open[0];
      
                     myDoubleSeries[0] = varH[0] - varO[0];
      
                     // THE myDoubleSeries NEEDS NOT TO HAVE THE INDEX ("[0]")
                     double var0 = MAX(myDoubleSeries, BarsBack)[0]; 
      

      Whereas for countif(() as shown in the doc it does need the index ("[0]")

      Syntax (notice the index is not mentioned (while it is required))
      Syntax


      CountIf(Func<bool> condition, int period)
      Example (notice the indexes are mentioned in the condition parameter)
      // If in the last 10 bars we have had 8 up bars then go long
      if (CountIf(() => Close[0] > Open[0], 10) > 8)
      EnterLong();
      as demonstrated in post #14

      PHP Code:
      varH[0] = High[0];
      varO[0] = Open[0];
      
      myDoubleSeries[0] = varH[0] - varO[0];
      
      // THE myDoubleSeries DOES NEED TO HAVE THE INDEX ("[0]")
      double var0 = MAX(myDoubleSeries[0], BarsBack)[0]; 
      

      Could you please explain why the index is required not to be added to the MAX() function while it is required to be added to the Countif() function?
      I understand the MAX() requires an ISeries<double> input while the Countif() requires a Func<bool> condition. But I'm not sure I can infer why the index is required in the former and not in the latter case from this requirement difference alone. Thanks!
      Last edited by PaulMohn; 03-01-2022, 01:06 AM.

      Comment


        #18
        I've got 2 new log tab errors
        Time Category Message
        28/02/2022 22:53:26 Default Value of property 'Rng0' of NinjaScript 'Testa' is 0 and not in valid range between 1 and 2147483647.
        Time Category Message
        01/03/2022 00:33:50 Default Indicator 'Testa': Error on calling 'OnBarUpdate' method on bar 5: condition Parameter name: 'CountIf' on bar 4 threw exception: Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index
        From the index of the variables Rng0 and Rng1.

        The first error is thrown when the indexes are set to zero in the State == State.SetDefaults scope as
        PHP Code:
        Rng0Index = 0;
        Rng1Index = 0; 
        

        The first second error is thrown when the indexes are set to 1 and above in the State == State.SetDefaults scope as
        PHP Code:
        Rng0Index = 1;
        Rng1Index = 1; 
        
        or
        PHP Code:
        Rng0Index = 5;
        Rng1Index = 5; 
        
        etc.

        What i'm trying to achieve is get the indexes as user input in from a switch.


        PHP Code:
        namespace NinjaTrader.NinjaScript.Indicators
        {
             public class Testa : Indicator
             {
                  private Series<double> Rng0;
                  private Series<double> Rng1;
        
                  protected override void OnStateChange()
                  {
                       if (State == State.SetDefaults)
                       {
        
                            Name = "Testa";
                            BarsBack = 5;
                            Rng0Index = 0;
                            Rng1Index = 0;
                       }
             }
             else if (State == State.Configure)
             {
             }
             else if (State == State.DataLoaded)
             {
                  Rng0 = new Series<double>(this, MaximumBarsLookBack.Infinite);
                  Rng1 = new Series<double>(this, MaximumBarsLookBack.Infinite);
             }
        
             protected override void OnBarUpdate()
             {
        
                  switch (Rngs1)
                  {
                       case CustomEnumNamespace0.Rngs.Rng:
                       {
                            Rng0[Rng0Index] = High[Rng0Index];
                            Rng1[Rng1Index] = Low[Rng1Index];
                            break;
                       }
                       // Other switch cases below
        
                       CountIf(() => Rng0[Rng0Index] - Rng1[Rng1Index] >= 15*TickSize, BarsBack); 
        

        What's causing the Log tab errors? Why doesn't it accept the index variable? while it accepts the index hardcoded/appended to the myDoubleSeries variable as demonstrated in post #14?
        Can i figure it out with the prints? If so how would you recommend doing the printing? Thanks!
        Last edited by PaulMohn; 03-01-2022, 01:11 AM.

        Comment


          #19
          Hello PaulMohn,

          MAX and MIN are indicators. Indicators require an input series.
          CountIf if a method that is not an indicator. It requires a lambda expression.

          You've got some confusion on the variable names.
          28/02/2022 22:53:26 Default Value of property 'Rng0' of NinjaScript 'Testa' is 0 and not in valid range between 1 and 2147483647.

          The variable Rng0 is causing the error here in this error message, not the Rng0Index. Likely this was declared with the Range attribute, from 1 to int.MaxValue. Meaning you cannot assign this a value of 0, it must be 1 or higher, or change the range attribute to range from 0 to int.MaxValue.

          With the indexes, these needs to be less than CurrentBar minus BarsBack to be a valid bar.
          Chelsea B.NinjaTrader Customer Service

          Comment


            #20
            Ok Chelsea thanks for the distinction. But what I don't get is why input series don't require the [0] index while the lambda expression does?

            Sorry for the error I misedited it today because as I thought I had pasted the wrong one yesterday after simplifying the code to share as easy example
            the correct error (I just got it now upon launching NT8)
            Time Category Message
            01/03/2022 16:15:28 Default Value of property 'Rng0Index' of NinjaScript 'Testa' is 0 and not in valid range between 1 and 2147483647.
            So it is an index error and not a variable error. I think the index is less than CurrentBar minus BarsBack as
            the indexes are zero by default
            PHP Code:
            Rng0Index = 0;
            Rng1Index = 0; 
            

            the BarBack is 5
            PHP Code:
            BarsBack = 5; 
            

            After checking with the prints,
            PHP Code:
            Print("Currentbar : " + CurrentBar); 
            

            CurrentBar is

            PHP Code:
            Currentbar : 1010
            Currentbar : 1011
            Currentbar : 1012
            Currentbar : 1013
            Currentbar : 1014
            Currentbar : 1015
            Currentbar : 1016
            Currentbar : 1017
            Currentbar : 1018
            Currentbar : 1019
            Currentbar : 1020
            Currentbar : 1021
            Currentbar : 1022
            Currentbar : 1023
            Currentbar : 1024
            Currentbar : 1025
            Currentbar : 1026
            Currentbar : 1027 
            

            (5 days and 1min set in the Dataseries parameters).

            0 (Rng0Index) < 1027 (latest Currentbar) - 5 (BarsBack)

            But I'm not sure if it is less than it the "starting" CurrentBar

            (for example 0 (Rng0Index) > 1 (first/second Currentbar) - 5 (BarsBack) )

            I do have a CurrentBar check at the top OnBarUpdate scope

            PHP Code:
            protected override void OnBarUpdate()
            {
                 if(CurrentBar < 5)
                 {
                      return;
                 } 
            

            But that's not solving the error.

            What else could be the reason for the index error? Thanks!
            Last edited by PaulMohn; 03-01-2022, 09:54 AM.

            Comment


              #21
              Hello PaulMohn,

              Are you certain that is the line of code causing the error?

              Can you replace the indexes with 0s and resolve the error?
              CountIf(() => Rng0[0] - Rng1[0] >= 15*TickSize, BarsBack);

              Can you Print Rng0[0] and Rng1[0] and see the value is set?
              Chelsea B.NinjaTrader Customer Service

              Comment


                #22
                Thanks Chelsea for the print tip.
                I just tested and it does print

                Test on CL
                PHP Code:
                CurrentBar : 1036
                Rng0 : 103.95
                Rng1 : 103.51 
                

                I commented out the index Properties and in State == State.SetDefaults scope variables
                PHP Code:
                protected override void OnStateChange()
                {
                     if (State == State.SetDefaults)
                     {
                          Description = @"Enter the description for your new custom Indicator here.";
                         Name = "Testa";
                         Calculate = Calculate.OnBarClose;
                         IsOverlay = true;
                         DisplayInDataBox = true;
                         DrawOnPricePanel = true;
                         DrawHorizontalGridLines = true;
                         DrawVerticalGridLines = true;
                         PaintPriceMarkers = true;
                         ScaleJustification = NinjaTrader.Gui.Chart.ScaleJustification.Right;
                         //Disable this property if your indicator requires custom values that cumulate with each new market data event.
                         //See Help Guide for additional information.
                         IsSuspendedWhileInactive = true;
                         BarsBack = 5;
                         // Rng0 = 0;
                         // Rng1 = 0; 
                

                PHP Code:
                // [Range(1, int.MaxValue), NinjaScriptProperty]
                // [Display(ResourceType = typeof(Custom.Resource), Name = "Rng0Index", GroupName = "Range 0 Index", Order = 2)]
                // public int Rng0Index
                // { get; set; } 
                
                PHP Code:
                // [Range(1, int.MaxValue), NinjaScriptProperty]
                // [Display(ResourceType = typeof(Custom.Resource), Name = "Rng0Index", GroupName = "Range 1 Index", Order = 4)]
                // public int Rng1Index
                // { get; set; } 
                

                Oh! sorry again! I just noticed my ranges in properties were still set to 1, int.MaxValue (for the Rng0Index Rng1Index properties). I must have forgot to change them back again after a copy and paste when testing again. Sorry my mistake again. I Just changed them to 0, int.MaxValue and now it's working without the prints error.


                The correct indexes Properties
                PHP Code:
                [Range(0, int.MaxValue), NinjaScriptProperty]
                [Display(ResourceType = typeof(Custom.Resource), Name = "Rng0Index", GroupName = "Range 0 Index", Order = 2)]
                public int Rng0Index
                { get; set; } 
                
                PHP Code:
                [Range(0, int.MaxValue), NinjaScriptProperty]
                [Display(ResourceType = typeof(Custom.Resource), Name = "Rng0Index", GroupName = "Range 1 Index", Order = 4)]
                public int Rng1Index
                { get; set; } 
                
                Last edited by PaulMohn; 03-01-2022, 10:55 AM.

                Comment


                  #23
                  Chelsea I got those other errors I found two complicated solutions for but I'd like to ask your advice on a possible more efficient one.

                  When I increase the BarsBack User Input int from 5 to say 150 this error is thrown
                  Time Category Message
                  01/03/2022 20:22:38 Default Indicator 'Testa': Error on calling 'OnBarUpdate' method on bar 5: You are accessing an index with a value that is invalid since it is out-of-range. I.E. accessing a series [barsAgo] with a value of 5 when there are only 4 bars on the chart.
                  I found a working fix as increasing the CurrentBar check from 5 to 150 as
                  PHP Code:
                  protected override void OnBarUpdate()
                  {
                       if(CurrentBar < 150)
                       {
                            return;
                       } 
                  

                  Then when I increase the BarsBack User Input int from 150 to say 360 this error is thrown

                  Time Category Message
                  01/03/2022 20:33:45 Default Indicator 'Testa': Error on calling 'OnBarUpdate' method on bar 360: You are accessing an index with a value that is invalid since it is out-of-range. I.E. accessing a series [barsAgo] with a value of 5 when there are only 4 bars on the chart.
                  The fix for that one would also be an increase of the CurrentBar Check to > 360
                  PHP Code:
                  protected override void OnBarUpdate()
                  {
                       if(CurrentBar < 360)
                       {
                            return;
                       } 
                  

                  I thought of adding an arbitrary large value to the CurrentBar check to let users input whatever (reasonable) BarBack value but this seems unrealisable (it seems the given high value would require the same amount of bars to be loaded on the chart prior to execute).

                  I then thought of adding a new enum/switch to automatically increase the CurrentBar check above the User Input BarBack value (but that seems also acomplicated process for the that simple aim and it doesn't seem to solve the arbitrary large number problem).

                  Do you know of any way to solve it more efficiently? Thanks.

                  Comment


                    #24
                    Hello PaulMohny,

                    You could use a variable for the number of bars and use this for the CurrentBar check and for the BarsBack value.

                    private int myBarsBack = 360;

                    if (CurrentBar < myBarsBack)
                    return;

                    CountIf(() => High[0] - Low[0] >= 15*TickSize, myBarsBack);
                    Chelsea B.NinjaTrader Customer Service

                    Comment


                      #25
                      Thanks Chelsea for the variable solution. But I was wondering how to do it while preserving the BarsBack as User Input so the user can still manually change the BarsBack value int from the properties indicator window (rather than modifying the hardcoded private int BarsBack= 360; value from within the script).
                      I originally had it set in the State == State.SetDefaults and in the #region Properties as

                      PHP Code:
                      protected override void OnStateChange()
                      {
                           if (State == State.SetDefaults)
                           {
                               BarsBack = 5;
                      
                      
                      #region Properties
                      [Range(1, int.MaxValue), NinjaScriptProperty]
                      [Display(ResourceType = typeof(Custom.Resource), Name = "BarsBack", GroupName = "Number of Bars for Stats", Order = 0)]
                      public int BarsBack
                      { get; set; } 
                      


                      But with the variable solution it seems i need to remove the User Input in the State == State.SetDefaults and in the #region Properties for it to work, but then I can't change the BarsBack value int form the properties indicator window anymore.

                      PHP Code:
                      namespace NinjaTrader.NinjaScript.Indicators
                      {
                           public class Testa : Indicator
                           {
                                private int BarsBack= 360;
                      
                                protected override void OnStateChange()
                                {
                                     if (State == State.SetDefaults)
                                     {
                      
                                          Name = "Testa";
                                          // BarsBack = 5;
                                     }
                           }
                      
                           protected override void OnBarUpdate()
                           {
                                if (CurrentBar < BarsBack)
                      return;
                      
                               CountIf(() => High[0] - Low[0] >= 15*TickSize, BarsBack);
                      
                      
                      
                      
                      // #region Properties
                      // [Range(1, int.MaxValue), NinjaScriptProperty]
                      // [Display(ResourceType = typeof(Custom.Resource), Name = "BarsBack", GroupName = "Number of Bars for Stats", Order = 0)]
                      // public int BarsBack
                      // { get; set; } 
                      

                      That works but no more User Input available for the BarsBack value as corollary.
                      Or is there a way to have both the User Input and the variable solution? If not do you have any other fix solution? Thanks!

                      Ah! I think I found a solution to make it work with your solution together with the Barsback Input User variable (assigning BarsBack to myBarsBack at the top of the OnBarUpdate scope then using myBarsBack in the CurrentBar check and in the Countif() method) as

                      PHP Code:
                      namespace NinjaTrader.NinjaScript.Indicators
                      {
                           public class Testa : Indicator
                           {
                                private int myBarsBack;
                      
                                protected override void OnStateChange()
                                {
                                     if (State == State.SetDefaults)
                                     {
                      
                                          Name = "Testa";
                                          BarsBack = 5;
                                     }
                           }
                      
                           protected override void OnBarUpdate()
                           {
                                myBarsBack = BarsBack;
                      
                                if (CurrentBar < myBarsBack)
                      return;
                      
                               CountIf(() => High[0] - Low[0] >= 15*TickSize, myBarsBack);
                      
                      
                      
                      #region Properties
                      [Range(1, int.MaxValue), NinjaScriptProperty]
                      [Display(ResourceType = typeof(Custom.Resource), Name = "BarsBack", GroupName = "Number of Bars for Stats", Order = 0)]
                      public int BarsBack
                      { get; set; } 
                      

                      It seems to be working as is. I'll do some more tests. Do you see any possible drawback with this approach? Many thanks again!
                      Last edited by PaulMohn; 03-01-2022, 04:58 PM. Reason: Think found way to use both User Input BarsBack variable and class level myBarsBack variable

                      Comment


                        #26
                        Hello PaulMohn,

                        Make the variable public and use the NinjaScriptProperty attribute.

                        [NinjaScriptProperty]
                        public int MyBarsBack
                        { get; set; }

                        In State.SetDefaults
                        MyBarsBack = 360;

                        In OnBarUpdate:
                        if (CurrentBar <= MyBarsBack)
                        return;

                        CountIf(() => High[0] - Low[0] >= 15*TickSize, MyBarsBack);
                        Chelsea B.NinjaTrader Customer Service

                        Comment


                          #27
                          Thanks Chelsea for the [NinjaScriptProperty] mention. I've just checked the doc but i'm not sure why to use it in my case.

                          But how strange, I tested your last solution and it works flawlessly.
                          it is more efficient too. but I don't understand yet why your last solution works while my original doesn't (when they seem to show the exact same structure.

                          My original problem with the out of range error script from post #25 (without your solution from post #24 )
                          PHP Code:
                          namespace NinjaTrader.NinjaScript.Indicators
                          {
                               public class Testa : Indicator
                               {
                          
                                    protected override void OnStateChange()
                                    {
                                         if (State == State.SetDefaults)
                                         {
                                              Name = "Testa";
                                              BarsBack = 5;
                                         }
                               }
                          
                               protected override void OnBarUpdate()
                               {
                                    if (CurrentBar < 5)
                          return;
                          
                                   CountIf(() => High[0] - Low[0] >= 15*TickSize, BarsBack);
                          
                          
                          
                          
                          #region Properties
                          [Range(1, int.MaxValue), NinjaScriptProperty]
                          [Display(ResourceType = typeof(Custom.Resource), Na me = "BarsBack", GroupName = "Number of Bars for S tats", Order = 0)]
                          public int BarsBack
                          { get; set; } 
                          

                          That throws the out of range error when inputing a value greater than the harcoded BarsBack = 5; in State == State.SetDefaults.


                          Your working latest solution from post #26
                          PHP Code:
                          namespace NinjaTrader.NinjaScript.Indicators
                          {
                               public class Testa : Indicator
                               {
                          
                                    protected override void OnStateChange()
                                    {
                                         if (State == State.SetDefaults)
                                         {
                                              Name = "Testa";
                                              myBarsBack = 360;
                                         }
                               }
                          
                               protected override void OnBarUpdate()
                               {
                                    if (CurrentBar <= myBarsBack)
                          return;
                          
                                   CountIf(() => High[0] - Low[0] >= 15*TickSize, myBarsBack);
                          
                          
                          #region Properties
                          [Range(1, int.MaxValue), NinjaScriptProperty]
                          [Display(ResourceType = typeof(Custom.Resource), Name = "BarsBack", GroupName = "Number of Bars for Stats", Order = 0)]
                          public int myBarsBack
                          { get; set; } 
                          

                          The single structure difference is the "<=" less or Equal to vs the "<" less than only in the CurrentBar Check. is this correct as it's supposed to? Or am I missing something?

                          Ah! now I see what you meant in post #24. The point emphasis was on the myBarsBack within the if() condition (not on the one from the new class level scope variable). It was sufficient to simply substitute the int hardcoded value in the CurrentBar check with the BarsBack variable for it to always accept whatever BarsBack User input is given in the Indicator Properties. Then no need for an added class level new variable/and new assignment to BarsBack at the top of the OnbarUpdate scope.

                          I think now all's clear. In fact it was clear from post #24 8but I placed the point emphasis on the wrong BarsBack mention at the time). In hindsight I see now I could have tested your post #24 solution without the private int myBarsBack = 360; variable, and keeping my BarsBack and adding it to the check.

                          The right point emphasis solution
                          PHP Code:
                          namespace NinjaTrader.NinjaScript.Indicators
                          {
                               public class Testa : Indicator
                               {
                          
                                    protected override void OnStateChange()
                                    {
                                         if (State == State.SetDefaults)
                                         {
                                              Name = "Testa";
                                              BarsBack = 5;
                                         }
                               }
                          
                               protected override void OnBarUpdate()
                               {
                                    if (CurrentBar < BarsBack)
                          return;
                          
                                   CountIf(() => High[0] - Low[0] >= 15*TickSize, BarsBack);
                          
                          
                          
                          
                          #region Properties
                          [Range(1, int.MaxValue), NinjaScriptProperty]
                          [Display(ResourceType = typeof(Custom.Resource), Na me = "BarsBack", GroupName = "Number of Bars for S tats", Order = 0)]
                          public int BarsBack
                          { get; set; } 
                          

                          To recap
                          The point emphasis solution on the CurrentBar Check using the BarsBack variable as substitute for the int hardcoded value was the single (and most efficient) needed fix.
                          The [NinjaScriptProperty] attribute was already present in the BarsBack #region properties "declaration". It's use it to display the control in the Indicator Properties window.
                          I also learn by the same occasion how it's possible to use a class level variable combined with a properties set in the State == State.SetDefaults (but I shouldn't need to use that combination).

                          Many thanks again for the right direction!

                          Comment

                          Latest Posts

                          Collapse

                          Topics Statistics Last Post
                          Started by Geovanny Suaza, 02-11-2026, 06:32 PM
                          0 responses
                          578 views
                          0 likes
                          Last Post Geovanny Suaza  
                          Started by Geovanny Suaza, 02-11-2026, 05:51 PM
                          0 responses
                          334 views
                          1 like
                          Last Post Geovanny Suaza  
                          Started by Mindset, 02-09-2026, 11:44 AM
                          0 responses
                          101 views
                          0 likes
                          Last Post Mindset
                          by Mindset
                           
                          Started by Geovanny Suaza, 02-02-2026, 12:30 PM
                          0 responses
                          554 views
                          1 like
                          Last Post Geovanny Suaza  
                          Started by RFrosty, 01-28-2026, 06:49 PM
                          0 responses
                          551 views
                          1 like
                          Last Post RFrosty
                          by RFrosty
                           
                          Working...
                          X