Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

Side by Side Bar Plot

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

    Side by Side Bar Plot

    See below pic. I am looking to change the below plot so that the Red and Green Bar plots are side by side (under the current bar). In order to make Red Bar visible, I am forced to make it a negative value otherwise it will be hidden behind the Green Bar when I make them both positive values. I attached the code. I am looking to make DownCount a positive value and plot is so it is side by side with the UpCount. How would I go about doing that?

    Click image for larger version

Name:	image.png
Views:	318
Size:	23.1 KB
ID:	1337574

    HTML Code:
    #region Using declarations
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Xml.Serialization;
    using NinjaTrader.Cbi;
    using NinjaTrader.Gui;
    using NinjaTrader.Gui.Chart;
    using NinjaTrader.Gui.SuperDom;
    using NinjaTrader.Gui.Tools;
    using NinjaTrader.Data;
    using NinjaTrader.NinjaScript;
    using NinjaTrader.Core.FloatingPoint;
    using NinjaTrader.NinjaScript.DrawingTools;
    #endregion
    
    //This namespace holds Indicators in this folder and is required. Do not change it.
    namespace NinjaTrader.NinjaScript.Indicators
    {
        public class OrderSpeed : Indicator
        {
            private int UpCount;
            private int DownCount;
            private int TotalCount;
            private double currentAsk;
            private double currentBid;
            
            private int barSpacing = 5; // Adjust spacing between bars
            
            protected override void OnStateChange()
            {
                if (State == State.SetDefaults)
                {
                    Description                                    = @"Enter the description for your new custom Indicator here.";
                    Name                                        = "OrderSpeed";
                    Calculate                                    = Calculate.OnEachTick;
                    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;
                    UpCount = 0;
                    DownCount = 0;
                    TotalCount = 0;
                    BarThickness = 8;
                }
                else if (State == State.Configure)
                {
                    AddPlot(new Stroke(Brushes.Green, BarThickness),PlotStyle.Bar,"UpCount");
                    AddPlot(new Stroke(Brushes.Red, BarThickness),PlotStyle.Bar,"DownCount");
                    AddPlot(new Stroke(Brushes.Orange, BarThickness),PlotStyle.Bar,"TotalCount");
                }
            }
    
            protected override void OnMarketData(MarketDataEventArgs marketDataUpdate)
            {
                if (marketDataUpdate.MarketDataType != MarketDataType.Last)
                    return;
    
                if (marketDataUpdate.Price >= marketDataUpdate.Ask)
                {
                    UpCount = UpCount + 1;
                }
                else if (marketDataUpdate.Price <= marketDataUpdate.Bid)
                {
                    DownCount = DownCount + 1;
                }
                
                TotalCount = UpCount + DownCount;
                Print(marketDataUpdate.Time + "TotalCount: " + TotalCount + "| Volume: " + marketDataUpdate.Volume);
                
            }
            protected override void OnBarUpdate()
            {
                Values[0][0] = UpCount;
                Values[1][0] = -DownCount;
    //            Values[2][0] = TotalCount;
    //            if(UpCount >= DownCount)
    //            {
    //                PlotBrushes[2][0] = Brushes.Green;
    //            }
    //            else
    //            {
    //                PlotBrushes[2][0] = Brushes.Red;
    //            }
                
                if(IsFirstTickOfBar == true)
                {
                    UpCount = 0;
                    DownCount = 0;
                    TotalCount = 0;
                }
                        
            }
            
            #region Properties
            [NinjaScriptProperty]
            [Display(Name = "Set Bar Thickness", GroupName = "Indicator Parameters", Order = 3)]
            public int BarThickness
            { get; set; }
            #endregion
        }
    }
    
    #region NinjaScript generated code. Neither change nor remove.
    
    namespace NinjaTrader.NinjaScript.Indicators
    {
        public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
        {
            private OrderSpeed[] cacheOrderSpeed;
            public OrderSpeed OrderSpeed(int barThickness)
            {
                return OrderSpeed(Input, barThickness);
            }
    
            public OrderSpeed OrderSpeed(ISeries<double> input, int barThickness)
            {
                if (cacheOrderSpeed != null)
                    for (int idx = 0; idx < cacheOrderSpeed.Length; idx++)
                        if (cacheOrderSpeed[idx] != null && cacheOrderSpeed[idx].BarThickness == barThickness && cacheOrderSpeed[idx].EqualsInput(input))
                            return cacheOrderSpeed[idx];
                return CacheIndicator<OrderSpeed>(new OrderSpeed(){ BarThickness = barThickness }, input, ref cacheOrderSpeed);
            }
        }
    }
    
    namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
    {
        public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
        {
            public Indicators.OrderSpeed OrderSpeed(int barThickness)
            {
                return indicator.OrderSpeed(Input, barThickness);
            }
    
            public Indicators.OrderSpeed OrderSpeed(ISeries<double> input , int barThickness)
            {
                return indicator.OrderSpeed(input, barThickness);
            }
        }
    }
    
    namespace NinjaTrader.NinjaScript.Strategies
    {
        public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
        {
            public Indicators.OrderSpeed OrderSpeed(int barThickness)
            {
                return indicator.OrderSpeed(Input, barThickness);
            }
    
            public Indicators.OrderSpeed OrderSpeed(ISeries<double> input , int barThickness)
            {
                return indicator.OrderSpeed(input, barThickness);
            }
        }
    }
    
    #endregion
    
    ​

    #2
    Hello algospoke,

    You would need to custom render rectangles and not render the plots.

    Below is a link to the Desktop SDK on custom rendering.
    Join the official NinjaScript Developer Community for comprehensive resources, documentation, and community support. Build custom indicators and automated strategies for the NinjaTrader platforms with our extensive guides and APIs.

    Join the official NinjaScript Developer Community for comprehensive resources, documentation, and community support. Build custom indicators and automated strategies for the NinjaTrader platforms with our extensive guides and APIs.


    And a link to an example that renders a scrollable object.
    Chelsea B.NinjaTrader Customer Service

    Comment


      #3
      Chelsea,

      I am working thru the links and sample you have provided and I am getting a handle on it. I am working on creating a rectangle whose height changes based on the body of the current candle. I am calculating OnEachTick. Below is a pic of the code that is very simple and the print comes out fine.

      Click image for larger version

