Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

StringSeries issue

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

    StringSeries issue

    I have a string series that contains colours.
    All works lovely but I have come across an issue I simply don't understand.

    In the IsFirstTickOf bar I set brushes like so just for past bars ie[1][2], etc
    CurrentBar is updated real time in OnBarUpdate below
    Code:
    if(IsFirstTickOfBar)
    {
                         ChartControl.Dispatcher.InvokeAsync((Action)(() =>
                        {
                        if(myStringSeries != null)
                        myBrush1 =  (System.Windows.Media.SolidColorBrush)new BrushConverter().ConvertFromString(myStringSeries[1]);//CurrentBar forces error
                        myBrush2 =  (System.Windows.Media.SolidColorBrush)new BrushConverter().ConvertFromString(myStringSeries[2]);                  
                        }));​
    }
    In On BarUpdate I update the colors with each tick in a temp variable and I can check i have the right colours later, like so
    Code:
            if(Close[0] < Open[0])
            {
            temp = Brushes.Red;
             myStringSeries[0] = temp
            }
    
    //Convert current bar colour to a SolidColorBrush to be used in OnRender - has to be done in it's own thread otherwise it throws errors
                          ChartControl.Dispatcher.InvokeAsync((Action)(() =>
                        {    
                        myBrush0 = (System.Windows.Media.SolidColorBrush)new BrushConverter().ConvertFromString(temp.ToString());
                         ​}));
    
    
            if(myStringSeries != null)
                    {
             //CC is just a method to take the hex code and turn it into "Red,Blue", etc  
                        Draw.TextFixed(this,"DKDK",CC(temp.ToString())+"\n"+
                            CC(myStringSeries[1])+"\n"+
                            CC(myStringSeries[2])// requires a null test
                        ,TextPosition.TopLeft);//
    
                    }​​
    In my draw statements everything works tickety boo. All my colour sequences are correct.
    MyBrushes however have completely different values. CurrentBar value however is always correct. So It's something to do with the StringSeries but it has beaten me what it might be.

    Can anyone shine a light as to where I am going wrong?

    #2
    Update

    Ok I seem to have isolated the issue to the fact that it's going back to the first bar in the series - and stays there.
    When loading historical data it produces the string series as it should but when live it sticks at the first bar.
    How can myStringSeries[1] be the first bar on the chart - it's not making any sense yet?
    Last edited by Mindset; 10-13-2024, 03:13 AM.

    Comment


      #3
      Hello Mindset,

      Is there a reason you are using InvokeAsync for the code you posted? That should not be required for managing a brush.
      JesseNinjaTrader Customer Service

      Comment


        #4
        Yes - it comes up with thread error if I don't

        Comment


          #5
          Hello Mindset,

          I just tried the following in an indicator and there is no error when using this code without the invoke, invoke is only used when working with UI controls that exist on a different thread. Making a brush is not dealing with threading at all or working with Ui controls so using InvokeAsync puts that code out of the OnBarUpdate context so it will happen at a later time, not at the time of the bar that is processing.

          Brush brush = (System.Windows.Media.SolidColorBrush)new BrushConverter().ConvertFromString("#FF6600");
          JesseNinjaTrader Customer Service

          Comment


            #6
            Ah - my apologies - I have spent so long on this I reported the wrong error.
            The error is that BrushConverter cannot convert from Null.
            But my code asks if myStringSeries != null so again I am confused.
            PS I have taken the InvokeAsync out.

            Comment


              #7
              Update - ok so I have taken the invokes out - and I can print my string series brushes from IsFirstTickOfBar.
              I have checked my string values and they are all correct and printing.
              The error is now in OnRender.
              I am using a try catch statement but It prints out the error line as line zero which is of no help to me.
              The print statements continue to work however.

              Code:
                protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
                      {
                    
                          base.OnRender(chartControl, chartScale);
                          SharpDX.Direct2D1.AntialiasMode oldAntialiasMode = RenderTarget.AntialiasMode;
              
              try
                                  {        
                  n = (int)RHS_Font.TextFormatHeight;
                          SharpDX.Direct2D1.Brush dxBrush0 = myBrush0.ToDxBrush(RenderTarget);
                          SharpDX.Direct2D1.Brush dxBrush1 = myBrush1.ToDxBrush(RenderTarget);
                          SharpDX.Direct2D1.Brush dxBrush2 = myBrush2.ToDxBrush(RenderTarget);
              
                          TextLayout RHS0 = new TextLayout(Globals.DirectWriteFactory,volzero,
                          RHS_Font.ToDirectWriteTextFormat(), 200, 25);// 15 is font size, 60 is text width
                              
                          TextLayout RHS1 = new TextLayout(Globals.DirectWriteFactory,volone,
                          RHS_Font.ToDirectWriteTextFormat(), 125, 25);
                                  
                          TextLayout RHS2 = new TextLayout(Globals.DirectWriteFactory,voltwo,
                          RHS_Font.ToDirectWriteTextFormat(), 130, 30);  
              
                           SharpDX.Vector2 StartLive0 = new SharpDX.Vector2(XPosition, ChartPanel.Y + n * 1);
                          SharpDX.Vector2 StartLive1 = new SharpDX.Vector2(XPosition, ChartPanel.Y + n * 2);
                          SharpDX.Vector2 StartLive2 = new SharpDX.Vector2(XPosition, ChartPanel.Y + n * 3);
              
                      
                      
                          RenderTarget.DrawTextLayout(StartLive0,RHS0, dxBrush0);
                          RenderTarget.DrawTextLayout(StartLive1,RHS1, dxBrush1);                    
                          RenderTarget.DrawTextLayout(StartLive2,RHS2, dxBrush2);    
              
                              
                                  
                                  
                                  if(!RHS0.IsDisposed)
                                  RHS0.Dispose();
                                  if(!RHS1.IsDisposed)
                                  RHS1.Dispose();
                                  if(!RHS2.IsDisposed)
                                  RHS2.Dispose();
              
                                      dxBrush0.Dispose();
                                      dxBrush1.Dispose();
                                      dxBrush2.Dispose();
              
                                
                                  }    
                                                      catch(Exception ex)
                      {
                      Print ("Error");
               
                  var st = new StackTrace(ex, true);
                  var frame = st.GetFrame(0);
                  var line = frame.GetFileLineNumber();
                          Print( line);
              }
                 RenderTarget.AntialiasMode = oldAntialiasMode;
              }
              ​​​
              Edit I should add - further to your first comment regarding Invoke.
              myBrush0 which is the current bar volume and is updated each tick in OnBarUpdate requires an Invoke otherwise it simply does not print and throws errors.
              Last edited by Mindset; 10-14-2024, 01:28 PM.

              Comment


                #8
                Hello Mindset,

                Thank you for the update.

                Is it the same error or something else now with OnRender? I am not seeing StringSeries in the code you provided for OnRender. Could you provide some more detail on what specifically is happening with that code in contrast to the other code you posted?



                JesseNinjaTrader Customer Service

                Comment


                  #9
                  Hello Jesse

                  I am trying to get myStringSeries - which is a series of colors - to be populated into SharpDX colors in an OnRender textLayout.
                  Like so....
                  Click image for larger version  Name:	rhs text.png Views:	0 Size:	34.8 KB ID:	1321456
                  This is currently done via a very clunky and not user friendly Draw.Text statements.

                  I collect colors via a temp variable in OnBarUpdate which changes with each tick; so temp = myStringSeries[0].
                  my OnBarUpdate code is below - this is for the CurrentBar only - note the use of the temp variable rather than myStringSeries as this changes throughout the life of the bar

                  Code:
                  ​
                  if(Close[0] < Open[0])
                  {
                  temp = Brushes.Red;
                  }
                  
                  if(Close[0] < Open[0])
                  {
                  temp = Brushes.Green;
                  }​
                  myStringSeries[0] = temp
                  
                  
                  //Convert current bar colour to a SolidColorBrush to be used in OnRender - has to be done in it's own thread otherwise it throws errors
                  ChartControl.Dispatcher.InvokeAsync((Action)(() =>
                  {
                  myBrush0 = (System.Windows.Media.SolidColorBrush)new BrushConverter().ConvertFromString(temp.ToString());
                  ​}));
                  
                  Print(myBrush0.ToString());




                  On the FirstTickOfBar I then use eg myStringSeries[1] to give my text layout for HISTORICAL bars only the correct color - and this is confirmed by the print statement.
                  So here the color is correct.​


                  Code:
                  if(IsFirstTickOfBar)
                  {
                                      if(myStringSeries[1] != null && myStringSeries[2] != null)
                  {
                                        myBrush1 = (System.Windows.Media.SolidColorBrush)new BrushConverter().ConvertFromString(myStringSeries[1]);
                                      myBrush2 = (System.Windows.Media.SolidColorBrush)new BrushConverter().ConvertFromString(myStringSeries[2]);
                  
                  Print("IsFirstTick Brush 1 =  " + myBrush1.ToString());// this value is correct
                  }
                  }​​
                  The issue appears to be in the OnRender method. The minute I try and use the line
                  SharpDX.Direct2D1.Brush dxBrush1 = myBrush1.ToDxBrush(RenderTarget);
                  everything stops working.
                  I have checked my string statements and colors all the way through to OnRender and they are correct for all values.
                  I have tried just using a random SharpDX color in onRender - and when I do that the currentbar text changes color with each tick and the historical
                  texts print with the random color - so clearly it's a color issue.
                  I just can't see why the dxBrush line causes issues.

                  Code:
                  protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
                  {
                  
                  base.OnRender(chartControl, chartScale);
                  SharpDX.Direct2D1.AntialiasMode oldAntialiasMode = RenderTarget.AntialiasMode;
                  
                  try
                  {
                  n = (int)RHS_Font.TextFormatHeight;
                  SharpDX.Direct2D1.Brush dxBrush0 = myBrush0.ToDxBrush(RenderTarget);
                  SharpDX.Direct2D1.Brush dxBrush1 = myBrush1.ToDxBrush(RenderTarget);//[COLOR=#c0392b] the minute I add this everthing goes to error[/COLOR]
                  
                  
                  TextLayout RHS0 = new TextLayout(Globals.DirectWriteFactory,volzero,
                  RHS_Font.ToDirectWriteTextFormat(), 200, 25);// 15 is font size, 60 is text width
                  
                  TextLayout RHS1 = new TextLayout(Globals.DirectWriteFactory,volone,
                  RHS_Font.ToDirectWriteTextFormat(), 125, 25);
                  
                  
                  SharpDX.Vector2 StartLive0 = new SharpDX.Vector2(XPosition, ChartPanel.Y + n * 1);
                  SharpDX.Vector2 StartLive1 = new SharpDX.Vector2(XPosition, ChartPanel.Y + n * 2);
                  
                  RenderTarget.DrawTextLayout(StartLive0,RHS0, dxBrush0);
                  RenderTarget.DrawTextLayout(StartLive1,RHS1, dxBrush1);
                  
                  if(!RHS0.IsDisposed)
                  RHS0.Dispose();
                  if(!RHS1.IsDisposed)
                  RHS1.Dispose();
                  
                  
                  dxBrush0.Dispose();
                  dxBrush1.Dispose();
                  
                  
                  
                  }
                  catch(Exception ex)
                  {
                  Print ("Error");
                  
                  var st = new StackTrace(ex, true);
                  var frame = st.GetFrame(0);
                  var line = frame.GetFileLineNumber();
                  Print( line);
                  }
                  RenderTarget.AntialiasMode = oldAntialiasMode;
                  }
                  ​​​​
                  I hope this explanation is ok but if not feel free to ask more questions.

                  Comment


                    #10
                    Hello Mindset,

                    The way you are using a SolidColorBrush is not correct for this type of use case and the dispatcher is putting that brush on the wrong thread so the value that gets set to the variable is not happening at the same time when that code is called. The correct way to do what you are asking or to create brushes to be used with OnRender is to call ToDxBrush directly on the brush, you don't need to re convert the brush after you set it to the variable.

                    If you have more than 1 brush its best to use OnRenderTargetChanged for those specific actions. The sample in the help guide shows how to conditionally set a brush from OnBarUpdate and then use that from OnRender and also handles the conversion to a DXBrush. Take a look at the sample named Recalculating a SharpDX Brush conditionally in OnBarUpdate()



                    Another note here would be to not use a string series to hold brushes, you can just make a Brush series and hold those brushes directly so no conversions are required.


                    JesseNinjaTrader Customer Service

                    Comment


                      #11
                      Thanks for the guidance Jesse - will look into and hopefully sort it out.

                      Comment


                        #12
                        Ok I have gone backwards lol.

                        I tried to follow the help files and use the OnREnderTargetChanged
                        using the following

                        Code:
                        public class BrushCondition : Indicator
                            {
                            private SharpDX.Direct2D1.Brush dxBrush = null; // the SharpDX brush used for rendering      
                        private System.Windows.Media.SolidColorBrush brushColor; // used to determine the color of the brush conditionally
                        
                                protected override void OnStateChange()
                                {
                                    if (State == State.SetDefaults)
                                    {
                                        Description                                    = @"Enter the description for your new custom Indicator here.";
                                        Name                                        = "BrushCondition";
                                        Calculate                                    = Calculate.OnBarClose;
                                        IsOverlay                                    = false;
                                        DisplayInDataBox                            = true;
                                        DrawOnPricePanel                            = true;
                                        DrawHorizontalGridLines                        = true;
                                        DrawVerticalGridLines                        = true;
                                        PaintPriceMarkers                            = true;
                                        ScaleJustification                            = NinjaTrader.Gui.Chart.ScaleJustification.Right;
                                
                                        IsSuspendedWhileInactive                    = true;
                                    }
                        
                                }
                        
                                protected override void OnBarUpdate()
                                {
                                    if(CurrentBar < 50)
                                        return;
                        
                                    if(Close[0] > Open[0])
                                        brushColor = Brushes.ForestGreen;
                                    else if (Close[0] < Open[0])
                                        brushColor = Brushes.Crimson;
                                    else
                                        brushColor = Brushes.PowderBlue;
                                }
                                        
                                public override void OnRenderTargetChanged()
                                {
                                  
                                    
                                        if(brushColor != null)
                                        {
                        
                                        if(dxBrush !=null)
                                            dxBrush.Dispose();
                                        
                                        if(RenderTarget !=null)
                                            dxBrush = brushColor.ToDxBrush(RenderTarget);
                                        }
                                }
                                
                                        protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
                                {
                          RenderTarget.FillRectangle(new SharpDX.RectangleF(ChartPanel.X, ChartPanel.Y, ChartPanel.W, ChartPanel.H/2), dxBrush);    
                                }
                        
                        
                            }
                        }​
                        And basically I can't get it to work - it works some of the time but if I change anything it gives me dire warnings of protected memory.
                        I am feeling quite thick at the moment as this is something that seems so basic.
                        Last edited by Mindset; 10-16-2024, 04:19 AM.

                        Comment


                          #13
                          Hello Mindset,

                          OnRender() may be running before you have assigned a Brush object to the brushColor variable and the brushColor variable may be null which means the dxBrush is also null.

                          Try checking the dxBrush is not null before trying to use it.

                          if (dxBrush != null)
                          {
                          RenderTarget.FillRectangle(new SharpDX.RectangleF(ChartPanel.X, ChartPanel.Y, ChartPanel.W, ChartPanel.H/2), dxBrush);
                          }
                          Chelsea B.NinjaTrader Customer Service

                          Comment


                            #14
                            Ah thank you, ChelseaB
                            I now have it plotting (OnEachTick) and it does not change color - or at least it does sporadically on occasion - certainly not working as it should.

                            I revised the code slightly so that I could see if the logic was operating correctly.

                            Code:
                                    protected override void OnBarUpdate()
                                    {
                                        if(CurrentBar < 50)
                                            return;
                                        string p  = String.Empty;
                                        if(Close[0] > Open[0])
                                        {
                                            brushColor = Brushes.ForestGreen;
                                            p = "Green";
                                        }
                                        else if (Close[0] < Open[0])
                                        {
                                            brushColor = Brushes.Crimson;
                                            p = "Crimson";
                                        }
                                        else
                                        {
                                            brushColor = Brushes.Blue;
                                            p = "Blue";
                                        }
                                        
                                        Draw.TextFixed(this,"Brushcolor",p,TextPosition.BottomRight);
                                    }
                                                #region OnRender
                                    public override void OnRenderTargetChanged()
                                    {
                                      
                                        
                                            if(brushColor != null)
                                            {
                            
                                            if(dxBrush !=null)
                                                dxBrush.Dispose();
                                            
                                            if(RenderTarget !=null)
                                                dxBrush = brushColor.ToDxBrush(RenderTarget);
                                            }
                                    }
                                    
                                            protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
                                    {
                                        if(dxBrush != null)
                              RenderTarget.FillRectangle(new SharpDX.RectangleF(ChartPanel.X, ChartPanel.Y, ChartPanel.W, ChartPanel.H/3), dxBrush);    
                                    }
                             
                            #endregion​

                            Comment


                              #15
                              Hello Mindset,

                              You are only generating a sharpdx brush when the render target changes, not after the brushColor has been re-assigned.

                              You could use a flag to notify the brush has changed.

                              private bool brushUpdated;

                              In OnBarUpdate():
                              Code:
                              if(Close[0] > Open[0])
                              {
                              brushColor = Brushes.ForestGreen;
                              brushUpdated = true;
                              }
                              In OnRender():
                              Code:
                              ​
                              if (brushUpdated)
                              {
                              if (dxBrush != null)
                              dxBrush.Dispose()​;
                              dxBrush = brushColor.ToDxBrush(RenderTarget);
                              }
                              ​Or you could use different variables to hold each color, and use if statements to select the variable (which would have less resource cost).

                              private Brush brushColor;
                              private SharpDX.Direct2D1.Brush greenDXBrush;
                              private SharpDX.Direct2D1.Brush blueDXBrush;
                              private SharpDX.Direct2D1.Brush dxBrush;​

                              In OnRenderTargetChanged:
                              Code:
                              if (greenDXBrush != null)
                              greenDXBrush.Dispose();
                              
                              if (RenderTarget != null)
                              greenDXBrush = Brushes.ForestGreen.ToDxBrush(RenderTarget);​
                              
                              if (blueDXBrush != null)
                              blueDXBrush.Dispose();
                              
                              if (RenderTarget != null)
                              blueDXBrush = Brushes.Blue.ToDxBrush(RenderTarget);​​​
                              In OnRender():
                              Code:
                              if (brushColor == Brushes.Green)
                              dxBrush = greenDXBrush;
                              
                              if (brushColor == Brushes.Blue)
                              dxBrush = blueDXBrush;


                              The Jtrealstats Realtime indicator on the User App Share uses a dictionary to store the brushes.
                              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;]
                              Chelsea B.NinjaTrader Customer Service

                              Comment

                              Latest Posts

                              Collapse

                              Topics Statistics Last Post
                              Started by Pabulon, Today, 12:40 PM
                              1 response
                              8 views
                              0 likes
                              Last Post NinjaTrader_Gaby  
                              Started by kazbek966, Today, 12:25 PM
                              1 response
                              9 views
                              0 likes
                              Last Post NinjaTrader_Jesse  
                              Started by jeb1000, Yesterday, 02:36 PM
                              8 responses
                              21 views
                              0 likes
                              Last Post jeb1000
                              by jeb1000
                               
                              Started by vp2199.cqg, Today, 12:28 PM
                              0 responses
                              5 views
                              0 likes
                              Last Post vp2199.cqg  
                              Started by kazbek966, Today, 12:14 PM
                              2 responses
                              10 views
                              0 likes
                              Last Post kazbek966  
                              Working...
                              X