Announcement

Collapse

Looking for a User App or Add-On built by the NinjaTrader community?

Visit NinjaTrader EcoSystem and our free User App Share!

Have a question for the NinjaScript developer community? Open a new thread in our NinjaScript File Sharing Discussion Forum!
See more
See less

Partner 728x90

Collapse

Calling an Indicator with multiple series

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

    Calling an Indicator with multiple series

    I would like to call one indicator from another indicator. When I do this, I will need to pass multiple data series to the indicator I am calling.

    For example

    Code:
    	public class Indicator_A : Indicator
    	{
    		Series<double> Func_X;
    		Series<double> Func_Y;
    		Series<double> Func_Filtered;
    				
    		protected override void OnStateChange()
    		{
    			if (State == State.SetDefaults)
    			{
    				// Indicator Setup Stuff
    			}
    			else if (State == State.Configure)
    			{
    				Func_A = new Series<double>(this);
    				Func_B = new Series<double>(this);
    				Func_Filtered = new Series<double>(this);
    			}
    			else if (State == State.DataLoaded)
    			{
    			}
    		}
    
    		protected override void OnBarUpdate()
    		{
    			// Logic to define Func_X and Func_Y
    			// Simple Example
    			Func_X[0] = SMA( Open, 12 )[0];
    			Func_Y[0] = MACD( Close, 12, true, 26, 9 )[0];
    			
    			[B]Func_Filtered[0] = Indicator_B( Func_X, Func_Y, 12, 7 );[/B]
    			
    		}
    Ninjascript autogenerates code that includes the indicators constructors. These constructors allow one price series to be passed to the indicator. This series is referenced as “Input”.

    How do I create overloaded constructors for Indicator_B to allow me to pass two Series to the indicator?
    Last edited by JeffCO7; 11-18-2017, 11:52 PM.

    #2
    Hello JeffCO7,

    Thank you for your note.

    This would not be possible with an indicator as you would need to have both data series in the hosting script as well, please see warning at the following link,



    You could create a method in a static class for which you could see the following example done by Jim Dooms, for how you could build something like that, see example inside the addon folder.


    Please let us know if you need further assistance.
    Alan P.NinjaTrader Customer Service

    Comment


      #3
      Alan,

      Thanks for the thoughts.

      Is it possible to create a reusable class that is not static? I have not been able to find a good way to create good reusable code in Ninjatrader.

      What I want to accomplish is to create a "function" that can be called multiple times with different inputs from one or more indicators. I want to be able to past multiple time series to the methods.

      Please forgive me as my C# OO is not strong. Using a partial class means that the variables are static. Therefore if I call it with methods in the partial class with different inputs for from multiple indicators, the methods are using common variables.

      I would like to be able to create multiple instances of my reusable code. How can I do this in NinjaTrader and still pass multiple time series to that reusable code?

      Comment


        #4
        Hello JeffCO7,

        You could put the calculation logic in an indicator and reference that indicator from another indicator. I have a sample which a strategy references gap logic within an indicator.



        Otherwise I would suggest using a static class.

        What is the reason you do not wish to use a static class?

        I look forward to your reply.
        Alan P.NinjaTrader Customer Service

        Comment


          #5
          Alan,

          If I use a static class, then all the class variables are static. If I instantiate the class from two different places, they end up sharing the same variable values. Therefore, I cannot create unique instances of the objects I instantiate from the static class.

          I need to be able to work with unique instances of the code and variables I write.

          I also described below that I need to be able to pass multiple data series to the method in the class. I cannot pass multiple data series to an indicator I call from another indicator or strategy.
          Last edited by JeffCO7; 12-04-2017, 10:36 AM.

          Comment


            #6
            jeff.reg#flachman.net

            Alan,

            So I have put together a simple indicator example to figure out how to define reusable code. As I mentioned, I am new to c#. I have an indicator called "MyReuseIndicator". It also uses an Addon called "MyReuseExampleAddon".

            In its current state it defines a subclass within an indicator, and reusable methods in an addon and a resuable subclass in an addon.

            I want to be able to define a data series in all these different instances.

            I have started with on data series in the indicator subclass (testClass) I have commented out lines that try to define a series in one of these classes. Lines: (105, 111,119 & 122)

            Can you take a look and help me fix this so that I can complete my examples.

            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.Research
            {
            	public class MyReuseIndicator : Indicator
            	{
            		#region Global Variables
            		testClass Test1;
            		testClass Test2;
            		double TestVal1;
            		double TestVal2;
            		
            		NinjaTrader.NinjaScript.AddOns.MyReuseExampleAddon.test2Class Test3;
            		NinjaTrader.NinjaScript.AddOns.MyReuseExampleAddon.test2Class Test4;
            		double TestVal3;
            		double TestVal4;
            		
            		NinjaTrader.NinjaScript.AddOns.MyReuseExampleAddon Test5;
            		NinjaTrader.NinjaScript.AddOns.MyReuseExampleAddon Test6;
            		double TestVal5;
            		double TestVal6;
            		
            		#endregion
            		protected override void OnStateChange()
            		{
            			if (State == State.SetDefaults)
            			{
            				Description									= @"Indicator with Reusable code examples";
            				Name										= "MyReuseIndicator";
            				Calculate									= Calculate.OnBarClose;
            				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;
            				IIntVal					= 1;
            				IDoubleVal					= 1;
            				AddPlot(Brushes.Green, "Result1");
            			}
            			else if (State == State.Configure)
            			{
            				Test1 = new testClass( 1 );
            				Test2 = new testClass( 2 );
            				
            				Test3 = new NinjaTrader.NinjaScript.AddOns.MyReuseExampleAddon.test2Class( 3 );
            				Test4 = new NinjaTrader.NinjaScript.AddOns.MyReuseExampleAddon.test2Class( 4 );
            				
            				Test5 = new NinjaTrader.NinjaScript.AddOns.MyReuseExampleAddon();
            				Test6 = new NinjaTrader.NinjaScript.AddOns.MyReuseExampleAddon();
            			}
            		}
            
            		protected override void OnBarUpdate()
            		{
            			//Add your custom indicator logic here.
            			if ( CurrentBar > 0 ) {
            			
            				Print( string.Format("\nMain >>>> BarNum {0}", CurrentBar ) );
            				TestVal1 = Test1.testMethod( High, "High" );
            				TestVal2 = Test2.testMethod( Low, "Low" );
            				
            				Print( string.Format( "Main >>>> High:{0}  Low:{1}  TestVal1:{2}  TestVal2:{3}", High[0], Low[0], TestVal1, TestVal2 ) );
            				
            				TestVal3 = Test3.test2Method( High, "High" );
            				TestVal4 = Test4.test2Method( Low, "Low" );
            				
            				Print( string.Format( "Main >>>> High:{0}  Low:{1}  TestVal3:{2}  TestVal4:{3}", High[0], Low[0], TestVal3, TestVal4 ) );
            				
            				TestVal5 = Test5.test3Method( High, "High" );
            				TestVal6 = Test6.test3Method( Low, "Low" );
            				Print( string.Format( "Main >>>> High:{0}  Low:{1}  TestVal5:{2}  TestVal6:{3}", High[0], Low[0], TestVal5, TestVal6 ) );
            				
            			}
            		}
            		
            		public class testClass
            		{
            //	Fix0	Series<double> PriceSeries1 = new Series<double>(this, MaximumBarsLookBack.Infinite);
            			double testVar;
            			double testVar1;
            			public testClass( double inVar)
            			{
            				
            //	Fix1		PriceSeries1[0] = 0;
            				testVar = 0;
            				testVar1 = inVar;
            			}
            			
            			public double testMethod( ISeries<double> inputSeries,
            										string iType)
            			{
            //	Fix2		PriceSeries1[0] = inputSeries[0];
            				testVar = inputSeries[0];
            				NinjaTrader.Code.Output.Process( string.Format( "testMethod >>>> Init:{0}  Type:{1}  In[1]:{2}, In[0]:{3}", testVar1, iType, inputSeries[1], testVar), PrintTo.OutputTab1 );
            //  Fix3		NinjaTrader.Code.Output.Process( string.Format( "testMethod >>>> Init:{0}  Type:{1}  In[1]:{2}, In[0]:{3}  PriceSeries[1]{4}", testVar1, iType, inputSeries[1], testVar, PriceSeries1[1]), PrintTo.OutputTab1 );
            				return testVar;
            				
            			}
            		}
            
            		#region Properties
            		[NinjaScriptProperty]
            		[Range(1, int.MaxValue)]
            		[Display(Name="IIntVal", Order=1, GroupName="Parameters")]
            		public int IIntVal
            		{ get; set; }
            
            		[NinjaScriptProperty]
            		[Range(1, double.MaxValue)]
            		[Display(Name="IDoubleVal", Order=2, GroupName="Parameters")]
            		public double IDoubleVal
            		{ get; set; }
            
            		[Browsable(false)]
            		[XmlIgnore]
            		public Series<double> Result1
            		{
            			get { return Values[0]; }
            		}
            		#endregion
            
            	}
            }
            
            #region NinjaScript generated code. Neither change nor remove.
            
            namespace NinjaTrader.NinjaScript.Indicators
            {
            	public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
            	{
            		private Research.MyReuseIndicator[] cacheMyReuseIndicator;
            		public Research.MyReuseIndicator MyReuseIndicator(int iIntVal, double iDoubleVal)
            		{
            			return MyReuseIndicator(Input, iIntVal, iDoubleVal);
            		}
            
            		public Research.MyReuseIndicator MyReuseIndicator(ISeries<double> input, int iIntVal, double iDoubleVal)
            		{
            			if (cacheMyReuseIndicator != null)
            				for (int idx = 0; idx < cacheMyReuseIndicator.Length; idx++)
            					if (cacheMyReuseIndicator[idx] != null && cacheMyReuseIndicator[idx].IIntVal == iIntVal && cacheMyReuseIndicator[idx].IDoubleVal == iDoubleVal && cacheMyReuseIndicator[idx].EqualsInput(input))
            						return cacheMyReuseIndicator[idx];
            			return CacheIndicator<Research.MyReuseIndicator>(new Research.MyReuseIndicator(){ IIntVal = iIntVal, IDoubleVal = iDoubleVal }, input, ref cacheMyReuseIndicator);
            		}
            	}
            }
            
            namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
            {
            	public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
            	{
            		public Indicators.Research.MyReuseIndicator MyReuseIndicator(int iIntVal, double iDoubleVal)
            		{
            			return indicator.MyReuseIndicator(Input, iIntVal, iDoubleVal);
            		}
            
            		public Indicators.Research.MyReuseIndicator MyReuseIndicator(ISeries<double> input , int iIntVal, double iDoubleVal)
            		{
            			return indicator.MyReuseIndicator(input, iIntVal, iDoubleVal);
            		}
            	}
            }
            
            namespace NinjaTrader.NinjaScript.Strategies
            {
            	public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
            	{
            		public Indicators.Research.MyReuseIndicator MyReuseIndicator(int iIntVal, double iDoubleVal)
            		{
            			return indicator.MyReuseIndicator(Input, iIntVal, iDoubleVal);
            		}
            
            		public Indicators.Research.MyReuseIndicator MyReuseIndicator(ISeries<double> input , int iIntVal, double iDoubleVal)
            		{
            			return indicator.MyReuseIndicator(input, iIntVal, iDoubleVal);
            		}
            	}
            }
            
            #endregion
            MyReuseIndicator
            Also MyReuseExampleAddon
            Last edited by JeffCO7; 12-05-2017, 08:09 AM. Reason: update

            Comment


              #7
              Alan,

              The code for MyReuseExampleAddon would not fit in the previous post. Here it is.

              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.Gui.Tools;
              #endregion
              
              //This namespace holds Add ons in this folder and is required. Do not change it. 
              namespace NinjaTrader.NinjaScript.AddOns
              {
              	public class MyReuseExampleAddon : NinjaTrader.NinjaScript.AddOnBase
              	{
              		double test3Var;
              		double test3Var1;
              		protected override void OnStateChange()
              		{
              			if (State == State.SetDefaults)
              			{
              				Description									= @"Example of reusable code in an Addon";
              				Name										= "MyReuseExampleAddon";
              				IIntVal_1			= 1;
              				IdoubleVal_1		= 1;
              			}
              			else if (State == State.Configure)
              			{
              				test3Var1 = IIntVal_1;				// This line does not seem to be called in my code.
              			}
              		}
              		
              		public double test3Method( ISeries<double> inputSeries,
              										string iType)
              			{
              				test3Var1 = test3Var;
              				test3Var = inputSeries[0];
              				NinjaTrader.Code.Output.Process( string.Format( "test3Method >>>> Init:{0}  Type:{1}  In[1]:{2}, In[0]:{3}", test3Var1, iType, inputSeries[1], test3Var), PrintTo.OutputTab1 );
              				return test3Var;
              				
              			}
              		
              		public class test2Class
              		{
              			double test2Var;
              			double test2Var1;
              			public test2Class( double inVar)
              			{
              				test2Var = 0;
              				test2Var1 = inVar;
              			}
              			
              			public double test2Method( ISeries<double> inputSeries,
              										string iType)
              			{
              				test2Var = inputSeries[0];
              				NinjaTrader.Code.Output.Process( string.Format( "test2Method >>>> Init:{0}  Type:{1}  In[1]:{2}, In[0]:{3}", test2Var1, iType, inputSeries[1], test2Var), PrintTo.OutputTab1 );
              				return test2Var;
              				
              			}
              		}
              		
              
              		#region Properties
              		[NinjaScriptProperty]
              		[Range(1, int.MaxValue)]
              		[Display(Name="IIntVal_1", Order=1, GroupName="Parameters")]
              		public int IIntVal_1
              		{ get; set; }
              
              		[NinjaScriptProperty]
              		[Range(1, double.MaxValue)]
              		[Display(Name="IdoubleVal_1", Order=2, GroupName="Parameters")]
              		public double IdoubleVal_1
              		{ get; set; }
              		#endregion
              
              	}
              }
              Below is the attached code file for both.
              Attached Files
              Last edited by JeffCO7; 12-05-2017, 08:17 AM.

              Comment


                #8
                Hello JeffCO7,

                Unfortunately this is going to fall outside the scope of what our NinjaScript desk supports. If you'd like, I can have someone reach out with a list of third parties that would be interested in working with you on your code.

                Please let us know if you need further assistance.
                Alan P.NinjaTrader Customer Service

                Comment


                  #9
                  Alan,

                  This is a basic requirement for a development environment for a trading system. I have developed over 500 indicators and variants on Tradestation. I am trying to migrate my top 5 here in a greatly simplified form. I have no intention of having someone else develop my systems.

                  It has been very frustrating because it is difficult to segment reusable code in NinjaTrader and there are great limitation on using an indicator as the main method for reusable code. It is important to be able to create reusable code. That code must maintain its own state, have its own series variables and must accept multiple series variables as input.

                  I have seen a number of other people trying to figure this out in NinjaTrader in different ways. In none of the cases have I seen a good answer to this. Some have found a workaround for their current need.

                  I am assuming your answer means it is not possible in NinjaTrader using the NinjaScript Editor. I am going to assume this is because there is no answer without the use of Visual Studio.

                  On the tradestation forum I rarely or never see an answer such as this "falls outside the scope of what our NinjaScript desk supports". This is a huge difference in customer support. So now i will rethink the last 4 months of my migration efforts and chose a path forward. Sad day.

                  Comment


                    #10
                    Hello JeffCO7,

                    Give me some time and I will put together a sample of how you can accomplish this via an addon.

                    Thank you for your patience.
                    Alan P.NinjaTrader Customer Service

                    Comment


                      #11
                      Alan,

                      Thank you for the help. I am excited to finally get past this hurdle.
                      Last edited by JeffCO7; 12-05-2017, 07:21 PM.

                      Comment


                        #12
                        Hello JeffCO7,

                        I have attached a sample indicator and addon.

                        The indicator passes the ISeries, Close, to a static method in the Addon, which returns an array. This array is then passed to another method in the addon which computes a SMA and returns it to the indicator as an array. This is done a second time for a second sma array. These two SMA arrays are then passed to a different static method in the Addon, which adds them together and returns the combined series as an array. This array is then plotted under the chart.

                        You are unable to access ninjatrader indicator methods from an addon so this requires you to write your own, which I have done with the SMA.

                        I used the same period SMA for simplicity.

                        Please let us know if you need further assistance.
                        Attached Files
                        Alan P.NinjaTrader Customer Service

                        Comment


                          #13
                          Alan,

                          Thank you for all the hard work. The "staticAddonMethodsDoubleSMAIndicator" solution copies the entire price series to an array each bar. This will become computationally expensive for large bar counts. Do you see this as a good approach for repeated use?

                          It looks like there is no way to define a Series as a local variable in a subclass or addon. -- is this True or False???

                          The attached code is the best I have been able to create. I have to create a Series instance in the main indicator. But then I can pass it to a subclass or addon via reference. This will work. However it would have been cleaner if I could define a new Series in a subclass or addon.

                          In the attached example, I have implemented three different methods for creating a block of reusable code.
                          1. Subclass in the main indicator
                          2. an Addon
                          3. a Subclass in an Addon

                          Note that I defined inputs for the Addon. These are unneeded for the example.

                          Let me know what you think about this approach as a reasonable design pattern.
                          Attached Files
                          Last edited by JeffCO7; 12-07-2017, 01:42 AM.

                          Comment


                            #14
                            Hello JeffCO7,

                            No, you cannot create a series in a subclass or add-on.

                            It would be out of the scope of our support desk to advice on unsupported code.

                            Please let us know if you need further assistance.
                            Alan P.NinjaTrader Customer Service

                            Comment


                              #15
                              Thank you.

                              Comment

                              Latest Posts

                              Collapse

                              Topics Statistics Last Post
                              Started by jaybedreamin, Today, 05:56 PM
                              0 responses
                              2 views
                              0 likes
                              Last Post jaybedreamin  
                              Started by DJ888, 04-16-2024, 06:09 PM
                              6 responses
                              18 views
                              0 likes
                              Last Post DJ888
                              by DJ888
                               
                              Started by Jon17, Today, 04:33 PM
                              0 responses
                              1 view
                              0 likes
                              Last Post Jon17
                              by Jon17
                               
                              Started by Javierw.ok, Today, 04:12 PM
                              0 responses
                              6 views
                              0 likes
                              Last Post Javierw.ok  
                              Started by timmbbo, Today, 08:59 AM
                              2 responses
                              10 views
                              0 likes
                              Last Post bltdavid  
                              Working...
                              X