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

most resource efficient way to use OnRender

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

    most resource efficient way to use OnRender


    I am new to OnRender and need some guidance.

    My first question is on the code attached.

    Did I instantiate the right stuff in the most resource efficient manner?

    Do I do it all under OnRender, or some in OnRenderTargetChanged? Or somewhere else?

    Did I dispose of everything that needed to be disposed?

    My goal is to make the code as efficient as possible when executing cause I have a lot to render

    HTML Code:
            protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
            {
    // call the base.OnRender() to ensure standard Plots work as designed
                base.OnRender(chartControl, chartScale);
    
    //    CREATE TEXT FORMAT            
                SharpDX.DirectWrite.TextFormat textFormat = new SharpDX.DirectWrite.TextFormat(NinjaTrader.Core.Globals.DirectWriteFactory,
                  "Arial", PriceTimerFontSize);
    
    //    CREATE BRUSHES        
                SharpDX.Direct2D1.Brush brushRed             = Brushes.Red.ToDxBrush(RenderTarget);
                SharpDX.Direct2D1.Brush brushGreen             = Brushes.Green.ToDxBrush(RenderTarget);
                SharpDX.Direct2D1.Brush brushWhite             = Brushes.White.ToDxBrush(RenderTarget);
                SharpDX.Direct2D1.Brush brushGray             = Brushes.DarkGray.ToDxBrush(RenderTarget);
                SharpDX.Direct2D1.Brush brushLimeGreen         = Brushes.LimeGreen.ToDxBrush(RenderTarget);
                SharpDX.Direct2D1.Brush brushAqua             = Brushes.Aqua.ToDxBrush(RenderTarget);
    
        
    //    SET X COORDINATE AT RIGHT EDGE OF CHART            
                int xPriceCoordinate = ChartPanel.X + ChartPanel.W - 50;    
    //    SET Y COORDINATE AT CURRENT CLOSE            
                int yPriceCoordinate = chartScale.GetYByValue(Bars.GetClose(Bars.Count - 1));        
    //     Y OFFSET FOR TARGET LINES                        
                float offset = chartScale.GetPixelsForDistance(50 * TickSize);                        
    
                SharpDX.Direct2D1.AntialiasMode oldAntialiasMode = RenderTarget.AntialiasMode;
                RenderTarget.AntialiasMode = SharpDX.Direct2D1.AntialiasMode.Aliased;
                
                
    //    CREATE RECTANGLE FOR TIME DISPLAY            
                SharpDX.Rectangle timeBox = new SharpDX.Rectangle(xPriceCoordinate, yPriceCoordinate - 10, 50, 20);
    //    CHANGE RECTANGLE COLOR TO RED WHEN 10 SECONDS LEFT BEFORE BAR CLOSE                    
                RenderTarget.FillRectangle(timeBox, (barTimeLeft.Hours == 0 && barTimeLeft.Minutes == 0 && barTimeLeft.Seconds <= 10) ? brushRed : brushGreen);
    //    DRAW TEXT WITH TIME LEFT UNTIL BAR CLOSES            
                RenderTarget.DrawText(" " + timeLeft, textFormat, timeBox, brushWhite);
    
                
    //    SET START COORDINATE FOR LINE
                int x1PriceCoordinate = xPriceCoordinate - 300;
    //    CREATE START AND ENDPOINTS            
                SharpDX.Vector2 lineStart;
                SharpDX.Vector2 lineEnd;
    
                
    //    DRAW LINE AT CURRENT PRICE            
                lineStart     = new SharpDX.Vector2(x1PriceCoordinate, yPriceCoordinate);
                lineEnd     = new SharpDX.Vector2(xPriceCoordinate, yPriceCoordinate);            
                RenderTarget.DrawLine(lineStart, lineEnd, brushGray, 1, lineDash);
    
    //    DRAW LINE 50 TICKS ABOVE CURRENT PRICE            
                float y = yPriceCoordinate + offset;
                lineStart     = new SharpDX.Vector2(x1PriceCoordinate, y);
                lineEnd     = new SharpDX.Vector2(xPriceCoordinate, y);
                RenderTarget.DrawLine(lineStart, lineEnd, brushAqua, 1);
    
    //    DRAW LINE 50 TICKS ABOVE BELOW PRICE            
                y = yPriceCoordinate - offset;
                lineStart     = new SharpDX.Vector2(x1PriceCoordinate, y);
                lineEnd     = new SharpDX.Vector2(xPriceCoordinate, y);
                RenderTarget.DrawLine(lineStart, lineEnd, brushAqua, 1);
    
    
    //    DRAW LINE 100 TICKS ABOVE CURRENT PRICE        
                y = yPriceCoordinate + 2 * offset;            
                lineStart     = new SharpDX.Vector2(x1PriceCoordinate, y);
                lineEnd     = new SharpDX.Vector2(xPriceCoordinate, y);
                RenderTarget.DrawLine(lineStart, lineEnd, brushLimeGreen, 1);
    
    //    DRAW LINE 100 TICKS BELOW CURRENT PRICE            
                y = yPriceCoordinate - 2 * offset;
                lineStart     = new SharpDX.Vector2(x1PriceCoordinate, y);
                lineEnd     = new SharpDX.Vector2(xPriceCoordinate, y);
                RenderTarget.DrawLine(lineStart, lineEnd, brushLimeGreen, 1);
    
    
                RenderTarget.AntialiasMode = oldAntialiasMode;
                brushRed.Dispose();
                brushGreen.Dispose();
                brushWhite.Dispose();
                brushGray.Dispose();
                brushLimeGreen.Dispose();
                brushAqua.Dispose();
        
            }
    ​

    My other OnRender question is for a lot of line drawing, rectangle drawing, etc... is it better to use DrawLine, DrawRectange, DrawText, etc

    Or is it a better use of system resource to use OnRender to create the objects?

    thanks

    #2
    Hello crea8ble,

    Thank you for your post.

    I don't see anything obviously incorrect with the code you've posted. One thing you may want to consider instead of using ToDxBrush(), you should construct a SharpDX Brush directly if a WPF brush is not ever needed. This is discussed in the Best Practices for SharpDX resources section of the help guide page below:

    HTML Code:
    https://ninjatrader.com/support/helpguides/nt8/index.html?using_sharpdx_for_custom_chart_rendering.htm#BestPracticesforSharpDXResources
    I recommend reading through the entire section for recommend practices on using SharpDX for custom rendering.

    If you are creating a lot of drawing objects, it would be better to use OnRender() to custom render the drawings.

    Please let us know if you have any further questions.
    Gaby V.NinjaTrader Customer Service

    Comment


      #3
      thanks Gaby

      I read through the OnRender docs a few times, but still have a coupe questions

      HTML Code:
              protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
              {
                  int space = 5;
                  int xStatusCoordinate, yStatusCoordinate, xTargetCoordinate, yTargetCoordinate, newX;
                  float yOffset, newY;
                  
      //=====    SET UP VECTORS AND RECTANGLE            
                  SharpDX.Vector2 lineStart;
                  SharpDX.Vector2 lineEnd;
                  SharpDX.Rectangle gapStatusBox;
      
      //=====    SET UP TEXT FORMAT            
                  SharpDX.DirectWrite.TextFormat textFormat = new SharpDX.DirectWrite.TextFormat(NinjaTrader.Core.Globals.DirectWriteFactory,
                    "Arial", 12);
                  textFormat.ParagraphAlignment =  SharpDX.DirectWrite.ParagraphAlignment.Center;
                  textFormat.TextAlignment =  SharpDX.DirectWrite.TextAlignment.Center;
                  
      //=====    SET UP BRUSHES            
                  SharpDX.Direct2D1.Brush brushBlue             = new SharpDX.Direct2D1.SolidColorBrush(RenderTarget, SharpDX.Color.Blue);
                  SharpDX.Direct2D1.Brush brushWhite             = new SharpDX.Direct2D1.SolidColorBrush(RenderTarget, SharpDX.Color.White);
                  SharpDX.Direct2D1.Brush brushRed             = new SharpDX.Direct2D1.SolidColorBrush(RenderTarget, SharpDX.Color.Red);
                  SharpDX.Direct2D1.Brush brushGreen             = new SharpDX.Direct2D1.SolidColorBrush(RenderTarget, SharpDX.Color.DarkGreen);
                  SharpDX.Direct2D1.Brush brushYellow         = new SharpDX.Direct2D1.SolidColorBrush(RenderTarget, SharpDX.Color.Yellow);
                  SharpDX.Direct2D1.Brush brushBlack             = new SharpDX.Direct2D1.SolidColorBrush(RenderTarget, SharpDX.Color.Black);
      
                  
                  xStatusCoordinate             = gapStatusXStart;                                                        //    SPACED 10 PIXELS RIGHT OF TARGETS            
                  newX = xStatusCoordinate - buttonWidth;
                              
                  newX = newX + buttonWidth;
                  gapStatusBox         = new SharpDX.Rectangle(newX, ChartPanel.Y + 10, buttonWidth, buttonHeight);
                  RenderTarget.FillRectangle(gapStatusBox, brushWhite);
                  RenderTarget.DrawText("AB", textFormat, gapStatusBox, brushBlack);    
                  
                  newX = newX + buttonWidth + space;
                  gapStatusBox         = new SharpDX.Rectangle(newX, ChartPanel.Y + 10, buttonWidth, buttonHeight);
                  RenderTarget.FillRectangle(gapStatusBox, brushWhite);
                  RenderTarget.DrawText("EQ", textFormat, gapStatusBox, brushBlack);    
      ​
      this is the beginning of my OnRender code. From the documentation it looked like I was supposed to create and dispose of my brushes in OnRender.

      Is that correct?

      If so, how do I create methods that I can call from OnRender if they need the brushes? I tried it and got an error that the method didn't have access to the brushes.

      Is there a different place in OnStateChange to create brushes?

      And can I dispose of them in State.Termionated?

      If not, how can my methods access them?

      thanks for your help

      Comment


        #4
        Hello cr8able,

        This sample script linked below demonstrates using a variable within the scope of the class that holds a brush that can be used as needed.



        Please let us know if you have any further questions.
        Gaby V.NinjaTrader Customer Service

        Comment


          #5
          hey Gabby
          I see the example you sent, but my problem isn't with too many brushes. And that example isn't using SharpDX brushes like I thought I needed for using them in OnRender.

          In the SampleCustomRender indicator, all the brushes are created inside of OnRender, used inside OnRender, and disposed of inside OnRender. For a larger program, that's a lot to go on inside OnRender.

          Before using OnRender, if I needed to draw multiple instances of the same type of object, I'd create a Method that drew the object. Then call it with different input parameters to draw each individual shape.

          What is the OnRender equivalent of something like that?

          Is there an example indicator that uses OnRender to draw, but is more complex?

          Comment


            #6
            Hello cre8able,

            Thanks for your response.

            If you want to create a method that can use your custom brush, you can create a parameter for your method that takes the DX brush. For example:

            Code:
            private void MyMethod(SharpDX.Direct2D1.Brush myDxBrush, SharpDX.Direct2D1.RenderTarget target)
            {
            target.DrawRectangle(new SharpDX.RectangleF(0, 0, 50, 50), myDxBrush);
            }
            However, please note that the brushes still need to be created in OnRender or OnRenderTargetChanged. You can assign those brushes to variables, but they can only ever by used by OnRender code. For example, you can make a class level variable for:

            private SharpDX.Direct2D1.Brush myDxBrush mybrush;

            Which can be used in OnRender or your custom methods, but note that can only ever be set from OnRender or OnRenderTargetChanged. Your custom method that uses this variable also needs to be called from OnRender.

            If you are creating class level variables for things like brushes, you need to use OnRenderTargetChanged to create resources. Clicking the border of the chart to resize it for example will cause a new render target to happen, along with many other events, so any private variables could become invalid between render passes.

            Please let us know if you have any further questions.​
            Gaby V.NinjaTrader Customer Service

            Comment


              #7
              Thanks Gabby, Here is a sample indicator to see if I got it right.

              Can you let me know if I got the right stuff in the right spot?. I will be adding Text and Rectangles, but I assume I can follow the same guidelines on where to put stuff

              I made the brush and vectors class level variables. I would do the same for textlayout and rectanges.

              In OnBarUpdate, I do all the math to find the values I need and then load them into a List. I have a load timer that I use to find out how efficiently my indicators load. I was shocked at how much faster it loaded with OnRendor instead of using things like Draw.Line. it took OnRender 1/10the the time to load that Draw.Line took.

              Since my variables are class level, I can just call my custom Method from OnRendor and put the code to display the lines in the method.

              in OnRenderTargetChanged I dispose of the brush and then create. I found this code in a NinjaScript Indicator and stole it. But want to make sure that is right

              I didn't dispose of the Vectors or the RenderTarget that I used in the custom Method. Is this correct or do I need to dispose of them?

              Should I dispose StrokeStyle, TextFormat & TestLayout in State.Terminated when I use them?

              thanks for your help


              HTML Code:
                  public class SwingHigh : Indicator
                  {
                      private const string CodeID = "SwingHigh";
                      public override string DisplayName {get {return CodeID; }}
                      
                      SharpDX.Direct2D1.Brush DxBrushAqua;    
                      
                      SharpDX.Vector2 lineStart;
                      SharpDX.Vector2 lineEnd;
                      
                      public class SwingHighLow
                      {
                          public DateTime         startTime;
                          public double            swingPrice;
                          public DateTime            endTime;
                          public bool                done;
                      }
                          private List<SwingHighLow> SwingHighs             = new List<SwingHighLow>();
              
                          private long                startTime;
                          private NinjaTrader.Gui.Tools.SimpleFont ArielFont12;
              
                      protected override void OnStateChange()
                      {
                          if (State == State.SetDefaults)
                          {
                              Description                    = @"Enter the description for your new custom Indicator here.";
                              Name                        = "SwingHigh";
                              Calculate                    = Calculate.OnPriceChange;
                              IsOverlay                    = true;
                              DrawOnPricePanel            = true;
                              ScaleJustification            = NinjaTrader.Gui.Chart.ScaleJustification.Right;
                              IsSuspendedWhileInactive    = true;
                          }
                          else if (State == State.Historical)
                          {
                              ArielFont12                 = new NinjaTrader.Gui.Tools.SimpleFont("Ariel", 12);
                          }
                          
                      }
                      
               //==================================================================================================            
                      protected override void OnBarUpdate()
                      {
                          if (CurrentBar < 2) return;    
                          if(CurrentBar == 5)    startTime = DateTime.Now.Ticks;            //=====    GET START TIME TO CHECK INDICATOR LOAD TIME
                          if (Count - 2 == CurrentBar && State == State.Historical)        //    IF LAST BAR AFTER LOADING HISTORICAL DATA
                              Print("  " + (((double)DateTime.Now.Ticks - startTime)/10000000).ToString("#.###") + "   " + SwingHighs.Count);    
                          DrawTextBox();
              
              //=====    LOOK FOR NEW SWING HIGHS
                          if ((IsFirstTickOfBar) && (High[1] > High[2] && High[1] > High[0]))        //=====    FOUND NEW SWING HIGH @ BAR 1
                          {
                              SwingHighLow v         = new SwingHighLow();                //===== ADD NEW SWING HIGH TO LIST                            
                              v.startTime            = Time[1];
                              v.swingPrice         = High[1];
                              v.endTime            = Time[1];
                              v.done                = false;
                              SwingHighs.Add(v);
                          }
              
              //=====    LOOK THROUGH ALL EXISTING SWING HIGHS TO SEE IF THEY ARE SWEPT
                          for (int x = SwingHighs.Count - 1; x >= 0 ; x--)
                          {
                              if (!SwingHighs[x].done)
                                  SwingHighs[x].endTime = Time[0];                        //=====    UPDATE END TIME TO NEW BAR
              
                              if(High[0] > SwingHighs[x].swingPrice)                        //=====    SWING HIGH SWEPT
                                  SwingHighs[x].done = true;
                          }            
                      }
                      
              //==================================================================================================            
                      protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
                      {
              
                          RenderSwingHighsFromList(chartControl, chartScale);
                          
                      }
                      
              //==================================================================================================                    
                      public override void OnRenderTargetChanged()
                      {
                          if (DxBrushAqua != null)
                              DxBrushAqua.Dispose();
              
                          DxBrushAqua    = new SharpDX.Direct2D1.SolidColorBrush(RenderTarget, SharpDX.Color.Aqua);
                      }        
                          
              //==================================================================================================            
                      private void RenderSwingHighsFromList(ChartControl chartControl, ChartScale chartScale)
                      {
                          for (int x = SwingHighs.Count - 1; x >= 0 ; x--)
                          {
                              int yPriceCoordinate = chartScale.GetYByValue(SwingHighs[x].swingPrice);        
                              int x1PriceCoordinate = chartControl.GetXByTime(SwingHighs[x].startTime);        
                              int x2PriceCoordinate = chartControl.GetXByTime(SwingHighs[x].endTime);        
                              
                              lineStart     = new SharpDX.Vector2(x1PriceCoordinate, yPriceCoordinate);
                              lineEnd     = new SharpDX.Vector2(x2PriceCoordinate, yPriceCoordinate);
                              RenderTarget.DrawLine(lineStart, lineEnd, DxBrushAqua, 1);
                          }
                      }
                      
              ​

              Comment


                #8
                Hello cre8able,

                I don't see anything obviously out of place within the script.

                StrokeStyle, TextFormat & TestLayout should be disposed any time they are created.

                Please see the section "SharpDX DisposeBase" under Best Practices for SharpDX resources which has details on how and when these resources should be disposed:

                HTML Code:
                https://ninjatrader.com/support/helpGuides/nt8/index.html?using_sharpdx_for_custom_chart_rendering.htm#BestPracticesforSharpDXResources
                ​
                Gaby V.NinjaTrader Customer Service

                Comment

                Latest Posts

                Collapse

                Topics Statistics Last Post
                Started by amousaber, Today, 09:30 AM
                0 responses
                4 views
                0 likes
                Last Post amousaber  
                Started by suraj, Today, 08:51 AM
                0 responses
                6 views
                0 likes
                Last Post suraj
                by suraj
                 
                Started by calmcosmia, Yesterday, 03:07 PM
                6 responses
                27 views
                0 likes
                Last Post karenmkrohn  
                Started by sofortune, 05-16-2024, 11:45 PM
                5 responses
                37 views
                0 likes
                Last Post sofortune  
                Started by Likomatervy, Today, 05:35 AM
                0 responses
                8 views
                0 likes
                Last Post Likomatervy  
                Working...
                X