Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

AddPlot should return a Plot object and make the values and brushes available

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

    AddPlot should return a Plot object and make the values and brushes available

    Inheritance and composition notwithstanding, it is quite common, especially for beginners, to build an indicator by taking one or more existing indicators they want to use as a basis and copying over the corresponding sections of code each state in OnStateChange, OnBarUpdate, the plots, and the inputs, and then adjusting as needed to make them work together.

    Consider this minimalist example of an SMA indicator that colors the plot green and red for up and down slope. This isn't intended to show perfect code form or anything like that - just to demonstrate the tasks required to merge some indicators together.

    Code:
    namespace NinjaTrader.NinjaScript.Indicators
    {
        public class ExampleSMA : Indicator
        {
            protected override void OnStateChange()
            {
                if (State == State.SetDefaults)
                {
                    Name                                        = "ExampleSMA";
                    IsOverlay                                    = true;
    
                    SMALength                    = 10;
    
                    AddPlot(Brushes.White, "SMAPlot");
                }
            }
    
            protected override void OnBarUpdate()
            {
                SMAPlotValues[0] = SMA(SMALength)[0];
    
                if (CurrentBar > 0) PlotBrushes[0][0] = (SMAPlotValues[0] > SMAPlotValues[1]) ? Brushes.Green : (SMAPlotValues[0] < SMAPlotValues[1]) ? Brushes.Red : PlotBrushes[0][1];
            }
    
            #region Properties
            [NinjaScriptProperty]
            [Range(1, int.MaxValue)]
            [Display(Name="SMALength", Order=1, GroupName="Parameters")]
            public int SMALength
            { get; set; }
    
            [Browsable(false)]
            [XmlIgnore]
            public Series<double> SMAPlotValues
            {
                get { return Values[0]; }
            }
            #endregion
    
        }
    }​
    Here's another one, the same but an EMA and using the colors cyan and magenta.

    Code:
    namespace NinjaTrader.NinjaScript.Indicators
    {
        public class ExampleEMA : Indicator
        {
            protected override void OnStateChange()
            {
                if (State == State.SetDefaults)
                {
                    Name                                        = "ExampleEMA";
                    IsOverlay                                    = true;
    
                    EMALength                    = 10;
    
                    AddPlot(Brushes.White, "EMAPlot");
                }
            }
    
            protected override void OnBarUpdate()
            {
                EMAPlotValues[0] = EMA(EMALength)[0];
    
                if (CurrentBar > 0) PlotBrushes[0][0] = (EMAPlotValues[0] > EMAPlotValues[1]) ? Brushes.Cyan : (EMAPlotValues[0] < EMAPlotValues[1]) ? Brushes.Magenta : PlotBrushes[0][1];
            }
    
            #region Properties
            [NinjaScriptProperty]
            [Range(1, int.MaxValue)]
            [Display(Name="EMALength", Order=1, GroupName="Parameters")]
            public int EMALength
            { get; set; }
    
            [Browsable(false)]
            [XmlIgnore]
            public Series<double> EMAPlotValues
            {
                get { return Values[0]; }
            }
            #endregion
    
        }
    }​
    Suppose now I want to merge these two together to make a super-duper SMA and EMA all in one indicator.

    I would end up with something like this, merging all of the sections together

    Code:
    namespace NinjaTrader.NinjaScript.Indicators
    {
        public class ExampleSMAandEMA : Indicator
        {
            protected override void OnStateChange()
            {
                if (State == State.SetDefaults)
                {
                    Name                                        = "ExampleSMAandEMA";
                    IsOverlay                                    = true;
    
                    SMALength                    = 10;
    ​                EMALength                    = 10;
    
                    AddPlot(Brushes.White, "SMAPlot");
    ​                AddPlot(Brushes.White, "EMAPlot");
                }
            }
    
            protected override void OnBarUpdate()
            {
                SMAPlotValues[0] = SMA(SMALength)[0];
    
                if (CurrentBar > 0) PlotBrushes[0][0] = (SMAPlotValues[0] > SMAPlotValues[1]) ? Brushes.Green : (SMAPlotValues[0] < SMAPlotValues[1]) ? Brushes.Red : PlotBrushes[0][1];
    ​
                EMAPlotValues[0] = EMA(EMALength)[0];
    
                if (CurrentBar > 0) PlotBrushes[0][0] = (EMAPlotValues[0] > EMAPlotValues[1]) ? Brushes.Cyan : (EMAPlotValues[0] < EMAPlotValues[1]) ? Brushes.Magenta : PlotBrushes[0][1];
            }
    
            #region Properties
            [NinjaScriptProperty]
            [Range(1, int.MaxValue)]
            [Display(Name="SMALength", Order=1, GroupName="Parameters")]
            public int SMALength
            { get; set; }
    
            [NinjaScriptProperty]
            [Range(1, int.MaxValue)]
            [Display(Name="EMALength", Order=1, GroupName="Parameters")]
            public int EMALength
            { get; set; }
    
            [Browsable(false)]
            [XmlIgnore]
            public Series<double> SMAPlotValues
            {
                get { return Values[0]; }
            }
    ​
    ​        [Browsable(false)]
            [XmlIgnore]
            public Series<double> EMAPlotValues
            {
                get { return Values[0]; }
            }
            #endregion
    
        }
    }​​
    Now, anyone who's spent some time with NinjaScript knows what comes next - you probably saw this coming a mile away. There named properties for the two plot values are both for index zero, and additionally, the PlotBrushes[] statements are both for index zero.

    But, why should I have to remember that it's index zero in the first place?

    What if:
    (1) The AddPlot statement returned a Plot object,
    (2) Plot objects had a .Values property that returned the right Values[] index,
    and (3) Plot objects had a .Brushes property that returned the right PlotBrushes[] index?

    Then, we would could have merged them and had this instead:

    Code:
    namespace NinjaTrader.NinjaScript.Indicators
    {
        public class ExampleSMAandEMA : Indicator
        {
            Plot SMAPlot = null;
    ​        Plot EMAPlot = null;
    
            protected override void OnStateChange()
            {
                if (State == State.SetDefaults)
                {
                    Name                                        = "ExampleSMAandEMA";
                    IsOverlay                                    = true;
    
                    SMALength                    = 10;
    ​                EMALength                    = 10;
    
                    SMAPlot = AddPlot(Brushes.White, "SMAPlot");
    ​                EMAPlot = AddPlot(Brushes.White, "EMAPlot");
                }
            }
    
            protected override void OnBarUpdate()
            {
                SMAPlot.Values[0] = SMA(SMALength)[0];
    
                if (CurrentBar > 0) SMAPlot.Brushes[0] = (SMAPlot.Values[0] > SMAPlot.Values[1]) ? Brushes.Green : (SMAPlot.Values[0] < SMAPlot.Values[1]) ? Brushes.Red : SMAPlot.Brushes[1];
    ​
                EMAPlot.Values[0] = EMA(EMALength)[0];
    
                if (CurrentBar > 0) EMAPlot.Brushes[0] = (EMAPlot.Values[0] > EMAPlot.Values[1]) ? Brushes.Cyan : (EMAPlot.Values[0] < EMAPlot.Values[1]) ? Brushes.Magenta : EMAPlot,Brushes[1];
            }
    
            #region Properties
            [NinjaScriptProperty]
            [Range(1, int.MaxValue)]
            [Display(Name="SMALength", Order=1, GroupName="Parameters")]
            public int SMALength
            { get; set; }
    
            [NinjaScriptProperty]
            [Range(1, int.MaxValue)]
            [Display(Name="EMALength", Order=1, GroupName="Parameters")]
            public int EMALength
            { get; set; }
    
            // note we don't really need these anymore, unless we want to access them externally from another indicator or strategy
            [Browsable(false)]
            [XmlIgnore]
            public Series<double> SMAPlotValues
            {
                get { return SMAPlot.Values; }
            }
    ​
    ​        [Browsable(false)]
            [XmlIgnore]
            public Series<double> EMAPlotValues
            {
                get { return EMAPlot.Values; }
            }
            #endregion
    
        }
    }​​


    What's better about this?

    Note that I don't have to know or care what index any of the plots are anymore. I can copy/paste all the code sections from the corresponding indicators straight in, and unless they use the same variable names or something like that, it all just works regardless of what plot number anything ended up as. All I need is to save the Plot object from AddPlot and I can get straight to the right Values[] index as well as the right PlotBrushes[] index by using MyPlot.Values[barsback] or MyPlot.Brushes[barsback].

    This would not break anything at all - it's completely backward compatible yet it makes it a lot easier for those new to NinjaTrader to intuitively access the plots by their names for both the value for each bar and the brush color for each bar.

    This suggestion replaces https://forum.ninjatrader.com/forum/...ts-plotbrushes with a more general and comprehensive solution.
    Bruce DeVault
    QuantKey Trading Vendor Services
    NinjaTrader Ecosystem Vendor - QuantKey

    #2
    Hello QuantKey_Bruce,

    I will put in a feature request for this, if there is already one your vote will be added.
    JesseNinjaTrader Customer Service

    Comment


      #3
      Vote "Y" for me.
      eDanny
      NinjaTrader Ecosystem Vendor - Integrity Traders

      Comment

      Latest Posts

      Collapse

      Topics Statistics Last Post
      Started by ghoul, Today, 06:02 PM
      0 responses
      7 views
      0 likes
      Last Post ghoul
      by ghoul
       
      Started by Barry Milan, Yesterday, 10:35 PM
      6 responses
      19 views
      0 likes
      Last Post Barry Milan  
      Started by DanielSanMartin, Yesterday, 02:37 PM
      2 responses
      13 views
      0 likes
      Last Post DanielSanMartin  
      Started by DJ888, 04-16-2024, 06:09 PM
      4 responses
      13 views
      0 likes
      Last Post DJ888
      by DJ888
       
      Started by terofs, Today, 04:18 PM
      0 responses
      12 views
      0 likes
      Last Post terofs
      by terofs
       
      Working...
      X