Name:	image.png
Views:	213
Size:	6.8 KB
ID:	1338528

      When I apply this BarHeight to the "height" section of the SharpDX.Rectangle, it doesn't match the Y-axis tick size of the chart. I assume the "height" section of the SharpDX.Rectangle doesn't directly correlate to the y-axis in terms of ticks. What would I need to change so that the height of my bar is equivalent to the height of the candle body no matter what instrument I am looking at (i.e. different tick sizes)?

      Comment


        #4
        Hello algospoke,

        The bar height in price would be High[0] - Low[0].

        Converted to y values in OnRender() this would be chartScale.GetYByValue(Bars.GetLow(CurrentBar)) - chartScale.GetYByValue(Bars.GetHigh(CurrentBar)).
        Where CurrentBar would be replaced with the bar number you want the height of.
        Chelsea B.NinjaTrader Customer Service

        Comment


          #5
          Chelsea,

          I was able to get side by side bars (my original request) with the info you provided. That was very helpful. My issue now is that the y-axis on the plot doesn't auto adjust for the max height between the two bars. In the first pic below you can see that the bar heights exceed the height of the axis. In the second pic below you will see that if I zoom out, the bar shows the correct height when compared to the y-axis values. How do I get the y-axis of the chart to auto adjust as the bars increase in size? I provided my code for reference.

          Click image for larger version

Name:	image.png
Views:	171
Size:	20.3 KB
ID:	1338798
          Click image for larger version

