Hello,
I created an indicator which provides an Average Daily TrueRange (ADTR) for TIME_PERIOD_AGGREGATION charts (possible periods between 30 seconds and 12 hours) and TICK_BAR charts. The timespan for ATR calculation is moving 24 hours back from last period, whereat time gaps over weekends and periods with ATR = 0 are filtered. The intention is to compensate lower ATRs (during night sessions, lunch times and so on) and higher ATRs (during main trading sessions).
Here is the source code of the ADTR indicator:
package jForex;
import com.dukascopy.api.indicators.*;
import com.dukascopy.api.IBar;
import java.awt.Color;
public class ADTR implements IIndicator
{
private IndicatorInfo clsIndicatorInfo;
private IIndicatorContext clsIndicatorContext;
private InputParameterInfo[] clsInputParameterInfos;
private OutputParameterInfo[] clsOutputParameterInfos;
private IBar[][] clsBarInputs = new IBar[1][];
private double[][] clsdOutputs = new double[1][];
public void onStart(IIndicatorContext context)
{
clsIndicatorContext = context;
clsIndicatorInfo = new IndicatorInfo("ADTR", "Average Daily TrueRange", "My Indicators", false, false, true, 1, 0, 1);
clsIndicatorInfo.setUnstablePeriod(true);
clsIndicatorInfo.setRecalculateOnNewCandleOnly(true);
clsInputParameterInfos = new InputParameterInfo[] { new InputParameterInfo("Inputs", InputParameterInfo.Type.BAR) };
clsOutputParameterInfos = new OutputParameterInfo[] { new OutputParameterInfo("ADTR", OutputParameterInfo.Type.DOUBLE, OutputParameterInfo.DrawingStyle.LINE) {{ setColor(Color.gray); }} };
}
public IndicatorResult calculate(int iStartIndex, int iEndIndex)
{
int iInIndex = 0, iOutIndex = 0, iIndex = 0, iPeriodsCount = 0;
long lIntervalOnePeriod = clsIndicatorContext.getFeedDescriptor().getPeriod().getInterval(),
lInterval30Seconds = 30 * 1000L, lIntervalOneHour = 2 * 60 * lInterval30Seconds, lIntervalOneDay = 24 * lIntervalOneHour,
lLastInterval = 0L, lIntervalSum = 0L;
double dLastAvgDailyTrueRange = Double.NaN, dLastTrueRange = 0.0, dTrueRangeSum = 0.0;
if (iStartIndex - getLookback() < 0)
iStartIndex = getLookback();
if (iStartIndex > iEndIndex)
return new IndicatorResult(0, 0);
for (iInIndex = iStartIndex, iOutIndex = 0 ; iInIndex <= iEndIndex ; iInIndex++, iOutIndex++)
{
if (lIntervalOneDay / Math.abs(lIntervalOnePeriod) > 1)
{
for (lIntervalSum = 0L, iPeriodsCount = 0, dTrueRangeSum = 0.0, iIndex = iInIndex ; iIndex >= iStartIndex && lIntervalSum < lIntervalOneDay; iIndex--)
{
lLastInterval = ((IBar)clsBarInputs[0][iIndex]).getTime() - ((IBar)clsBarInputs[0][iIndex-1]).getTime();
if ((lIntervalOnePeriod >= lInterval30Seconds && lLastInterval == lIntervalOnePeriod) || (lIntervalOnePeriod == -1 && lLastInterval < lIntervalOneHour))
{
lIntervalSum += lLastInterval;
dLastTrueRange = Math.max(((IBar)clsBarInputs[0][iIndex-1]).getHigh(), ((IBar)clsBarInputs[0][iIndex]).getOpen()) - Math.min(((IBar)clsBarInputs[0][iIndex-1]).getLow(), ((IBar)clsBarInputs[0][iIndex]).getOpen());
if (dLastTrueRange > 0.0)
{
iPeriodsCount++;
dTrueRangeSum += dLastTrueRange;
}
}
}
if (iPeriodsCount > 0 && dTrueRangeSum > 0.0)
dLastAvgDailyTrueRange = dTrueRangeSum / iPeriodsCount;
}
else
{
dLastTrueRange = Math.max(((IBar)clsBarInputs[0][iInIndex-1]).getHigh(), ((IBar)clsBarInputs[0][iInIndex]).getOpen()) - Math.min(((IBar)clsBarInputs[0][iInIndex-1]).getLow(), ((IBar)clsBarInputs[0][iInIndex]).getOpen());
if (dLastTrueRange > 0.0)
dLastAvgDailyTrueRange = dLastTrueRange;
}
clsdOutputs[0][iOutIndex] = dLastAvgDailyTrueRange;
}
return new IndicatorResult(iStartIndex, iOutIndex);
}
public int getLookback()
{
return 1;
}
public int getLookforward()
{
return 0;
}
public IndicatorInfo getIndicatorInfo()
{
return clsIndicatorInfo;
}
public InputParameterInfo getInputParameterInfo(int iIndex)
{
return (iIndex <= clsInputParameterInfos.length ? clsInputParameterInfos[iIndex] : null);
}
public OptInputParameterInfo getOptInputParameterInfo(int iIndex)
{
return null;
}
public OutputParameterInfo getOutputParameterInfo(int iIndex)
{
return (iIndex <= clsOutputParameterInfos.length ? clsOutputParameterInfos[iIndex] : null);
}
public void setInputParameter(int iIndex, Object oArray)
{
clsBarInputs[iIndex] = (IBar[]) oArray;
}
public void setOptInputParameter(int iIndex, Object oValue)
{
return;
}
public void setOutputParameter(int iIndex, Object oArray)
{
clsdOutputs[iIndex] = (double[]) oArray;
}
}
Hint to support:
I tried to filter the chart DataType using "IIndicatorContext.getFeedDescriptor().getDataType()". But this method provides "TICKS" instead of "TICK_BAR" applied to a 100 Ticks TickBar chart. This seems to be an error, please correct it with next API version.
Because of this getDataType() problem my only indication for chart type is "IIndicatorContext.getFeedDescriptor().getPeriod().getInterval()" at line 29 which provides -1 for TickBar charts.
If the timespan between two TickBar period start times is one hour or more, then I assume that a weekend is between theese two periods.
Question:
Because I want to use this indicator inside other indicators I tried to encode a test indicator ADTRTest.java.
But I get a NullPointerException in the calculate method at line 39. I need help to find out and eliminate the reason of this error. I don't know if the root cause is in ADTR indicator (although standalone ADTR works fine without exception) or in calling ADTRTest indicator.
package jForex;
import com.dukascopy.api.indicators.*;
import com.dukascopy.api.IBar;
public class ADTRTest implements IIndicator
{
private IndicatorInfo clsIndicatorInfo;
private IIndicatorContext clsIndicatorContext;
private InputParameterInfo[] clsInputParameterInfos;
private OutputParameterInfo[] clsOutputParameterInfos;
private IIndicator clsIndicatorADTR;
private IBar[][] clsBarInputs = new IBar[1][];
private double[][] clsdOutputs = new double[1][];
public void onStart(IIndicatorContext context)
{
clsIndicatorContext = context;
IIndicatorsProvider indicatorsProvider = clsIndicatorContext.getIndicatorsProvider();
clsIndicatorADTR = indicatorsProvider.getIndicator("ADTR");
clsIndicatorInfo = new IndicatorInfo("ADTRTest", "Average Daily TrueRange Test", "My Indicators", false, false, true, 1, 0, 1);
clsInputParameterInfos = new InputParameterInfo[] { new InputParameterInfo("Inputs", InputParameterInfo.Type.BAR) };
clsOutputParameterInfos = new OutputParameterInfo[] { new OutputParameterInfo("ADTRTest", OutputParameterInfo.Type.DOUBLE, OutputParameterInfo.DrawingStyle.LINE) };
}
public IndicatorResult calculate(int iStartIndex, int iEndIndex)
{
int iLookback = getLookback(), iInIndex = 0, iOutIndex = 0, iIndex = 0;
double[] dIndicatorADTROutputs;
if (iStartIndex - iLookback < 0)
iStartIndex = iLookback;
if (iStartIndex > iEndIndex)
return new IndicatorResult(0, 0);
dIndicatorADTROutputs = new double[iEndIndex - iStartIndex + 1];
clsIndicatorADTR.setInputParameter(0, clsBarInputs[0]);
clsIndicatorADTR.setOutputParameter(0, dIndicatorADTROutputs);
IndicatorResult indicatorADTRResult = clsIndicatorADTR.calculate(iStartIndex, iEndIndex);
for (iInIndex = iStartIndex, iOutIndex = 0 ; iInIndex <= iEndIndex ; iInIndex++, iOutIndex++)
{
clsdOutputs[0][iOutIndex] = dIndicatorADTROutputs[iOutIndex];
}
return new IndicatorResult(iStartIndex, iOutIndex);
}
public int getLookback()
{
return clsIndicatorADTR.getLookback();
}
public int getLookforward()
{
return 0;
}
public IndicatorInfo getIndicatorInfo()
{
return clsIndicatorInfo;
}
public InputParameterInfo getInputParameterInfo(int iIndex)
{
return (iIndex <= clsInputParameterInfos.length ? clsInputParameterInfos[iIndex] : null);
}
public OptInputParameterInfo getOptInputParameterInfo(int iIndex)
{
return null;
}
public OutputParameterInfo getOutputParameterInfo(int iIndex)
{
return (iIndex <= clsOutputParameterInfos.length ? clsOutputParameterInfos[iIndex] : null);
}
public void setInputParameter(int iIndex, Object oArray)
{
clsBarInputs[iIndex] = (IBar[]) oArray;
}
public void setOptInputParameter(int iIndex, Object oValue)
{
return;
}
public void setOutputParameter(int iIndex, Object oArray)
{
clsdOutputs[iIndex] = (double[]) oArray;
}
}
Regards
AbsoluteReturner