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

Indicator lines drawn during OnRender get clipped until user clicks panel

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

    Indicator lines drawn during OnRender get clipped until user clicks panel

    I have an indicator that draws many lines during OnRender. It appears that they are getting clipped. When the user clicks on the panel, NT8 appears to expand the clipping rectangle and scale things so that the lines appear.

    Should I expect NT8 to scale things after OnRender returns? Or do I have a way to ask NT8 to re-scale the panel?

    #2
    Hello dween,

    Thank you for your post.

    Based on your description, this may be a symptom of the IsSuspendedWhileInactive property. Please try setting IsSuspendedWhileInactive to false in OnStateChange() when the state is State.SetDefaults or State.Configure and see if this resolves the behavior. For more information:


    Please let me know if I may be of further assistance.
    Emily C.NinjaTrader Customer Service

    Comment


      #3
      Originally posted by NinjaTrader_Emily View Post
      Hello dween,

      Thank you for your post.

      Based on your description, this may be a symptom of the IsSuspendedWhileInactive property. Please try setting IsSuspendedWhileInactive to false in OnStateChange() when the state is State.SetDefaults or State.Configure and see if this resolves the behavior.
      NinjaTrader_Emily - thanks for the suggestion. I made the change, setting
      Code:
      IsSuspendedWhileInactive = false;
      - I get the same result as before. Just to give you a visual example, I'm including the before and after in a picture below.

      Looking at messages I'm Print()-ing, I see that my code is triggered by calls to OnRender() and I get the first result below.

      I see that, once the user clicks the panel, I see a call to OnRenderTargetChanged() and then voila the panel looks right.

      * It would make sense to me that, after drawing a bunch of lines into the panel, that the "render target" (the panel) will have changed - it will be rescaled.
      * I'm guessing that somehow the internals have not picked up on the changes and have not decided (yet) to rescale what's on the panel.
      * I'm finally guessing that once the user touches the panel it finally does decide to rescale the panel - and this is reflected in a call to OnRenderTargetChanged().

      You probably see where I'm going

      => is there a way for me to tell NT8 "yo - you probably don't realize it but my indicator's panel has new drawing objects that require the panel's clipping/scaling to change - please compute the new clipping rectangle and scaling and re-call OnRenderTargetChanged()"?

      Note: I already have in place logic that waits until OnRender() has been called and then sets a timer for 0.25 - 1.50 seconds and then calls ForceRefresh() - that had been my fix but it stopped working - I note that it triggers calls to OnRender but not to OnRenderTargetChanged.
      Attached Files
      Last edited by dween; 09-28-2023, 04:39 PM.

      Comment


        #4
        Hello dween,

        Thank you for your reply.

        OnRender() is called frequently, especially after the state is State.Realtime. There is information about the marker used to call OnRender() and when it is reset here:There is a check every 250ms that decides if the chart needs to be updated, and typically ForceRefresh() should only be called if it is necessary to queue OnRender() to be called at the end of the next 250ms timer.

        How are your drawing objects and their coordinates on the chart being calculated? The platform comes with a SampleCustomRender indicator that renders various shapes that stay within the visible range of the chart. I am not able to get the same behavior with that indicator where if I click away from the chart they do not render as expected; they continuously stay rendered in the same location on the chart, even as new bars are formed.

        I look forward to your reply.
        Emily C.NinjaTrader Customer Service

        Comment


          #5
          Originally posted by NinjaTrader_Emily View Post
          Hello dween,

          Thank you for your reply.

          OnRender() is called frequently, especially after the state is State.Realtime. There is information about the marker used to call OnRender() and when it is reset here:There is a check every 250ms that decides if the chart needs to be updated, and typically ForceRefresh() should only be called if it is necessary to queue OnRender() to be called at the end of the next 250ms timer.
          Emily, I'm grateful that you're being responsive on this issue I'm wrestling with.

          I have a workaround that waits until after OnRender stops getting called & then calls ForceRefresh() - that isn't helping for some reason.


          Originally posted by NinjaTrader_Emily View Post
          How are your drawing objects and their coordinates on the chart being calculated? The platform comes with a SampleCustomRender indicator that renders various shapes that stay within the visible range of the chart. I am not able to get the same behavior with that indicator where if I click away from the chart they do not render as expected; they continuously stay rendered in the same location on the chart, even as new bars are formed.
          I suspect that I exceed some limit & that's why my drawn objects appear not to be accounted for in the clipping/scaling that's being done.

          I do a "Draw.Line" call for each bar in the price panel, and then perhaps a dozen additional rectangles and lines.

          I'll try an experiment where only a few bars are showing & see whether I see the issue in that scenario.​

          Comment


            #6
            Hello dween,

            Are there any errors appearing on the Log tab of the Control Center?

            (I'm wondering if you are re-using brushes or possibly running out of graphics memory)
            Chelsea B.NinjaTrader Customer Service

            Comment


              #7
              Originally posted by NinjaTrader_ChelseaB View Post
              Hello dween,

              Are there any errors appearing on the Log tab of the Control Center?

              (I'm wondering if you are re-using brushes or possibly running out of graphics memory)
              https://ninjatrader.com/support/foru...606#post789606

              Reasonable questions, Chelsea.

              1. I do not see any messages appear in the Log tab of the Control Center when I run this scenario (I can send a screen shot).
              2. Referring to your question about re-using brushes - I can confirm that I have code that runs on OnRenderTargetChanged that re-allocates the SharpDx brushes (I could show that code too...).

              Finally, looping back to the experiment I proposed during chat with Emily - I tried to tell whether the problem was that I was doing too much during OnRender so that I exceeded some time limit and the framework went on to choose initial scaling/sizing parameters for my panel before I was finished:

              I tested this by resizing my displayed security so that only a few bars showed (so that my "loop over all bars" would have a small # of loops) then loading my indicator - that made no difference - I still am only seeing the panel sized too small in the Y direction to show all the lines I drew during OnRender.

              Something I didn't mention: the problem only happens (but always happens) when I first load the Indicator during a session (or right after a recompile).

              So
              * no messages are generated in the Log tab
              * it appears not to be that I'm exceeding a fixed amount-of-time-spent-in-OnRender (because I limited the bars to work on and it made no difference)
              * it only happens for the first time I load the Indicator during a session

              Any ideas?

              This looks really bad and I don't have a workaround.

              Comment


                #8
                Hello dween,

                Thank you for your reply.

                When it comes to working with brushes, you have an option of using a static brush and implicitly recreating/disposing of the brush in OnRender() as shown in the first example on this page:
                Code:
                protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
                {
                  // implicitly recreate and dispose of brush on each render pass
                  using (SharpDX.Direct2D1.SolidColorBrush dxBrush = new SharpDX.Direct2D1.SolidColorBrush(RenderTarget, SharpDX.Color.Blue))
                  {
                    RenderTarget.FillRectangle(new SharpDX.RectangleF(ChartPanel.X, ChartPanel.Y, ChartPanel.W, ChartPanel.H), dxBrush);
                  }
                }​
                Or, you could recalculate the brush in OnRenderTargetChanged() as demonstrated in the example "Recalculating a SharpDX Brush based on user input" on the following page:More information about working with brushes may be found here:Since this happens when you are first loading the indicator or after recompiling the indicator, I am curious if this potentially has to do with any values that are precomputed outside of OnRender(). Do you also see this behavior if you right-click the chart and select Reload NinjaScript?

                There are some performance best practices listed on this page that may be relevant:Are there values you are calculating in OnBarUpdate() for example, that might not be available right away (depending on how frequently the Calculate property calls OnBarUpdate())? If you are precomputing values, I suggest adding Print() statements after those values are calculated to see if they are available before OnRender() is called. Perhaps some of the values used in OnRender() don't match the expected values until OnBarUpdate() is called in real time. Additionally, if you are not already, it is certainly a best practice to restrict OnRender() to only the visible bars on the chart. This helps to prevent spikes in CPU consumption and preserve PC resources. It makes your script more efficient and can prevent unexpected behaviors due to resource limitations.

                Even if OnBarUpdate() is set to calculate On Price Change or On Each Tick, when it is processed historically it will only be called on bar close unless the script is designed to use Tick Replay. If there are values used from OnBarUpdate() in your OnRender() logic, this may be important to keep in mind. For more info about developing for Tick Replay (which may or may not apply to your script):


                Please let me know if I may be of further assistance.
                Emily C.NinjaTrader Customer Service

                Comment


                  #9
                  I think I've said this already in fewer words, but here's my thought:

                  Context

                  I've made some calls to Draw.Rectangle, Draw.HorizontalLine and Draw.Line.

                  The NT8 framework decides how much of those should be visible based on the points I've drawn to and how those points align horizontally with the bars in my security, and apparently vertically scaling things to fit everything I've drawn.

                  This decision about how to scale things vertically is, I think, the problem: NT8 is only including a subset of the things that I've drawn: the "bounding box" is vertically starting around "0" and going positive, and I need the bounding box to extend to some negative extent.

                  Note that once a user moves the mouse to my panel, presses the mouse down and drags either left or right, suddenly NT8 reconsiders and re-computes the bounding box to include the Lines, Rectangles and HorizontalLines that I've drawn below the 'zero' line.

                  I'm doing exactly the same logic in OnRender (I've got traces that demonstrate this).


                  Proposal

                  NT8 provides an API call for my Indicator (ForceRefresh()) to ask NT8 to trigger a call to OnRender.

                  Could NT8 expose a way for my Indicator to explicitly ask for NT8 to do the precursor step, that is, to ask NT8 to reconsider the objects I have drawn, and recompute the bounding/clipping box for the objects I have drawn in my panel?

                  I figure it would then call both my OnRenderTargetChanged and OnRender methods, necessarily, afterwards.

                  Comment


                    #10
                    Hello dween,

                    Thank you for your patience and for the detailed explanation.

                    You asked, "Could NT8 expose a way for my Indicator to explicitly ask for NT8 to do the precursor step, that is, to ask NT8 to reconsider the objects I have drawn, and recompute the bounding/clipping box for the objects I have drawn in my panel?​"

                    Based on your description, it sounds like the MinValue and MaxValue are not being calculated as expected for the auto-scaling of the y-axis until a user clicks and drags on the chart. You could consider setting the MinValue and MaxValue in OnCalculateMinMax() to override the values used for the min/max when auto-scaling the y-axis:You would then just need to ensure that the values are calculated considering your objects that are drawn in the panel and that the MinValue and MaxValue are set accordingly.

                    I believe this should address your inquiry; I look forward to hearing the results.
                    Emily C.NinjaTrader Customer Service

                    Comment


                      #11
                      Originally posted by NinjaTrader_Emily View Post
                      Hello dween,

                      Thank you for your patience and for the detailed explanation.

                      You asked, "Could NT8 expose a way for my Indicator to explicitly ask for NT8 to do the precursor step, that is, to ask NT8 to reconsider the objects I have drawn, and recompute the bounding/clipping box for the objects I have drawn in my panel?​"

                      Based on your description, it sounds like the MinValue and MaxValue are not being calculated as expected for the auto-scaling of the y-axis until a user clicks and drags on the chart. You could consider setting the MinValue and MaxValue in OnCalculateMinMax() to override the values used for the min/max when auto-scaling the y-axis...
                      Emily, thank you for this lead - I'll pursue it - I'm feeling hopeful!

                      Comment

                      Latest Posts

                      Collapse

                      Topics Statistics Last Post
                      Started by Balage0922, Today, 07:38 AM
                      0 responses
                      1 view
                      0 likes
                      Last Post Balage0922  
                      Started by JoMoon2024, Today, 06:56 AM
                      0 responses
                      6 views
                      0 likes
                      Last Post JoMoon2024  
                      Started by Haiasi, 04-25-2024, 06:53 PM
                      2 responses
                      19 views
                      0 likes
                      Last Post Massinisa  
                      Started by Creamers, Today, 05:32 AM
                      0 responses
                      6 views
                      0 likes
                      Last Post Creamers  
                      Started by Segwin, 05-07-2018, 02:15 PM
                      12 responses
                      1,786 views
                      0 likes
                      Last Post Leafcutter  
                      Working...
                      X