Name:	image.png
Views:	179
Size:	19.1 KB
ID:	1338799
          HTML Code:
          #region Using declarations
          using System;
          using System.Collections.Generic;
          using System.ComponentModel;
          using System.ComponentModel.DataAnnotations;
          using System.Linq;
          using System.Text;
          using System.Threading.Tasks;
          using System.Windows;
          using System.Windows.Input;
          using System.Windows.Media;
          using System.Xml.Serialization;
          using NinjaTrader.Cbi;
          using NinjaTrader.Gui;
          using NinjaTrader.Gui.Chart;
          using NinjaTrader.Gui.SuperDom;
          using NinjaTrader.Gui.Tools;
          using NinjaTrader.Data;
          using NinjaTrader.NinjaScript;
          using NinjaTrader.Core.FloatingPoint;
          using NinjaTrader.NinjaScript.DrawingTools;
          #endregion
          
          //This namespace holds Indicators in this folder and is required. Do not change it.
          namespace NinjaTrader.NinjaScript.Indicators
          {
              public class OrderSpeed : Indicator
              {
                  private int UpCount;
                  private int DownCount;
                  private int TotalCount;
                  private double currentAsk;
                  private double currentBid;
                  
                  private System.Windows.Media.Brush    UpCountColor;
                  private System.Windows.Media.Brush    DownCountColor;
                  private int BarOpacity;
                  private float XLocation;
                  private float UpBarTop;
                  private float DownBarTop;
                  private float UpBarHeight;
                  private float DownBarHeight;
                  private float BarWidth;
                  private float YBaseline;
                  
                  protected override void OnStateChange()
                  {
                      if (State == State.SetDefaults)
                      {
                          Description                                    = @"Enter the description for your new custom Indicator here.";
                          Name                                        = "OrderSpeed";
                          Calculate                                    = Calculate.OnEachTick;
                          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;
                          UpCount = 0;
                          DownCount = 0;
                          TotalCount = 0;
                          BarThickness = 8;
                          UpCountColor = Brushes.Green;
                          DownCountColor = Brushes.Red;
                          BarOpacity =50;
                      }
                      else if (State == State.Configure)
                      {
                      }
                  }
          
                  protected override void OnMarketData(MarketDataEventArgs marketDataUpdate)
                  {
                      if (marketDataUpdate.MarketDataType != MarketDataType.Last)
                          return;
          
                      if (marketDataUpdate.Price >= marketDataUpdate.Ask)
                      {
                          UpCount = UpCount + 1;
                      }
                      else if (marketDataUpdate.Price <= marketDataUpdate.Bid)
                      {
                          DownCount = DownCount + 1;
                      }
                      
                      TotalCount = UpCount + DownCount;
                      Print(marketDataUpdate.Time + "UpCount: " + UpCount + "| DownCount: " + DownCount);
                      
                  }
                  protected override void OnBarUpdate()
                  {
                      if(CurrentBar < 1)
                          return;
                      
                      if(IsFirstTickOfBar == true)
                      {
                          UpCount = 0;
                          DownCount = 0;
                          TotalCount = 0;
                      }
                  }
                  
                  protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
                  {
                      if(UpCountColor == null)
                          UpCountColor = Brushes.Green;
                      
                      if(DownCountColor == null)
                          DownCountColor = Brushes.Red;
                      
                      SharpDX.Direct2D1.Brush UpCountColorDx = UpCountColor.ToDxBrush(RenderTarget, BarOpacity / 100f);//Sets color of the Up Bars
                      SharpDX.Direct2D1.SolidColorBrush UpCountBarOutline = new SharpDX.Direct2D1.SolidColorBrush(RenderTarget, SharpDX.Color.Green);//Sets color of Up Bar Outline
                      
                      SharpDX.Direct2D1.Brush DownCountColorDX = DownCountColor.ToDxBrush(RenderTarget, BarOpacity / 100f);//Sets color of the Down Bars
                      SharpDX.Direct2D1.SolidColorBrush DownCountBarOutline = new SharpDX.Direct2D1.SolidColorBrush(RenderTarget, SharpDX.Color.Red);//Sets color of Down Bar Outline
                      
                      XLocation = chartControl.GetXByBarIndex(ChartBars,CurrentBar);//Grabs x-location of current bar
                      UpBarTop = chartScale.GetYByValue(UpCount);
                      DownBarTop = chartScale.GetYByValue(DownCount);
                      BarWidth = (float)chartControl.BarWidth;//Sets the bar width of the UpCount & DownCount Bars equal to half size of chart's bar width
                      YBaseline = chartScale.GetYByValue(0);
                      
                      float baselineY = chartScale.GetYByValue(0);
                      
                      SharpDX.RectangleF UpCountBar = new SharpDX.RectangleF(XLocation,UpBarTop,BarWidth,YBaseline-UpBarTop);
                      RenderTarget.FillRectangle(UpCountBar, UpCountColorDx);
                      RenderTarget.DrawRectangle(UpCountBar, UpCountBarOutline);
                      
                      SharpDX.RectangleF DownCountBar = new SharpDX.RectangleF(XLocation-BarWidth,DownBarTop,BarWidth,YBaseline-DownBarTop);
                      RenderTarget.FillRectangle(DownCountBar, DownCountColorDX);
                      RenderTarget.DrawRectangle(DownCountBar, DownCountBarOutline);
                              
                      
                  }
                      
                  #region Properties
                  [NinjaScriptProperty]
                  [Display(Name = "Set Bar Thickness", GroupName = "Indicator Parameters", Order = 3)]
                  public int BarThickness
                  { get; set; }
                  #endregion
              }
          }
          
          #region NinjaScript generated code. Neither change nor remove.
          
          namespace NinjaTrader.NinjaScript.Indicators
          {
              public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
              {
                  private OrderSpeed[] cacheOrderSpeed;
                  public OrderSpeed OrderSpeed(int barThickness)
                  {
                      return OrderSpeed(Input, barThickness);
                  }
          
                  public OrderSpeed OrderSpeed(ISeries<double> input, int barThickness)
                  {
                      if (cacheOrderSpeed != null)
                          for (int idx = 0; idx < cacheOrderSpeed.Length; idx++)
                              if (cacheOrderSpeed[idx] != null && cacheOrderSpeed[idx].BarThickness == barThickness && cacheOrderSpeed[idx].EqualsInput(input))
                                  return cacheOrderSpeed[idx];
                      return CacheIndicator<OrderSpeed>(new OrderSpeed(){ BarThickness = barThickness }, input, ref cacheOrderSpeed);
                  }
              }
          }
          
          namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
          {
              public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
              {
                  public Indicators.OrderSpeed OrderSpeed(int barThickness)
                  {
                      return indicator.OrderSpeed(Input, barThickness);
                  }
          
                  public Indicators.OrderSpeed OrderSpeed(ISeries<double> input , int barThickness)
                  {
                      return indicator.OrderSpeed(input, barThickness);
                  }
              }
          }
          
          namespace NinjaTrader.NinjaScript.Strategies
          {
              public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
              {
                  public Indicators.OrderSpeed OrderSpeed(int barThickness)
                  {
                      return indicator.OrderSpeed(Input, barThickness);
                  }
          
                  public Indicators.OrderSpeed OrderSpeed(ISeries<double> input , int barThickness)
                  {
                      return indicator.OrderSpeed(input, barThickness);
                  }
              }
          }
          
          #endregion
          
          ​
          Attached Files

          Comment


            #6
            Hello algospoke,

            Override the OnCalculateMinMax() method and set the MaxValue and MinValue to the top price and bottom price of the price scale.
            Join the official NinjaScript Developer Community for comprehensive resources, documentation, and community support. Build custom indicators and automated strategies for the NinjaTrader platforms with our extensive guides and APIs.
            Chelsea B.NinjaTrader Customer Service

            Comment


              #7
              Chelsea,

              The goal of this indicator is to show the number of buy trades (green bar) vs sell trades (sell trades).

              Question:
              The Y-axis isn't updating correctly with the new MaxValue like you suggested. See pic below of the chart where the bars are still exceeding the Y-axis value. When I add prints, it looks like the prints in OnBarUpdate are printing on the correct time and date whereas the prints in OnCalculateMinMax() are printing on the wrong time and date. Why is this happening? I want the MaxValue to update whenever IsFirstTickOfBar == true. How do I do that? (fyi I am using playback). See below for output window & code.

              Click image for larger version

Name:	image.png
Views:	273
Size:	31.5 KB
ID:	1339777
              Click image for larger version

