Profile Picture

ISeries.LookBack appears to actually look forward

Posted By davidS 8 Years Ago
Message
Posted Sunday February 08 2009
As my first indicator programming exercise, I decided to patch DoQ_Indicators to work with the latest RE release (build 380). When I run the MedianSeries indicator, the indicator actually leads the data!! I've attached a screen shot showing MedianSeries on opening prices using a 7 day period. According to the code (below) the indicator output should be the opening price from 3 days prior, instead it shows the opening price from 3 days in the future. Am I missing something obvious?Unsure

protected override double CalcNewValue(int index)
{

if (index < _Period - 1)//init
{
return double.NaN;
}
else
{
int Mid = _Period / 2;

double Median = (_Period % 2 != 0) ? inputs[0].LookBack(index - Mid) : //odd
(inputs[0].LookBack(index - Mid) + inputs[0].LookBack(index - Mid - 1)) / 2; //even

return Median;
}

}

Attachments
MedianSeriesIndicator.JPG (429 views, 133.00 KB)
Posted Sunday February 08 2009
The value passed to LookBack to look back at, starting with the current one.  So LookBack(0) is the current bar's value, LookBack(1) is the previous bar's value, etc.  So instead of LookBack(index - mid) you probably just want LookBack(mid).

Daniel

Posted Monday February 09 2009
Daniel,
As I read the documentation I would agree with you, but when applied to historical data within the context of CalcNewValue, LookBack(0) always returns the very last value in the historical series, regardless of the value of the index parameter passed to the method. If I write an indicator that simply returns inputs[0].LookBack(0) I would expect it to copy back the time series, but all I get is a constant value corresponding to the most recent bar (see code below). I've also discovered that the index parameter of CalcNewValue counts backwards in time, so that the lowest values refer to the most recent bars. The implication of this ordering is that if the indicator has a startup transient, e.g., during which NaN is returned, it appears at the end of the series as plotted by the indicator.

A few other observations:
inputs[0].LookBack(index) copies back the time series. This is the result that I expected to see when I used inputs[0].LookBack(0)
inputs[0].LookBack(index + 5) throws a System.ArgumentOutOfRangeException
inputs[0].LookBack(index - 5) generates a non-causal copy of the time series, with the indicator preceding the data by 5 samples

Suggestions/clarifications would be greatly appreciated.

David

//------------------Code Sample-----------------------------

using System;
using System.Collections.Generic;
using System.Text;

using RightEdge.Common;
using RightEdge.Indicators;

namespace DoQ_Indicators
{
[
YYEIndicatorAttribute
(
System.Drawing.KnownColor.Chocolate,
YYEIndicatorAttribute.EIndicatorGroup.Other,
Author = "DES",
CompanyName = "DES",
DefaultDrawingPane = "Price Pane",
Description = "LookBack test code",
GroupName = "DoQ-Indicators",
HelpText = "",
Id = "a67d0f06-179b-4be9-911b-dbecfcff110a",
Name = "LookBackTest",
Version = "0.0.1"
)
]
[Serializable]
[SeriesInputAttribute("Series", 1, Value = BarElement.Close)]

public class LookBackTest : SeriesCalculatorBaseWithValues
{
int Transient = 10;

[ConstructorArgument(Name = "Transient",
Type = ConstructorArgumentType.Integer,
Value = 10,
Order = 1)]

public LookBackTest(int transient)
: base(1)
{
this.Transient = transient;
}


//   This method is where you calculate the indicator value for a given bar.
//   The index parameter to this method tells you which value you need to
//   calculate.
//   You can access the first input series with inputs[0], the second
//   input series with inputs[1], etc.
protected override double CalcNewValue(int index)
{

if (index < Transient)//init
{
return double.NaN;
}
else
{

// Here is the test line that is modified to reproduce the results described in the post
double rslt = inputs[0].LookBack(0);

return rslt;
}

}

protected override void Reset()
{
//   This method is called when the series needs to be recalculated.
//   If you save any state between calls to CalcNewValue(), you should
//   reset that state in this method.
}

}
}
Posted Monday February 09 2009
If you're looking to verify the amount of history available for a calculation, I usually check the indicator's list size....

protected override double CalcNewValue(int index)
{
if ( this.Count < Transient)
{return double.NaN;}
else
{return inputs[0].LookBack(index);}
}

HTH,
Mark
Posted Monday February 09 2009
The index parameter that is passed to CalcNewValue is also a "lookback" index.  However...

RightEdge indicators can be calculated both iteratively and "all-at-once."  Generally they are calculated iteratively when they are used in a system, and all at once when you add them to a quick chart.  When they are calculated iteratively, then the index parameter to CalcNewValue is always zero (since you are supposed to calculate the most recent value), and LookBack(0) on the input series is the corresponding input series value.  But when they are calculated all at once, the index parameter will be Count - 1 the first time, Count - 2 the next, all the way down to 3, 2, 1, 0.  But during all these calls the input series will always have the values for the entire chart, so calling LookBack(0) on them will always get the last value.  If you want the value of an input series corresponding to index, call input.LookBack(index), and if you want the value 5 bars before that, call input.LookBack(index + 5)... but check to make sure that index > Count - 5 first, or you'll get an ArgumentOutOfRangeException.

So you should always use the form LookBack(index + desiredLookBack) when referring to indicator inputs.  If they are being calculated iteratively, then index will always be zero and it will still work.

This can make it difficult to write indicators correctly in RightEdge.  The "all-at-once" mode was included because on some indicators it would allow for faster calculations.  I think it's more trouble than it's worth though, so someday if we redesign the indicator plugin interfaces it will probably get removed.

Thanks,
Daniel

Posted Monday February 09 2009
Instead of removing functionality that may be important where speed is an issue, why not keep the potentially faster functionality, but document it well?

I haven't dealt with this particular issue, but I have run into other efficiency issues when dealing with high-frequency data and lots of symbols. Efficiency can be important.

Posted Monday February 09 2009
Daniel,

Ah! Now it all makes sense. It's the difference between real-time (iterative) and historical (all-at-once) that had me confused. I agree with another forum participant that this would be very useful information to have in the API documentation. Currently, it doesn't really address all-at-once calculation, nor does it really explain the role of the index parameter.

It's clear that the programmer needs to take care in the creation of indicators to be used in backtesting, since it is very possible to generate non-causal indicators!

Thanks,
David
Posted Tuesday February 10 2009
The "all-at-once" mode isn't used during backtesting, so if you do accidentally create a non-causal indicator it won't act that way during backtesting.

Daniel



Similar Topics


Reading This Topic


2005-2017 © RightEdge Systems