Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

Making NinjaScript indicator efficient

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

    Making NinjaScript indicator efficient

    Hi,

    I have the below code, however it fails to run smoothly and causes NT8 to freeze.

    What is the best practice for making this work efficiently? I have read the NinjaScript Best Practices, however I don't see if there is a better way to achieve the outcome.

    I suspect most of the inefficiency is coming from Configure and DataLoaded states?


    Code:
    public class zUSDtrend : Indicator
        {
            private Series<double> GBPUSD5m;
            private Series<double> GBPUSD60m;
            private Series<double> EURUSD5m;
            private Series<double> EURUSD60m;
            private Series<double> AUDUSD5m;
            private Series<double> AUDUSD60m;
            private Series<double> USDJPY5m;
            private Series<double> USDJPY60m;
            private Series<double> USDCAD5m;
            private Series<double> USDCAD60m;
            private int barsAgo;
            protected override void OnStateChange()
            {
                if (State == State.SetDefaults)
                {
                    Description                                 = @"Enter the description for your new custom Indicator here.";
                    Name                                        = "zUSDtrend";
                    Calculate                                   = Calculate.OnBarClose;
                    BarsRequiredToPlot                          = 20;
                    IsOverlay                                   = false;
                    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;
                    SMA5mPeriod                 = 8;
                    SMA60mPeriod                    = 8;
                    AddPlot(new Stroke(Brushes.DarkCyan, DashStyleHelper.Dot, 1), PlotStyle.Line, "GBPUSD 5m");
                    AddPlot(new Stroke(Brushes.Orange, DashStyleHelper.Dot, 1), PlotStyle.Line, "GBPUSD 60m");
                    AddPlot(new Stroke(Brushes.DarkCyan, DashStyleHelper.Dot, 1), PlotStyle.Line, "EURUSD 5m");
                    AddPlot(new Stroke(Brushes.Orange, DashStyleHelper.Dot, 1), PlotStyle.Line, "EURUSD 60m");
                    AddPlot(new Stroke(Brushes.DarkCyan, DashStyleHelper.Dot, 1), PlotStyle.Line, "AUDUSD 5m");
                    AddPlot(new Stroke(Brushes.Orange, DashStyleHelper.Dot, 1), PlotStyle.Line, "AUDUSD 60m");
                    AddPlot(new Stroke(Brushes.DarkCyan, DashStyleHelper.Dot, 1), PlotStyle.Line, "USDJPY 5m");
                    AddPlot(new Stroke(Brushes.Orange, DashStyleHelper.Dot, 1), PlotStyle.Line, "USDJPY 60m");
                    AddPlot(new Stroke(Brushes.DarkCyan, DashStyleHelper.Dot, 1), PlotStyle.Line, "USDCAD 5m");
                    AddPlot(new Stroke(Brushes.Orange, DashStyleHelper.Dot, 1), PlotStyle.Line, "USDCAD 60m");
                }
                else if (State == State.Configure)
                {
                    AddDataSeries("GBPUSD", Data.BarsPeriodType.Minute, 5, Data.MarketDataType.Last);
                    AddDataSeries("GBPUSD", Data.BarsPeriodType.Minute, 60, Data.MarketDataType.Last);
                  
                    AddDataSeries("EURUSD", Data.BarsPeriodType.Minute, 5, Data.MarketDataType.Last);
                    AddDataSeries("EURUSD", Data.BarsPeriodType.Minute, 60, Data.MarketDataType.Last);
                    AddDataSeries("AUDUSD", Data.BarsPeriodType.Minute, 5, Data.MarketDataType.Last);
                    AddDataSeries("AUDUSD", Data.BarsPeriodType.Minute, 60, Data.MarketDataType.Last);
                  
                    AddDataSeries("USDJPY", Data.BarsPeriodType.Minute, 5, Data.MarketDataType.Last);
                    AddDataSeries("USDJPY", Data.BarsPeriodType.Minute, 60, Data.MarketDataType.Last);
                  
                    AddDataSeries("USDCAD", Data.BarsPeriodType.Minute, 5, Data.MarketDataType.Last);
                    AddDataSeries("USDCAD", Data.BarsPeriodType.Minute, 60, Data.MarketDataType.Last);
                    AddLine(Brushes.DodgerBlue, 0, "zero");
                }
                else if (State == State.DataLoaded)
                {
                    GBPUSD5m = new Series<double>(this);
                    GBPUSD60m = new Series<double>(this);
                    EURUSD5m = new Series<double>(this);
                    EURUSD60m = new Series<double>(this);
                    AUDUSD5m = new Series<double>(this);
                    AUDUSD60m = new Series<double>(this);
                    USDJPY5m = new Series<double>(this);
                    USDJPY60m = new Series<double>(this);
                    USDCAD5m = new Series<double>(this);
                    USDCAD60m = new Series<double>(this);
                }
            }
            protected override void OnBarUpdate()
            {
                //Add your custom indicator logic here.
                if (CurrentBar < 60) return;
                    for (int barsAgo = 0; barsAgo < 2; barsAgo++)
                    {
                        Values[0][0] = -1000 * ((SMA(BarsArray[1],SMA5mPeriod)[0] - SMA(BarsArray[1],SMA5mPeriod)[1]) / SMA(BarsArray[1],SMA5mPeriod)[1]);
                        Values[1][0] = -1000 * ((SMA(BarsArray[2],SMA5mPeriod)[0]- SMA(BarsArray[2],SMA5mPeriod)[1]) / SMA(BarsArray[2],SMA5mPeriod)[1]);
                        Values[2][0] = -1000 * ((SMA(BarsArray[3],SMA60mPeriod)[0] - SMA(BarsArray[3],SMA60mPeriod)[1]) / SMA(BarsArray[3],SMA60mPeriod)[1]);
                        Values[3][0] = -1000 * ((SMA(BarsArray[4],SMA60mPeriod)[0]- SMA(BarsArray[4],SMA60mPeriod)[1]) / SMA(BarsArray[4],SMA60mPeriod)[1]);
                        Values[4][0] = -1000 * ((SMA(BarsArray[5],SMA60mPeriod)[0] - SMA(BarsArray[5],SMA60mPeriod)[1]) / SMA(BarsArray[5],SMA60mPeriod)[1]);
                        Values[5][0] = -1000 * ((SMA(BarsArray[6],SMA60mPeriod)[0]- SMA(BarsArray[6],SMA60mPeriod)[1]) / SMA(BarsArray[4],SMA60mPeriod)[1]);
                        Values[6][0] = 1000 * ((SMA(BarsArray[7],SMA60mPeriod)[0] - SMA(BarsArray[7],SMA60mPeriod)[1]) / SMA(BarsArray[7],SMA60mPeriod)[1]);
                        Values[7][0] = 1000 * ((SMA(BarsArray[8],SMA60mPeriod)[0]- SMA(BarsArray[8],SMA60mPeriod)[1]) / SMA(BarsArray[8],SMA60mPeriod)[1]);
                      
                        Values[8][0] = 1000 * ((SMA(BarsArray[9],SMA60mPeriod)[0] - SMA(BarsArray[9],SMA60mPeriod)[1]) / SMA(BarsArray[9],SMA60mPeriod)[1]);
                        Values[9][0] = 1000 * ((SMA(BarsArray[10],SMA60mPeriod)[0]- SMA(BarsArray[10],SMA60mPeriod)[1]) / SMA(BarsArray[10],SMA60mPeriod)[1]);
                    }
    
            }​
    Attached Files

    #2
    You do have Calculate set to OnBarClose, so you are probably okay there, but you also have references to the [0], i.e. current bar in OnBarUpdate. I am not sure if those [0]'s override the OnBarClose setting, possibly causing OnBarUpdate to run OnPriceChange or OnEachTick. Maybe add a Print("Time = " + Time[0].ToShortTimeString()); to the start of OnBarUpdate to see in NinjaScript Output how frequently OnBarUpdate is processing.

    You can also use "if (this.IsFirstTickOfBar)" to stop processing on every tick or price change. I only want a few items to process on every price change, so I use IsFirstTickOfBar for everything else to scale back on overkill processing. I also use code like: if (SmaMov1[1] > SmaMov1[2]) to test if a moving average is rising (prior to bar[0]) in order to avoid processing on that ever-changing current bar. Granted, I am using much higher frequency bars, but if OnBarUpdate is processing on every tick or price change, it makes no difference which Bars.BarsType.BarsPeriod.Value you have set. The processing rate would be identical.

    Comment


      #3
      Thanks c2injacator.

      Very helpful. I will try those things plus maybe eliminating the number of SMA calculations might help....perhaps put SMA as variable and then use the variable multiple times over....

      In your experience, is having that quantity of data in a Ninjascript coming to the limits of the way NT uses C#?

      Comment


        #4
        If your entire OnBarUpdate processes numerous bars or calls other methods during the price changes or tick changes, that can be a primary cause of inefficiency. When I realized that I could go to a more frequent bar, and not have to process in real time for everything, I could get the system to be more efficient. There are a very few things where I want real-time (i.e. OnPriceChange) so those are processed in a separate part of OnBarUpdate. The current bar itself (Bar[0]) always updates in real time, but I only have one data series per chart and limit charts to 3 or less. Maybe you can do more, but I don't have experience with that. A lot would depend on your cpu speed and ram.

        I think you can allow the system to update several Bar[0]'s, but when you are using several moving averages or other indicators on the bars, those can be more demanding. The trick for me is to use higher bar frequency as needed (like 10 sec vs 60), but set Calculate to OnBarClose.

        Example: Say you process OnBarUpdate on a 60 second bar vs a 10 second bar. It looks like the 60 second bar should be more efficient, but if Calculate is set to OnPriceChange for the 60 second bar, OnBarUpdate processes every single time the price changes... could be well over 100 calls. Whereas if you are using 10 second bars, and Calculate is set to OnBarClose, the number of calls to OnBarUpdate during that same 60 seconds is always 6. (If you do that though, you should consider using moving averages with more periods to compensate for the greater bar frequency.)

        ​If you need some things to process with every price change, set Calculate to OnPriceChange, but in OnBarUpdate you can use "if (this.IsFirstTickOfBar) {your code}" for calls that can wait for your bar update frequency (like say 10 seconds, or whatever it is). In that case, I think avoiding [0] is a good idea because it only processes the open for that bar... Use [1] and higher to access the completed bars.

        ALSO: Another huge efficiency savings is found in the Data Series window. Try using Load Data Based on "Bars" instead of "Days". I generally use 1000 to 4000 bars (Bars to Load) BUT NOTE: ​The charts continue to add bars PAST that starting amount. You can reset the Bars to Load to clip off the early end of the chart. e.g. I reset 2000 to 1800 and back again every couple of hours. And finally, do not over-compress the bars visible on the chart. That can freeze things up if you use a lot of image objects (triangles, arrows, etc)... Instead go to a lesser bar frequency, like from 10 seconds to 60 seconds to see a wider time interval.

        If you use say 4000 bars, with each doubling of time, like 30 sec to 60 sec gives you twice the overall time coverage. You might have to wait as the data is loaded, but just the first time you use that bar setting.
        Last edited by c2injacator; 09-03-2024, 08:26 PM.

        Comment


          #5
          Hello Apm123,

          Thank you for your post.

          c2injacator brings up some good tips.

          One other thing you may want to try is caching the indicators by saving them to a variable. Take a look at the SampleMACrossover script included in the platform which demonstrates saving the SMA to a variable which is then used in OnBarUpdate.

          Comment

          Latest Posts

          Collapse

          Topics Statistics Last Post
          Started by Geovanny Suaza, 02-11-2026, 06:32 PM
          0 responses
          649 views
          0 likes
          Last Post Geovanny Suaza  
          Started by Geovanny Suaza, 02-11-2026, 05:51 PM
          0 responses
          370 views
          1 like
          Last Post Geovanny Suaza  
          Started by Mindset, 02-09-2026, 11:44 AM
          0 responses
          109 views
          0 likes
          Last Post Mindset
          by Mindset
           
          Started by Geovanny Suaza, 02-02-2026, 12:30 PM
          0 responses
          574 views
          1 like
          Last Post Geovanny Suaza  
          Started by RFrosty, 01-28-2026, 06:49 PM
          0 responses
          576 views
          1 like
          Last Post RFrosty
          by RFrosty
           
          Working...
          X