Name:	image.png
Views:	162
Size:	27.5 KB
ID:	1339778
              HTML Code:
              #region Using declarations
              using System;
              using System.Collections.Generic;
              using System.ComponentModel;
              using System.ComponentModel.DataAnnotations;
              using System.Linq;
              using System.Text;
              using System.Threading.Tasks;
              using System.Windows;
              using System.Windows.Input;
              using System.Windows.Media;
              using System.Xml.Serialization;
              using NinjaTrader.Cbi;
              using NinjaTrader.Gui;
              using NinjaTrader.Gui.Chart;
              using NinjaTrader.Gui.SuperDom;
              using NinjaTrader.Gui.Tools;
              using NinjaTrader.Data;
              using NinjaTrader.NinjaScript;
              using NinjaTrader.Core.FloatingPoint;
              using NinjaTrader.NinjaScript.DrawingTools;
              #endregion
              
              //This namespace holds Indicators in this folder and is required. Do not change it.
              namespace NinjaTrader.NinjaScript.Indicators
              {
                  public class OrderSpeed : Indicator
                  {
                      private int UpCount;
                      private int DownCount;
                      private int TotalCount;
                      private double currentAsk;
                      private double currentBid;
                      private int BarCounter;
                      
                      private System.Windows.Media.Brush    UpCountColor;
                      private System.Windows.Media.Brush    DownCountColor;
                      private int BarOpacity;
                      private float XLocation;
                      private float UpBarTop;
                      private float DownBarTop;
                      private float UpBarHeight;
                      private float DownBarHeight;
                      private float BarWidth;
                      private float YBaseline;
                      
                      private List<(int BarIndex, float RedBar, float GreenBar)> BarData = new List<(int BarIndex, float RedBar, float GreenBar)>();
                      private float MaxRedBar;
                      private float MaxGreenBar;
                      
                      protected override void OnStateChange()
                      {
                          if (State == State.SetDefaults)
                          {
                              Description                                    = @"Enter the description for your new custom Indicator here.";
                              Name                                        = "OrderSpeed";
                              Calculate                                    = Calculate.OnEachTick;
                              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;
                              IsAutoScale = true;
                              UpCount = 0;
                              DownCount = 0;
                              TotalCount = 0;
                              BarCounter = 0;
                              BarThickness = 8;
                              UpCountColor = Brushes.Green;
                              DownCountColor = Brushes.Red;
                              BarOpacity =50;
                          }
                          else if (State == State.Configure)
                          {
                          }
                      }
              
                      protected override void OnMarketData(MarketDataEventArgs marketDataUpdate)
                      {
                          if (marketDataUpdate.MarketDataType != MarketDataType.Last)
                              return;
              
                          if (marketDataUpdate.Price >= marketDataUpdate.Ask)
                          {
                              UpCount = UpCount + 1;
                          }
                          else if (marketDataUpdate.Price <= marketDataUpdate.Bid)
                          {
                              DownCount = DownCount + 1;
                          }
                          
                          TotalCount = UpCount + DownCount;
              //            Print(marketDataUpdate.Time + "UpCount: " + UpCount + "| DownCount: " + DownCount);
              //            Print(marketDataUpdate.Time + "| Market DownCount: " + DownCount);
                          
                      }
                      protected override void OnBarUpdate()
                      {
                          if(State == State.Historical)
                              return;
                          
                          if(CurrentBar < 1)
                              return;
                                      
                          if(IsFirstTickOfBar == true)
                          {
                              BarCounter = BarCounter + 1;
                              
              //                var LastRow1 = BarData[BarData.Count - 1];
              //                Print(Time[0].ToString() + "LastRow.RedBar: " + LastRow.RedBar);
              //                Print(Time[0].ToString() + "LastRow.GreenBar: " + LastRow.GreenBar);
              //                Print(Time[0].ToString() + "DownBarTop: " + DownBarTop);
              //                Print(Time[0].ToString() + "UpBarTop: " + UpBarTop);
              //                Print(Time[0].ToString() + "MaxRedBar: " + MaxRedBar);
              //                Print(Time[0].ToString() + "MaxGreenBar: " + MaxGreenBar);
                              
                              if(BarData.Count > 0)
                              {
              //                    Print(Time[0].ToString() + "test: ");
                                  var LastRow = BarData[BarData.Count - 1];
                                  
                                  if(DownBarTop < LastRow.RedBar)
                                  {
                                      MaxRedBar = DownBarTop;
              //                        Print(Time[0].ToString() + "MaxRedBar: " + MaxRedBar);
                                  }
                                  if(UpBarTop < LastRow.GreenBar)
                                  {
                                      MaxGreenBar = UpBarTop;
              //                        Print(Time[0].ToString() + "MaxGreenBar: " + MaxGreenBar);
                                  }
                              }
                              
                              BarData.Add((CurrentBar - 1, DownBarTop, UpBarTop));
                              
                              MaxValue = Math.Max(MaxRedBar,MaxGreenBar) + 10;
                              MinValue = 0;
                          }
                          
                                  
                          if(IsFirstTickOfBar == true)
                          {
                              Print(Time[0].ToString() + "UpCount: " + UpCount + "| DownCount: " + DownCount);
                              UpCount = 0;
                              DownCount = 0;
                              TotalCount = 0;
                          }
                      }
                      
                      protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
                      {
                          if(State == State.Historical)
                              return;
                          
                          if(UpCountColor == null)
                              UpCountColor = Brushes.Green;
                          
                          if(DownCountColor == null)
                              DownCountColor = Brushes.Red;
                          
                          SharpDX.Direct2D1.Brush UpCountColorDx = UpCountColor.ToDxBrush(RenderTarget, BarOpacity / 100f);//Sets color of the Up Bars
                          SharpDX.Direct2D1.SolidColorBrush UpCountBarOutline = new SharpDX.Direct2D1.SolidColorBrush(RenderTarget, SharpDX.Color.Green);//Sets color of Up Bar Outline
                          
                          SharpDX.Direct2D1.Brush DownCountColorDX = DownCountColor.ToDxBrush(RenderTarget, BarOpacity / 100f);//Sets color of the Down Bars
                          SharpDX.Direct2D1.SolidColorBrush DownCountBarOutline = new SharpDX.Direct2D1.SolidColorBrush(RenderTarget, SharpDX.Color.Red);//Sets color of Down Bar Outline
              //            Print(Time[0].ToString() + " BarCounter = " + BarCounter);
                          
                          foreach(var(BarIndex, RedBar, GreenBar) in BarData)
                          {
                              XLocation = chartControl.GetXByBarIndex(ChartBars,BarIndex);//Grabs x-location of current bar
                              UpBarTop = GreenBar;
                              DownBarTop = RedBar;
                              BarWidth = (float)chartControl.BarWidth;//Sets the bar width of the UpCount & DownCount Bars equal to half size of chart's bar width
                              YBaseline = chartScale.GetYByValue(0);
                              
                              SharpDX.RectangleF UpCountBar2 = new SharpDX.RectangleF(XLocation,UpBarTop,BarWidth,YBaseline-UpBarTop);
                              RenderTarget.FillRectangle(UpCountBar2, UpCountColorDx);
                              RenderTarget.DrawRectangle(UpCountBar2, UpCountBarOutline);
                              
                              SharpDX.RectangleF DownCountBar2 = new SharpDX.RectangleF(XLocation-BarWidth,DownBarTop,BarWidth,YBaseline-DownBarTop);
                              RenderTarget.FillRectangle(DownCountBar2, DownCountColorDX);
                              RenderTarget.DrawRectangle(DownCountBar2, DownCountBarOutline);
                          }
                                              
                          XLocation = chartControl.GetXByBarIndex(ChartBars,CurrentBar);//Grabs x-location of current bar
                          UpBarTop = chartScale.GetYByValue(UpCount);
              //            Print(Time[0].ToString() + "UpBarTop: " + UpBarTop);
                          DownBarTop = chartScale.GetYByValue(DownCount);
                          BarWidth = (float)chartControl.BarWidth;//Sets the bar width of the UpCount & DownCount Bars equal to half size of chart's bar width
                          YBaseline = chartScale.GetYByValue(0);
                          
                          SharpDX.RectangleF UpCountBar = new SharpDX.RectangleF(XLocation,UpBarTop,BarWidth,YBaseline-UpBarTop);
                          RenderTarget.FillRectangle(UpCountBar, UpCountColorDx);
                          RenderTarget.DrawRectangle(UpCountBar, UpCountBarOutline);
                          
                          SharpDX.RectangleF DownCountBar = new SharpDX.RectangleF(XLocation-BarWidth,DownBarTop,BarWidth,YBaseline-DownBarTop);
                          RenderTarget.FillRectangle(DownCountBar, DownCountColorDX);
                          RenderTarget.DrawRectangle(DownCountBar, DownCountBarOutline);
                                  
                          
                      }
                      
                      //The below is needed so that the Y-axis auto scales to highest value. Also need to add IsAutoScale = true in the State.SetDefaults
                      public override void OnCalculateMinMax()
                      {
                          if(State == State.Historical)
                              return;
              //            Print(Time[0].ToString() + "DownBarTop2: " + DownBarTop);
              //            Print(Time[0].ToString() + "UpBarTop2: " + UpBarTop);
                          MinValue = 0;
                          MaxValue = Math.Max(MaxRedBar,MaxGreenBar) + 10;
                          Print(Time[0].ToString() + "MaxValue: " + MaxValue);
                          
                      }
                          
                      #region Properties
                      [NinjaScriptProperty]
                      [Display(Name = "Set Bar Thickness", GroupName = "Indicator Parameters", Order = 3)]
                      public int BarThickness
                      { get; set; }
                      #endregion
                  }
              }
              
              #region NinjaScript generated code. Neither change nor remove.
              
              namespace NinjaTrader.NinjaScript.Indicators
              {
                  public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
                  {
                      private OrderSpeed[] cacheOrderSpeed;
                      public OrderSpeed OrderSpeed(int barThickness)
                      {
                          return OrderSpeed(Input, barThickness);
                      }
              
                      public OrderSpeed OrderSpeed(ISeries<double> input, int barThickness)
                      {
                          if (cacheOrderSpeed != null)
                              for (int idx = 0; idx < cacheOrderSpeed.Length; idx++)
                                  if (cacheOrderSpeed[idx] != null && cacheOrderSpeed[idx].BarThickness == barThickness && cacheOrderSpeed[idx].EqualsInput(input))
                                      return cacheOrderSpeed[idx];
                          return CacheIndicator<OrderSpeed>(new OrderSpeed(){ BarThickness = barThickness }, input, ref cacheOrderSpeed);
                      }
                  }
              }
              
              namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
              {
                  public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
                  {
                      public Indicators.OrderSpeed OrderSpeed(int barThickness)
                      {
                          return indicator.OrderSpeed(Input, barThickness);
                      }
              
                      public Indicators.OrderSpeed OrderSpeed(ISeries<double> input , int barThickness)
                      {
                          return indicator.OrderSpeed(input, barThickness);
                      }
                  }
              }
              
              namespace NinjaTrader.NinjaScript.Strategies
              {
                  public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
                  {
                      public Indicators.OrderSpeed OrderSpeed(int barThickness)
                      {
                          return indicator.OrderSpeed(Input, barThickness);
                      }
              
                      public Indicators.OrderSpeed OrderSpeed(ISeries<double> input , int barThickness)
                      {
                          return indicator.OrderSpeed(input, barThickness);
                      }
                  }
              }
              
              #endregion
              
              ​

              Comment


                #8
                Here's how AI fixed it... seems to be working:

                PHP Code:
                #region Using declarations
                using System;
                using System.Collections.Generic;
                using System.ComponentModel;
                using System.ComponentModel.DataAnnotations;
                using System.Linq;
                using System.Text;
                using System.Threading.Tasks;
                using System.Windows;
                using System.Windows.Input;
                using System.Windows.Media;
                using System.Xml.Serialization;
                using NinjaTrader.Cbi;
                using NinjaTrader.Gui;
                using NinjaTrader.Gui.Chart;
                using NinjaTrader.Gui.SuperDom;
                using NinjaTrader.Gui.Tools;
                using NinjaTrader.Data;
                using NinjaTrader.NinjaScript;
                using NinjaTrader.Core.FloatingPoint;
                using NinjaTrader.NinjaScript.DrawingTools;
                #endregion
                
                //This namespace holds Indicators in this folder and is required. Do not change it.
                namespace NinjaTrader.NinjaScript.Indicators
                {
                    public class OrderSpeed : Indicator
                    {
                        private int UpCount;
                        private int DownCount;
                        private int TotalCount;
                        private double currentAsk;
                        private double currentBid;
                        private int BarCounter;
                        
                        private System.Windows.Media.Brush    UpCountColor;
                        private System.Windows.Media.Brush    DownCountColor;
                        private int BarOpacity;
                        private float XLocation;
                        private float UpBarTop;
                        private float DownBarTop;
                        private float UpBarHeight;
                        private float DownBarHeight;
                        private float BarWidth;
                        private float YBaseline;
                        
                        private List<(int BarIndex, float RedBar, float GreenBar)> BarData = new List<(int BarIndex, float RedBar, float GreenBar)>();
                        private float MaxRedBar;
                        private float MaxGreenBar;
                        
                        protected override void OnStateChange()
                        {
                            if (State == State.SetDefaults)
                            {
                                Description                                    = @"Tracks and displays order speed with up/down counts.";
                                Name                                        = "OrderSpeed";
                                Calculate                                    = Calculate.OnEachTick;
                                IsOverlay                                    = false;
                                DisplayInDataBox                            = true;
                                DrawOnPricePanel                            = true;
                                DrawHorizontalGridLines                        = true;
                                DrawVerticalGridLines                        = true;
                                PaintPriceMarkers                            = true;
                                ScaleJustification                            = NinjaTrader.Gui.Chart.ScaleJustification.Right;
                                IsSuspendedWhileInactive                    = true;
                                IsAutoScale = true;
                                UpCount = 0;
                                DownCount = 0;
                                TotalCount = 0;
                                BarCounter = 0;
                                BarThickness = 8;
                                UpCountColor = Brushes.Green;
                                DownCountColor = Brushes.Red;
                                BarOpacity = 50;
                                
                                // Initialize max values
                                MaxRedBar = 0;
                                MaxGreenBar = 0;
                            }
                            else if (State == State.Configure)
                            {
                                // Additional configuration if needed
                            }
                        }
                
                        protected override void OnMarketData(MarketDataEventArgs marketDataUpdate)
                        {
                            if (marketDataUpdate.MarketDataType != MarketDataType.Last)
                                return;
                
                            if (marketDataUpdate.Price >= marketDataUpdate.Ask)
                            {
                                UpCount = UpCount + 1;
                            }
                            else if (marketDataUpdate.Price <= marketDataUpdate.Bid)
                            {
                                DownCount = DownCount + 1;
                            }
                            
                            TotalCount = UpCount + DownCount;
                            
                            // Update max values as data comes in
                            MaxRedBar = Math.Max(MaxRedBar, DownCount);
                            MaxGreenBar = Math.Max(MaxGreenBar, UpCount);
                            
                            // Ensure auto-scaling works properly
                            MaxValue = Math.Max(MaxRedBar, MaxGreenBar) + 10;
                        }
                
                        protected override void OnBarUpdate()
                        {
                            if(State == State.Historical)
                                return;
                            
                            if(CurrentBar < 1)
                                return;
                                        
                            if(IsFirstTickOfBar)
                            {
                                BarCounter++;
                                
                                // Store the current up/down counts for this bar
                                BarData.Add((CurrentBar - 1, DownCount, UpCount));
                                
                                // Log the counts at the start of a new bar
                                Print(Time[0].ToString() + " UpCount: " + UpCount + " | DownCount: " + DownCount);
                                
                                // Reset counts for the new bar
                                UpCount = 0;
                                DownCount = 0;
                                TotalCount = 0;
                            }
                        }
                        
                        protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
                        {
                            if(State == State.Historical)
                                return;
                            
                            if(UpCountColor == null)
                                UpCountColor = Brushes.Green;
                            
                            if(DownCountColor == null)
                                DownCountColor = Brushes.Red;
                            
                            // Create brushes for rendering
                            SharpDX.Direct2D1.Brush upCountColorDx = UpCountColor.ToDxBrush(RenderTarget, BarOpacity / 100f);
                            SharpDX.Direct2D1.SolidColorBrush upCountBarOutline = new SharpDX.Direct2D1.SolidColorBrush(RenderTarget, SharpDX.Color.Green);
                            
                            SharpDX.Direct2D1.Brush downCountColorDx = DownCountColor.ToDxBrush(RenderTarget, BarOpacity / 100f);
                            SharpDX.Direct2D1.SolidColorBrush downCountBarOutline = new SharpDX.Direct2D1.SolidColorBrush(RenderTarget, SharpDX.Color.Red);
                            
                            // Get baseline Y position for zero
                            YBaseline = chartScale.GetYByValue(0);
                            
                            // Render historical bars
                            foreach(var (barIndex, redBar, greenBar) in BarData)
                            {
                                XLocation = chartControl.GetXByBarIndex(ChartBars, barIndex);
                                BarWidth = (float)chartControl.BarWidth;
                                
                                UpBarTop = chartScale.GetYByValue(greenBar);
                                DownBarTop = chartScale.GetYByValue(redBar);
                                
                                // Render green (up) bar
                                SharpDX.RectangleF upCountBar = new SharpDX.RectangleF(XLocation, UpBarTop, BarWidth, YBaseline - UpBarTop);
                                RenderTarget.FillRectangle(upCountBar, upCountColorDx);
                                RenderTarget.DrawRectangle(upCountBar, upCountBarOutline);
                                
                                // Render red (down) bar
                                SharpDX.RectangleF downCountBar = new SharpDX.RectangleF(XLocation - BarWidth, DownBarTop, BarWidth, YBaseline - DownBarTop);
                                RenderTarget.FillRectangle(downCountBar, downCountColorDx);
                                RenderTarget.DrawRectangle(downCountBar, downCountBarOutline);
                            }
                            
                            // Render current bar
                            XLocation = chartControl.GetXByBarIndex(ChartBars, CurrentBar);
                            BarWidth = (float)chartControl.BarWidth;
                            
                            UpBarTop = chartScale.GetYByValue(UpCount);
                            DownBarTop = chartScale.GetYByValue(DownCount);
                            
                            // Render current green (up) bar
                            SharpDX.RectangleF currentUpCountBar = new SharpDX.RectangleF(XLocation, UpBarTop, BarWidth, YBaseline - UpBarTop);
                            RenderTarget.FillRectangle(currentUpCountBar, upCountColorDx);
                            RenderTarget.DrawRectangle(currentUpCountBar, upCountBarOutline);
                            
                            // Render current red (down) bar
                            SharpDX.RectangleF currentDownCountBar = new SharpDX.RectangleF(XLocation - BarWidth, DownBarTop, BarWidth, YBaseline - DownBarTop);
                            RenderTarget.FillRectangle(currentDownCountBar, downCountColorDx);
                            RenderTarget.DrawRectangle(currentDownCountBar, downCountBarOutline);
                            
                            // Clean up resources
                            upCountColorDx.Dispose();
                            upCountBarOutline.Dispose();
                            downCountColorDx.Dispose();
                            downCountBarOutline.Dispose();
                        }
                        
                        // The below is needed so that the Y-axis auto scales to highest value
                        public override void OnCalculateMinMax()
                        {
                            if(State == State.Historical)
                                return;
                            
                            MinValue = 0;
                            MaxValue = Math.Max(MaxRedBar, MaxGreenBar) + 10;
                        }
                            
                        #region Properties
                        [NinjaScriptProperty]
                        [Display(Name = "Set Bar Thickness", GroupName = "Indicator Parameters", Order = 3)]
                        public int BarThickness
                        { get; set; }
                        #endregion
                    }
                }
                
                #region NinjaScript generated code. Neither change nor remove.
                
                namespace NinjaTrader.NinjaScript.Indicators
                {
                    public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
                    {
                        private OrderSpeed[] cacheOrderSpeed;
                        public OrderSpeed OrderSpeed(int barThickness)
                        {
                            return OrderSpeed(Input, barThickness);
                        }
                
                        public OrderSpeed OrderSpeed(ISeries<double> input, int barThickness)
                        {
                            if (cacheOrderSpeed != null)
                                for (int idx = 0; idx < cacheOrderSpeed.Length; idx++)
                                    if (cacheOrderSpeed[idx] != null && cacheOrderSpeed[idx].BarThickness == barThickness && cacheOrderSpeed[idx].EqualsInput(input))
                                        return cacheOrderSpeed[idx];
                            return CacheIndicator<OrderSpeed>(new OrderSpeed(){ BarThickness = barThickness }, input, ref cacheOrderSpeed);
                        }
                    }
                }
                
                namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
                {
                    public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
                    {
                        public Indicators.OrderSpeed OrderSpeed(int barThickness)
                        {
                            return indicator.OrderSpeed(Input, barThickness);
                        }
                
                        public Indicators.OrderSpeed OrderSpeed(ISeries<double> input , int barThickness)
                        {
                            return indicator.OrderSpeed(input, barThickness);
                        }
                    }
                }
                
                namespace NinjaTrader.NinjaScript.Strategies
                {
                    public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
                    {
                        public Indicators.OrderSpeed OrderSpeed(int barThickness)
                        {
                            return indicator.OrderSpeed(Input, barThickness);
                        }
                
                        public Indicators.OrderSpeed OrderSpeed(ISeries<double> input , int barThickness)
                        {
                            return indicator.OrderSpeed(input, barThickness);
                        }
                    }
                }
                
                #endregion
                
                &#8203; 
                

                Last edited by MiCe1999; 04-07-2025, 02:14 PM.

                Comment


                  #9
                  ​MiCe1999,

                  Thanks for the response. Much appreciated. This fixed my issue but now I got a new issue. I noticed as new bars are generated and it forces the Y-axis to adjust but the previous historical bars don't adjust with the new Y-axis. See below pic. When this bar (circled in blue) was first generated, it correctly matched the 85 trades but once the next bars formed, it no longer matched the values on the Y-axis.

                  Click image for larger version

