Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

Efficient Code Guide

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

    Efficient Code Guide

    I am trying to reduce the CPU load my indicator eats up. It is necessary to run on each tick. I am already using OnRender and disposing of DXbrushes OnRenderChange.

    I checked developer.ninjatrader.com but this was never fully developed.
    I have looked at the following posts - https://ninjatrader.com/support/foru...n-memory-usage


    I believe my code is running unnecessary CPU cycles but I can't find a guide to help me visualize the best way to utilize class, method, scope... within NinjaTrader to create the least CPU usage.
    Are there existing guides within NinjaTrader or do I need to study C# resources?

    Thanks

    #2
    Hello mlprice12,

    Thanks for your post.

    Ultimately, the key would be to do less in OnRender() where possible to reduce the number of resources that the indicator uses.

    Some performance tips would be not to do calculations or create objects within OnRender(), re-use objects in the script where possible, and keep objects rendered inside the visible ChartBars (e.g. don't render stuff offscreen or above/below the scale.

    See this help guide page for documentation about SharpDX Best Practices: https://ninjatrader.com/support/help...ataComparisons

    See this help guide about NinjaScript Best Practices: https://ninjatrader.com/support/help...tm#Performance

    And, here are some example scripts in this forum post which you may find helpful: https://ninjatrader.com/support/foru...ion#post789606

    Here is a link to an Ecosystem User App Share indicator which you may find to be helpful as well: https://ninjatraderecosystem.com/use...i-tick-volume/

    Let us know if we may assist further.
    <span class="name">Brandon H.</span><span class="title">NinjaTrader Customer Service</span><iframe name="sig" id="sigFrame" src="/support/forum/core/clientscript/Signature/signature.php" frameborder="0" border="0" cellspacing="0" style="border-style: none;width: 100%; height: 120px;"></iframe>

    Comment


      #3
      Thank you NinjaTrader_BrandonH

      I have been focusing on this quote from NinjaTrader_ChelseaB
      Indicators that process ticks or use tick based bars, create a lot of objects, or are inefficiently coded will require more CPU time and memory.
      (By inefficiently coded I mean scripts that declare variables within methods creating new class level objects and using new memory locations on each call of the method, instead of within the scope of the class causing those memory locations to be re-used on each call of the method.)
      I am not sure if I am declaring variables inefficiently.


      Your best guess is the OnRender is causing the CPU overload.
      This is my entire OnRender logic. I was under the impression all of those elements needed to be inside OnRender but could I pull out "xAsk, xBid, y, startPointAsk, startPointBid, radiusA, radiusB" variables?
      Code:
      protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
      {
      base.OnRender(chartControl, chartScale);
      for(int barIndex = ChartBars.FromIndex; barIndex <= ChartBars.ToIndex; barIndex++)
      {
      DateTime timeValue = BarsArray[0].GetTime(barIndex);
      int barsAgo = BarsArray[1].GetBar(timeValue);
      {
      if(mid.IsValidDataPointAt(barsAgo))
      {
      float xAsk = chartControl.GetXByBarIndex(ChartBars, barsAgo)+4;
      float xBid = chartControl.GetXByBarIndex(ChartBars, barsAgo)-4;
      float y = chartScale.GetYByValue(mid.GetValueAt(barsAgo));
      
      SharpDX.Vector2 startPointAsk = new SharpDX.Vector2(xAsk, y);
      SharpDX.Vector2 startPointBid = new SharpDX.Vector2(xBid, y);
      
      SharpDX.Vector2 centerPointAsk = (startPointAsk + startPointAsk) / 2;
      SharpDX.Vector2 centerPointBid = (startPointBid + startPointBid) / 2;
      
      if(radiusAsk.IsValidDataPointAt(barsAgo))
      {
      float radiusA = radiusAsk.GetValueAt(barsAgo);
      float radiusB = radiusBid.GetValueAt(barsAgo);
      
      SharpDX.Direct2D1.Ellipse ellipseAsk = new SharpDX.Direct2D1.Ellipse(centerPointAsk, radiusA, radiusA);
      SharpDX.Direct2D1.Ellipse ellipseBid = new SharpDX.Direct2D1.Ellipse(centerPointBid, radiusB, radiusB);
      
      if(radiusAsk.GetValueAt(barsAgo)/divisor>= 0.85)
      RenderTarget.FillEllipse(ellipseAsk, askAreaBrushDx);
      
      if(radiusBid.GetValueAt(barsAgo)/divisor>= 0.85)
      RenderTarget.FillEllipse(ellipseBid, bidAreaBrushDx);
      }
      }
      }
      }
      }

      Comment


        #4
        Hello mlprice12,

        Thanks for your note.

        Any variables that could be declared outside of OnRender() should be declared outside of OnRender() to increase performance.

        This could include the following variables in the code you shared: timeValue, barsAgo, xAsk, xBid, radiusA, radiusB, and y.

        Also, note that Vector2 variables and ellipses could also be made outside of OnRender() and have the x and y values updated in OnRender().

        See the 'Jtrealstats Realtime Level Ii / Tick Volume' script from the Ecosystem User App Share linked in my previous post. I am also linking this script below. This script is a good demonstration of efficient custom rendering.

        This is a conversion of the Realtime Level II/Tick Volume. Please contact the original author for any questions or comments. Update Aug 27th, 2018: Since OnRenderTargetChanged can be called before State.DataLoaded, setting the dxmBrushes["backColor"].MediaBrush here wasn&apos;t working out and was sometimes null. Changed this to now get the chart background color (and text color) from [&#8230;]


        Let us know if we may assist further.


        The NinjaTrader Ecosystem website is for educational and informational purposes only and should not be considered a solicitation to buy or sell a futures contract or make any other type of investment decision. The add-ons listed on this website are not to be considered a recommendation and it is the reader's responsibility to evaluate any product, service, or company. NinjaTrader Ecosystem LLC is not responsible for the accuracy or content of any product, service or company linked to on this website.
        <span class="name">Brandon H.</span><span class="title">NinjaTrader Customer Service</span><iframe name="sig" id="sigFrame" src="/support/forum/core/clientscript/Signature/signature.php" frameborder="0" border="0" cellspacing="0" style="border-style: none;width: 100%; height: 120px;"></iframe>

        Comment


          #5
          NinjaTrader_BrandonH

          I have JTrealstats and will dive deeper into its code for a solution.

          Thank you and I will reach out after working more on my indicator.

          Comment


            #6
            NinjaTrader_BrandonH

            I ran some tests and it appears that its my OnBarUpdate that is causing almost 11GB´s of Ram usage and OnRender 3GB.
            Digging deeper it is clear the radiusAsk and Radius Bid variables are taking the most memory use. It looks like the repeated SUM function is creating the memory overload. Would this make sense?

            Taking out the SUM function my memory use is down to 3GB.

            Since calculating the average bar volume is an important data point, what would be a better workaround?


            On a separate note: I was working on creating separate methods to make the code mor efficient.
            I was able to reduce the OnBarUpdate Memory usage down to 3.5GB by creating new methods outside of OnBarUpdate as an attempt to "reuse the memory locations" BUT I am struggling to get my OnRender to pull the values from those methods.

            For example when I try to Print(aVol[0]); in OnBarUpdate, the output is 0.

            My Original OnBarUpdate

            Code:
            protected override void OnBarUpdate()
            {
            //if (State != State.Realtime || CurrentBars[1] < 1)
            if (CurrentBars[1] < 1)
            return;
            
            
            NinjaTrader.NinjaScript.BarsTypes.VolumetricBarsTy pe barsType = BarsArray[1].BarsType as
            NinjaTrader.NinjaScript.BarsTypes.VolumetricBarsTy pe;
            
            if (barsType == null)
            return;
            
            if ((ToTime(Time[0]) > 92540 ) && (ToTime(Time[0]) <= 111500 ))
            {
            
            if ((ToTime(Time[0]) <= 92550 ))
            {
            if (IsFirstTickOfBar)
            {
            bar1 = CurrentBars[1];
            }
            }
            else
            {
            if (IsFirstTickOfBar)
            {
            timePeriod = CurrentBars[1] - bar1;
            }
            
            barVol[0] = barsType.Volumes[CurrentBars[1]].TotalVolume;
            mid[0] = (Highs[1][0] + Lows[1][0])/2;
            
            if ((barsType.Volumes[CurrentBars[1]].TotalBuyingVolume/(((SUM(barVol, timePeriod)[0])/timePeriod)/2)*divisor) > maxSize)
            radiusAsk[0] = maxSize;
            
            else if ((barsType.Volumes[CurrentBars[1]].TotalBuyingVolume/(((SUM(barVol, timePeriod)[0])/timePeriod)/2)*divisor) > 0.7)
            radiusAsk[0] = (float)(barsType.Volumes[CurrentBars[1]].TotalBuyingVolume/(((SUM(barVol, timePeriod)[0])/timePeriod)/2)*divisor);
            
            
            if ((barsType.Volumes[CurrentBars[1]].TotalSellingVolume/(((SUM(barVol, timePeriod)[0])/timePeriod)/2)*divisor) > maxSize)
            radiusBid[0] = maxSize;
            
            else if ((barsType.Volumes[CurrentBars[1]].TotalSellingVolume/(((SUM(barVol, timePeriod)[0])/timePeriod)/2)*divisor) > 0.7)
            radiusBid[0] = (float)(barsType.Volumes[CurrentBars[1]].TotalSellingVolume/(((SUM(barVol, timePeriod)[0])/timePeriod)/2)*divisor);
            
            }
            }
            
            else
            return;
            }
            }
            After adding new methods outside OnBarUpdate
            Code:
             if ((ToTime(Time[0]) > 92540 ) && (ToTime(Time[0]) <= 92550 ))
            {
            if (IsFirstTickOfBar)
            {
            UpdateBar1();
            }
            else
            return;
            }
            else if ((ToTime(Time[0]) > 92550) )
            {
            if (IsFirstTickOfBar)
            {
            UpdateTimePeriod();
            }
            UpdateBarVol();
            UpdateRadius();
            }
            else
            return;
            }
            public void UpdateBar1()
            {
            if ((ToTime(Time[0]) > 92540 ) && (ToTime(Time[0]) <= 92550 ))
            {
            if (IsFirstTickOfBar)
            {
            int bar1 = CurrentBars[1];
            }
            else
            return;
            }
            
            }
            public void UpdateTimePeriod()
            {
            if ((ToTime(Time[0]) > 92550) )
            {
            if (IsFirstTickOfBar)
            {
            int timePeriod = CurrentBars[1] - bar1;
            mid[0] = (Highs[1][0] + Lows[1][0])/2;
            }
            }
            }
            public void UpdateBarVol()
            {
            NinjaTrader.NinjaScript.BarsTypes.VolumetricBarsTy pe barsType = BarsArray[1].BarsType as
            NinjaTrader.NinjaScript.BarsTypes.VolumetricBarsTy pe;
            if ((ToTime(Time[0]) > 92550) )
            {
            barVol[0] = Math.Round((double)(barsType.Volumes[CurrentBars[1]].TotalVolume),3);
            aVol[0] = (float)(Math.Round((barsType.Volumes[CurrentBars[1]].TotalBuyingVolume/(((SUM(barVol, timePeriod)[0])/timePeriod)/2)),3));
            bVol[0] = (float)(Math.Round((barsType.Volumes[CurrentBars[1]].TotalSellingVolume/(((SUM(barVol, timePeriod)[0])/timePeriod)/2)),3));
            }
            }
            public void UpdateRadius()
            {
            if (aVol[0] != 0 && bVol[0] != 0)
            {
            radiusAsk[0] = (float)(Math.Round(aVol[0]*divisor,3));
            radiusBid[0] = (float)(Math.Round(bVol[0]*divisor,3));
            if (radiusAsk[0] > maxSize)
            {
            radiusAsk[0] = maxSize;
            }
            if (radiusBid[0] > maxSize)
            {
            radiusBid[0] = maxSize;
            }
            }
            }
            Last edited by mlprice12; 05-11-2022, 06:02 PM.

            Comment


              #7
              Hello mlprice12,

              Thanks for your note.

              Triggering a method from OnBarUpdate() is the exact same as putting the code within OnBarUpdate().

              Re-using memory locations means not making new objects when it is not necessary. This would not have anything to do with calling methods in the script.

              Note that doing calculations in a script does not take up memory.

              An example of where your script is taking new memory locations because it is being recreated on each pass would be as follows.

              int bar1 = CurrentBars[1];

              int timePeriod = CurrentBars[1] - bar1;


              Your previous code shared in post #3 will be very resource-intensive since new objects are being generated on every render pass that happens. Moving code to a method that is called in the script would not improve memory management.

              If you comment out all the code in OnRender(), does the behavior continue?

              If not then it is likely the OnRender() method code causing the script to use a large number of resources, not the OnBarUpdate() method.

              Also note that if you have a custom series synchronized to a 1-tick series, this would cause a large amount of data to be processed.

              Please study the script 'Jtrealstats Realtime Level 2 / Tick Volume' from the Ecosystem User App Share linked below which is a great example of good rendering practices. This script demonstrates not making objects within OnRender() to improve the resources being used by the script.

              Jtrealstats Realtime Level 2 / Tick Volume: https://ninjatraderecosystem.com/use...i-tick-volume/

              Let us know if we may assist further.


              <span class="name">Brandon H.</span><span class="title">NinjaTrader Customer Service</span><iframe name="sig" id="sigFrame" src="/support/forum/core/clientscript/Signature/signature.php" frameborder="0" border="0" cellspacing="0" style="border-style: none;width: 100%; height: 120px;"></iframe>

              Comment


                #8
                Thanks NinjaTrader_BrandonH

                You said
                Note that doing calculations in a script does not take up memory.
                I was able to reduce memory use from about 14GB to 2GB by simply removing the SUM function from
                Code:
                radiusAsk[0] = (float)(barsType.Volumes[CurrentBars[1]].TotalBuyingVolume/(((SUM(barVol, timePeriod)[0])/timePeriod)/2)*divisor);
                and simply inserting "15"
                Code:
                radiusAsk[0] = (float)(barsType.Volumes[CurrentBars[1]].TotalBuyingVolume/15*divisor);
                This change shouldn't have reduced memory use?

                Thanks

                Comment


                  #9
                  Originally posted by mlprice12 View Post
                  Thanks NinjaTrader_BrandonH

                  You said
                  I was able to reduce memory use from about 14GB to 2GB by simply removing the SUM function from
                  Code:
                  radiusAsk[0] = (float)(barsType.Volumes[CurrentBars[1]].TotalBuyingVolume/(((SUM(barVol, timePeriod)[0])/timePeriod)/2)*divisor);
                  and simply inserting "15"
                  Code:
                  radiusAsk[0] = (float)(barsType.Volumes[CurrentBars[1]].TotalBuyingVolume/15*divisor);
                  This change shouldn't have reduced memory use?

                  Thanks

                  Hi mlprice,

                  This looks expensive ..
                  .. calling, re-instantiating, SUM() and passing BarVol multiple times in the same OBU() event cycle.
                  "(SUM(barVol, timePeriod)[0])/timePeriod)/2)"

                  Following the normal best practice of re-instantiating re-used indicators once in OSC().DataLoaded might help reduce the CPU and memory load.


                  This also looks like it might be expensive.. calling this multiple times per OBU() event.
                  "(float)(barsType.Volumes[CurrentBars[1]].TotalBuyingVolume"

                  Might call it once and stuff it into a local float var and then use the float var for all current direct references.


                  Is "BarVol" a dataseries set to infinity? That could chew up memory fast.


                  Good luck!

                  Hedge
                  Last edited by hedgeplay; 05-12-2022, 06:32 PM.

                  Comment


                    #10
                    Hello mlprice12,

                    Thanks for your note.

                    As hedgeplay stated, calling and re-instantiating variables that are using SUM() on every render pass would cause a very large number of resources to be used by the script since new objects are being generated on every render pass.

                    You would need to follow the best practice of re-instantiating and re-using variables where possible in your script to reduce the resources being used.

                    The previously attached NinjaScript, Jtrealstats Realtime Level 2 / Tick, linked in post #7 is a great example of best rendering practices and NOT making objects within OnRender() to reduce the number of resources consumed by the script.

                    Let us know if we may assist further.
                    <span class="name">Brandon H.</span><span class="title">NinjaTrader Customer Service</span><iframe name="sig" id="sigFrame" src="/support/forum/core/clientscript/Signature/signature.php" frameborder="0" border="0" cellspacing="0" style="border-style: none;width: 100%; height: 120px;"></iframe>

                    Comment

                    Latest Posts

                    Collapse

                    Topics Statistics Last Post
                    Started by Geovanny Suaza, 02-11-2026, 06:32 PM
                    0 responses
                    647 views
                    0 likes
                    Last Post Geovanny Suaza  
                    Started by Geovanny Suaza, 02-11-2026, 05:51 PM
                    0 responses
                    368 views
                    1 like
                    Last Post Geovanny Suaza  
                    Started by Mindset, 02-09-2026, 11:44 AM
                    0 responses
                    108 views
                    0 likes
                    Last Post Mindset
                    by Mindset
                     
                    Started by Geovanny Suaza, 02-02-2026, 12:30 PM
                    0 responses
                    571 views
                    1 like
                    Last Post Geovanny Suaza  
                    Started by RFrosty, 01-28-2026, 06:49 PM
                    0 responses
                    573 views
                    1 like
                    Last Post RFrosty
                    by RFrosty
                     
                    Working...
                    X