Hi support team,
I have written a sample indicator and strategy to demonstrate the issue I am experiencing.
Below is the code of a custom indicator that just calls EMA(34) to populate the 1st output array and calls EMA(34,MaType.EMA) to populate the 2nd output array.
package Indicators;
import com.dukascopy.api.IIndicators;
import com.dukascopy.api.IIndicators.MaType;
import com.dukascopy.api.indicators.*;
import java.awt.Color;
import java.lang.Math;
public class EMATest implements IIndicator {
private IndicatorInfo indicatorInfo;
private InputParameterInfo[] inputParameterInfos;
private OptInputParameterInfo[] optInputParameterInfos;
private OutputParameterInfo[] outputParameterInfos;
private double[][] inputs = new double[1][];
private int emaPeriod;
private double[][] outputs = new double[2][];
private IIndicator emaIndi, maIndi;
private IIndicatorContext indiContext;
public void onStart(IIndicatorContext context)
{
this.indiContext = context;
indicatorInfo = new IndicatorInfo("EMATest", "Test", "Test", false, false, false, 1, 1, 2);
inputParameterInfos = new InputParameterInfo[]
{
new InputParameterInfo("Input data", InputParameterInfo.Type.DOUBLE) { { setAppliedPrice(IIndicators.AppliedPrice.CLOSE); } }
};
optInputParameterInfos = new OptInputParameterInfo[]
{
new OptInputParameterInfo("EMA Period", OptInputParameterInfo.Type.OTHER, new IntegerRangeDescription(34, 1, 100, 1))
};
outputParameterInfos = new OutputParameterInfo[]
{
new OutputParameterInfo("EMATest EMA", OutputParameterInfo.Type.DOUBLE, OutputParameterInfo.DrawingStyle.LINE),
new OutputParameterInfo("EMATest MA(EMA)", OutputParameterInfo.Type.DOUBLE, OutputParameterInfo.DrawingStyle.LINE)
};
IIndicatorsProvider indProvider = context.getIndicatorsProvider();
emaIndi = indProvider.getIndicator("EMA");
maIndi = indProvider.getIndicator("MA");
maIndi.setOptInputParameter(1, MaType.EMA.ordinal());
outputParameterInfos[0].setColor(java.awt.Color.RED);
outputParameterInfos[1].setColor(java.awt.Color.BLUE);
}
public IndicatorResult calculate(int startIndex, int endIndex)
{
//calculating startIndex taking into account lookback value
if (startIndex - getLookback() < 0) {
startIndex -= startIndex - getLookback();
}
//calculate EMA
emaIndi.setInputParameter(0, inputs[0]);
emaIndi.setOutputParameter(0, outputs[0]);
IndicatorResult emaResult = emaIndi.calculate(emaIndi.getLookback(), endIndex);
if (emaResult.getNumberOfElements() == 0)
{
//EMA returned 0 values
return new IndicatorResult(0, 0);
}
//calculate MA
maIndi.setInputParameter(0, inputs[0]);
maIndi.setOutputParameter(0, outputs[1]);
IndicatorResult maResult = maIndi.calculate(maIndi.getLookback(), endIndex);
if (emaResult.getNumberOfElements() == 0)
{
//EMA returned 0 values
return new IndicatorResult(0, 0);
}
return maResult;
}
public IndicatorInfo getIndicatorInfo() {
return indicatorInfo;
}
public InputParameterInfo getInputParameterInfo(int index) {
if (index <= inputParameterInfos.length) {
return inputParameterInfos[index];
}
return null;
}
public int getLookback() {
return emaIndi.getLookback(); // assume emaIndi and maIndi have the same getLooback() as they are EMA's of the same period
}
public int getLookforward() {
return 0;
}
public OptInputParameterInfo getOptInputParameterInfo(int index) {
if (index <= optInputParameterInfos.length) {
return optInputParameterInfos[index];
}
return null;
}
public OutputParameterInfo getOutputParameterInfo(int index) {
if (index <= outputParameterInfos.length) {
return outputParameterInfos[index];
}
return null;
}
public void setInputParameter(int index, Object array) {
inputs[index] = (double[]) array;
}
public void setOptInputParameter(int index, Object value)
{
switch (index)
{
case 0:
emaPeriod = (Integer) value;
emaIndi.setOptInputParameter(0, emaPeriod);
maIndi.setOptInputParameter(0, emaPeriod);
break;
}
}
public void setOutputParameter(int index, Object array) {
outputs[index] = (double[]) array;
}
}
When this indicator is dropped on a chart along with an EMA(34) and MA(34,MaType.EMA) the values of my indicator's outputs and the other 2 indicators is exactly the same.
I have also written a strategy that calls this custom indicator as well as the EMA(34) and MA(34,MaType.EMA) and simply outputs the values on every OnBar event to the console with a timestamp - refer to code below.
package jforex;
import java.io.File;
import java.util.*;
import com.dukascopy.api.*;
import com.dukascopy.api.IIndicators.MaType;
public class EMATestStrategy implements IStrategy {
private IEngine engine;
private IConsole console;
private IHistory history;
private IContext context;
private IIndicators indicators;
private IUserInterface userInterface;
@Configurable("Instrument") public Instrument instrument = Instrument.EURUSD;
@Configurable("Period") public Period period = Period.ONE_HOUR;//FOUR_HOURS;// DAILY_SUNDAY_IN_MONDAY ;//DAILY;ONE_HOUR;//
@Configurable("EMA Length") public int emaLength = 34;
public void onStart(IContext context) throws JFException
{
this.engine = context.getEngine();
this.console = context.getConsole();
this.history = context.getHistory();
this.context = context;
this.indicators = context.getIndicators();
this.userInterface = context.getUserInterface();
this.indicators.registerCustomIndicator(new File(context.getFilesDir() + System.getProperty("file.separator") + "EMATest.jfx"));
}
public void onAccount(IAccount account) throws JFException {
}
public void onMessage(IMessage message) throws JFException {
}
public void onStop() throws JFException {
}
public void onTick(Instrument instrument, ITick tick) throws JFException {
}
public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException
{
// return on events we are not interested in
if (instrument != this.instrument) return;
// skip all other periods
if (period != this.period) return;
Object[] EMATestResult = indicators.calculateIndicator(this.instrument,
this.period,
new OfferSide[] {OfferSide.BID},
"EMATest",
new IIndicators.AppliedPrice[]{IIndicators.AppliedPrice.CLOSE},
new Object[]{emaLength},
Filter.WEEKENDS,
1,
bidBar.getTime(),
0);
Object[] EMAResult = indicators.calculateIndicator(this.instrument,
this.period,
new OfferSide[] {OfferSide.BID},
"EMA",
new IIndicators.AppliedPrice[]{IIndicators.AppliedPrice.CLOSE},
new Object[]{emaLength},
Filter.WEEKENDS,
1,
bidBar.getTime(),
0);
Object[] MAResult = indicators.calculateIndicator(this.instrument,
this.period,
new OfferSide[] {OfferSide.BID},
"MA",
new IIndicators.AppliedPrice[]{IIndicators.AppliedPrice.CLOSE},
new Object[]{emaLength, MaType.EMA.ordinal()},
Filter.WEEKENDS,
1,
bidBar.getTime(),
0);
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
cal.setTimeInMillis(bidBar.getTime());
console.getOut().println(cal.get(Calendar.DAY_OF_MONTH) + "." + (cal.get(Calendar.MONTH)+1) + "." + cal.get(Calendar.YEAR) + " " + cal.get(Calendar.HOUR_OF_DAY) + ":" + cal.get(Calendar.MINUTE) +
" - EMA(" + emaLength + ") = " + ((double[])EMAResult[0])[0] +
", MA(" + emaLength + ", MaType.EMA) = " + ((double[])MAResult[0])[0] +
", EMATest(" + emaLength + ") = " + ((double[])EMATestResult[0])[0] +
", EMATest(" + emaLength + ", MaType.EMA) = " + ((double[])EMATestResult[1])[0]);
}
}
The console output of this strategy clearly illustrates that calling the EMA(34) and MA(34,MaType.EMA) straight from the strategy provides the same values as the indicators on the chart, but the 2 output arrays from the custom indicator are the same as each other,
but different than the direct indicator calls.
Can you please explain the results and help me to get around this please?
thanks in advance,
Christian