Name:	image.png
Views:	139
Size:	17.7 KB
ID:	1339927
                  Attached Files

                  Comment


                    #10
                    See if my current version works for you, it seems to be working fine: OrderSpeedOpacity.cs
                    Attached Files

                    Comment


                      #11
                      Chelsea,

                      Is there a way to have the y-axis auto adjust as you scroll left to match the highest y-value for whatever is being shown in on the chart (i.e. as the highest red/green bar scrolls off the screen the y-axis adjusts to the new highest value shown on the screen)? See below pics. The first pic shows the y-axis properly adjusted to the highest value however as I scroll left and these large red/bars fall off the screen, the bars look so small because the y-axis got adjust for the latest bar (see pic 2). The bar values are based on OnMarketData and they are drawn using OnRender. If this was a normal plot used on OnBarUpdate, it properly adjusts for only the values that are shown. It doesn't work the same for OnRender.

                      Click image for larger version

Name:	image.png
Views:	136
Size:	3.9 KB
ID:	1341950

                      Click image for larger version

Name:	image.png
Views:	117
Size:	3.6 KB
ID:	1341951

                      Thanks

                      Comment


                        #12
                        Originally posted by algospoke View Post
                        Chelsea,

                        Is there a way to have the y-axis auto adjust as you scroll left to match the highest y-value for whatever is being shown in on the chart (i.e. as the highest red/green bar scrolls off the screen the y-axis adjusts to the new highest value shown on the screen)? See below pics. The first pic shows the y-axis properly adjusted to the highest value however as I scroll left and these large red/bars fall off the screen, the bars look so small because the y-axis got adjust for the latest bar (see pic 2). The bar values are based on OnMarketData and they are drawn using OnRender. If this was a normal plot used on OnBarUpdate, it properly adjusts for only the values that are shown. It doesn't work the same for OnRender.

                        Click image for larger version

