Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

Indicator's implementation of ISeries is confusing

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

    Indicator's implementation of ISeries is confusing

    The Indicator class implements ISeries<double> . This is convenient as it allows things like

    SMA(20)[30] - get the SMA(20) 30 bars ago.

    Consider the following code.

    Code:
    namespace NinjaTrader.NinjaScript.Indicators
    {
        public class IndicatorISeriesTest : Indicator
        {
            protected override void OnStateChange()
            {
                switch(State)
                {
                    case State.SetDefaults:
                        Description                                 = @"Testing the Indicators ISeries implementation";
                        Name                                        = "IndicatorISeriesTest";
                        Calculate                                   = Calculate.OnBarClose;
                        IsOverlay                                   = false;
                        DisplayInDataBox                            = true;
                        DrawOnPricePanel                            = true;
                        DrawHorizontalGridLines                     = true;
                        DrawVerticalGridLines                       = true;
                        PaintPriceMarkers                           = true;
                        ScaleJustification                          = 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;
    
                        BarsRequiredToPlot = 20;
                        break;
                    case State.DataLoaded:
                        indicator = Range();    //Indicator picked here doesn't matter)
                        break;
                }
            }
    
    
            Range indicator;
    
            protected override void OnBarUpdate()
            {
                Print(string.Format("indicator[0] = {0}, indicator.GetValueAt(CurrentBar) = {1}, indicator.Value.GetValueAt(CurrentBar) = {2}, indicator.Close.GetValueAt(CurrentBar) = {3}",
                    indicator[0],
                    indicator.GetValueAt(CurrentBar),
                    indicator.Value.GetValueAt(CurrentBar),
                    indicator.Close.GetValueAt(CurrentBar)));
    
                // indicator[0] == indicator.Value[0] but
                // indicator.GetValueAt(CurrentBar) != indicator.Value.GetValueAt(CurrentBar)
                // indicator.GetValueAt(CurrentBar) == indicator.Close.GetValueAt(CurrentBar)
            }
        }
    }

    There is definitely an inconsistency in terms of how an indicator implements ISeries<double>

    It returns indicator.Value for the [] operator but it returns indcator.Close when you call GetValueAt.

    Why the inconsistency?

    #2
    Hello,

    This is expected and is also documented in the GetValueAt documentation.

    https://ninjatrader.com/support/help...getvalueat.htm

    Tip: If called directly from the instance of the NinjaScript object, the value which is returned corresponds to the input series the object is running. (e.g., Close, High, Low, SMA, etc.). If you're attempting to obtain another indicator value, you will need to pull this from the calculated indicator Value or Plot:
    If you need a plot value you would just need to be specific and access it through the Values collection directly. BarsAgo is not the same system and has a convenience method to access the first plot from the indicator instance, for indicators with multiple plots you would need to be specific just like how GetValueAt works and use the Values collection to specify what data you wanted.

    Please let me know if I may be of further assistance.
    JesseNinjaTrader Customer Service

    Comment


      #3
      Ah. It would have been helpful if specific documentation for this was also in Indicator's documentation.

      This complicates things if one wants to use GetValueAt on the "Input" for an indicator. If the "Input" series is another indicator then

      Input[barsAgo] != Input.GetValueAt(CurrentBar - barsAgo).

      To create an SMA of the Range indicator which version is correct

      SMA(Range(), 20)
      or
      SMA(Range().Value, 20)

      If this is done via the UI which of the above is called?

      Comment


        #4
        Hello ntbone,

        From the UI or a chart your first sample would be what is used. Range has a single plot and the UI would reference the primary plot value in that use case.
        If you select something with Multiple plots in the UI it will ask you to be specific and select which plot you want from the drop down it displays.

        Please let me know if I may be of further assistance.
        JesseNinjaTrader Customer Service

        Comment


          #5
          If SMA(Range(), 20) is used instead of SMA(Range().Value, 20) and the indicator uses Input.GetValueAt instead of Input[barsAgo] it will not get the correct value.

          I am still confused as to why the indicator's GetValueAt returns the Close instead of the Value.GetValueAt. Quite often when I am coding indicators I use GetValueAt as dealing it is easier to work with incrementing bar indices instead of bars ago. There's no clear way to get equivalent functionality of Input.GetValueAt.

          Comment


            #6
            Hello ntbone,

            If SMA(Range(), 20) is used instead of SMA(Range().Value, 20) and the indicator uses Input.GetValueAt instead of Input[barsAgo] it will not get the correct value.
            Are you speaking of a hypothetical situation or are you seeing something specific? If you have a specific situation or sample please provide that.


            I am still confused as to why the indicator's GetValueAt returns the Close instead of the Value.GetValueAt.
            Because that is how it was developed and how it is documented for that specific use..

            GetValueAt is documented that it produces the input series value used directly on the indicator in the GetValueAt documentation so there should be no confusion when using that.

            Tip: If called directly from the instance of the NinjaScript object, the value which is returned corresponds to the input series the object is running. (e.g., Close, High, Low, SMA, etc.). If you're attempting to obtain another indicator value, you will need to pull this from the calculated indicator Value or Plot:
            Quite often when I am coding indicators I use GetValueAt as dealing it is easier to work with incrementing bar indices instead of bars ago. There's no clear way to get equivalent functionality of Input.GetValueAt.
            You just need to be specific on the data you want to GetValueA instead of trying to use the indicator instance for that purpose. The BarsAgo that defaults to the only plot for single plot indicators is not able to be replicated with GetValueAt on the indicator instance, you would need to be more specific when using GetValueAt. .

            GetValueAt is generally used specifically with Rendering where you are dealing with specific visible bar indexes and would not usually be used for accessing data in other areas where BarsAgo can be used instead. For example OnBarUpdate would use BarsAgo, OnRender would use GetValueAt. If you choose to use GetValueAt then you would just need to make sure you are being specific with your code and using the correct object for the data you want to access.


            Please let me know if I may be of further assistance.

            JesseNinjaTrader Customer Service

            Comment


              #7
              Originally posted by NinjaTrader_Jesse View Post
              Hello ntbone,
              Are you speaking of a hypothetical situation or are you seeing something specific? If you have a specific situation or sample please provide that.
              My code above demonstrates this problem.


              Originally posted by NinjaTrader_Jesse View Post
              Hello ntbone,
              Because that is how it was developed and how it is documented for that specific use..
              I am glad that it is documented but since Indicator implements ISeries<double> it would make more sense for it to implement it consistently. To have it implement ISeries<double> and return one series for most of its methods, and a different series for GetValueAt goes against the idea of the interface ISeries<T>. ISeries<T> represents one series and thus all its methods would pertain to that single series. If one writes code for ISeries<T> one expects all the methods from that implementation to be consistent. The implementation of ISeries by indicator makes the use of the interface unreliable. This will lead to my next point.


              While barAgo is useful for accessing data, quite often I use GetValueAt with explicit bar indices becasu
              1. Its much easier to debug. barsAgo changes as each bar is processed but the index of the bar (as returned by CurrentBar) stays the same for the life of the indicator. If the code is dealing with bar 37, its easy enough to inspect bar 37.
              2. Storing references to bars for later use. Tracking a specific bar in an indicator for later use (by the same indicator) can be quite useful at times so storing the absolute index allows for easy query later.
              3. The information collected in OnBarUpdate is being used outside of OnBarUpdate. Per the documentation

                Note: In most cases, you will access the historical price series using a core event handler such as OnBarUpdate. For more advance developers, you may find situations where you wish to access historical price series outside of the core event methods, such as your own custom mouse click. In these advanced scenarios, you may run into situations where the barsAgo pointer is not in sync with the current bar, and may result in errors when trying to obtain this information. In those cases, please use the Bars.Get...() methods with the absolute bar index (e.g., Bars.GetClose(), Bars.GetTime(), etc.)
              Regardless of the reasons for looking up a value with GetValueAt vs using barsAgo, if I have an indicator and its Input series is another Indicator (as defined through the UI) and I want to use GetValueAt(barIndex), I have to determine what type of implementation Input is.

              If Input is a Indicator, then I have to cast Input to Indicator, determine which plot was connected up to the Input, and then call Indicator.Values[index].GetValueAt(X).

              That eliminates the usefulness of a interface because now it requires specific knowledge of an implementation in order to obtain the correct and desired values for the various functions.

              I have yet to learn, from a design perspective, why Indicator would implement ISeries<double> and provide different data for the methods making the implementation of ISeries<T> potentially point to multiple series instead of a single one.
              Last edited by ntbone; 05-04-2021, 02:03 AM.

              Comment


                #8
                Hello ntbone,

                My code above demonstrates this problem.
                The help guide documentation is the expectation, you need to work within that expectation for whatever you make. The original post you provided would be expected. GetValueAt expects you are programming explicitly, You need to specify the series you want to get value at if you use that method.

                I am glad that it is documented but since Indicator implements ISeries<double> it would make more sense for it to implement it consistently
                You can certainly ask for a feature request for things that you think would make more sense, this is an item that would be unlikely to change as many items rely on the help guide expectation and have be programmed accordingly.

                Regardless of the reasons for looking up a value with GetValueAt vs using barsAgo, if I have an indicator and its Input series is another Indicator (as defined through the UI) and I want to use GetValueAt(barIndex), I have to determine what type of implementation Input is.
                Right, the documentation for Input shows how you can determine what the type of input was. Again you need to be explicit when you develop complex items.

                If Input is a Indicator, then I have to cast Input to Indicator, determine which plot was connected up to the Input, and then call Indicator.Values[index].GetValueAt(X).
                That would be correct in this use case.

                That eliminates the usefulness of a interface because now it requires specific knowledge of an implementation in order to obtain the correct and desired values for the various functions.
                This is not really intended to be a one method solves all generic, its got a specific purpose of looking up data for the specific series that you are calling GetValueAt from.

                Please let me know if I may be of further assistance.
                JesseNinjaTrader Customer Service

                Comment

                Latest Posts

                Collapse

                Topics Statistics Last Post
                Started by abnfbjsaddfdej, Today, 02:37 AM
                0 responses
                1 view
                0 likes
                Last Post abnfbjsaddfdej  
                Started by ct, Today, 01:53 AM
                0 responses
                4 views
                0 likes
                Last Post ct
                by ct
                 
                Started by SmartArtsStudio, Yesterday, 11:24 PM
                0 responses
                3 views
                0 likes
                Last Post SmartArtsStudio  
                Started by kenz987, Yesterday, 10:06 PM
                0 responses
                4 views
                0 likes
                Last Post kenz987
                by kenz987
                 
                Started by AaronKoRn, Yesterday, 07:24 PM
                0 responses
                8 views
                0 likes
                Last Post AaronKoRn  
                Working...
                X