Name:	image.png
Views:	135
Size:	3.9 KB
ID:	1341950

                        Click image for larger version

Name:	image.png
Views:	116
Size:	3.6 KB
ID:	1341951

                        Thanks
                        Here's a version that scales, only renders visible area and caches brushes: OrderSpeed.cs

                        Comment


                          #13
                          Hello algospoke,

                          MiCe1999 may already have a solution for you.

                          My suggestion is to loop through the ChartBars.FromIndex to ChartBars.ToIndex and set the MaxValue and MinValue to the highest high and lowest low plot value.
                          Chelsea B.NinjaTrader Customer Service

                          Comment


                            #14
                            Originally posted by NinjaTrader_ChelseaB View Post
                            Hello algospoke,

                            MiCe1999 may already have a solution for you.

                            My suggestion is to loop through the ChartBars.FromIndex to ChartBars.ToIndex and set the MaxValue and MinValue to the highest high and lowest low plot value.
                            Thank you ChelseaB for pointing it out. I usually stay away from getting X via Chartbars.From/ToIndex because x coordinate 'jumps' with variable width bars, but since our drawings are tied to the bars is the simpler way and probably faster too.

                            algospoke, if you want to use CelseaB recommendation, replace GetVisibleBarRange:

                            PHP Code:
                            private (int firstVisibleBar, int lastVisibleBar) GetVisibleBarRange(ChartControl chartControl)
                            {
                                int firstVisibleBar = ChartBars.GetBarIdxByX(chartControl, 0);
                                int lastVisibleBar = ChartBars.GetBarIdxByX(chartControl, (int)chartControl.ActualWidth);
                                
                                // Ensure the values are valid
                                firstVisibleBar = Math.Max(0, firstVisibleBar);
                                lastVisibleBar = Math.Min(CurrentBar, lastVisibleBar);
                                
                                return (firstVisibleBar, lastVisibleBar);
                            }&#8203; 
                            
                            with:

                            PHP Code:
                            private (int firstVisibleBar, int lastVisibleBar) GetVisibleBarRange(ChartControl chartControl)
                            {
                                int firstVisibleBar = ChartBars.FromIndex;
                                int lastVisibleBar = ChartBars.ToIndex;
                                
                                // Ensure the values are valid
                                firstVisibleBar = Math.Max(0, firstVisibleBar);
                                lastVisibleBar = Math.Min(CurrentBar, lastVisibleBar);
                                
                                return (firstVisibleBar, lastVisibleBar);
                            }&#8203; 
                            

                            Comment

                            Latest Posts

                            Collapse

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