I have created a simple custom indicator to demonstrate a number of bugs in connection with array lengths being passed to indicators.
The indicator has two inputs, bid prices and ask prices, and two outputs, bid volumes and ask volumes, and simply displays the two volumes together.
Unfortunately the system passes inconsistent array lengths resulting in errors.
package jforex;
import com.dukascopy.api.Filter;
import com.dukascopy.api.IConsole;
import com.dukascopy.api.Library;
import com.dukascopy.api.OfferSide;
import com.dukascopy.api.indicators.IIndicator;
import com.dukascopy.api.indicators.IIndicatorContext;
import com.dukascopy.api.indicators.IndicatorInfo;
import com.dukascopy.api.indicators.IndicatorResult;
import com.dukascopy.api.indicators.InputParameterInfo;
import com.dukascopy.api.indicators.OptInputParameterInfo;
import com.dukascopy.api.indicators.OutputParameterInfo;
import java.awt.Color;
public class Volumes implements IIndicator {
private IIndicatorContext context;
private IConsole console;
private IndicatorInfo indicatorInfo;
private InputParameterInfo[] inputParameterInfos;
private int lookback = 1;
private int lookforward = 0;
private OptInputParameterInfo[] optInputParameterInfos;
private OutputParameterInfo[] outputParameterInfos;
private Object[] inputs;
private Object[] outputs;
/**
* Constructor
*/
public Volumes() {
indicatorInfo = new IndicatorInfo(
"MyVOLS", // name
"Bid/Ask Volumes", // title
"My indicators", // groupName
false, // overChart
false, // overVolumes
false, // unstablePeriod
2, // numberOfInputs
0, // numberOfOptionalInputs
2); // numberOfOutputs
inputParameterInfos = new InputParameterInfo[] {
new InputParameterInfo(
"bidPrices", // name
InputParameterInfo.Type.PRICE), // type
new InputParameterInfo(
"askPrices", // name
InputParameterInfo.Type.PRICE)}; // type
inputParameterInfos[0].setOfferSide(OfferSide.BID);
inputParameterInfos[1].setOfferSide(OfferSide.ASK);
inputParameterInfos[0].setFilter(Filter.NO_FILTER);
inputParameterInfos[1].setFilter(Filter.NO_FILTER);
outputParameterInfos = new OutputParameterInfo[] {
new OutputParameterInfo(
"bidVols", // name
OutputParameterInfo.Type.DOUBLE, // type
OutputParameterInfo.DrawingStyle.HISTOGRAM), // drawingStyle
new OutputParameterInfo(
"askVols", // name
OutputParameterInfo.Type.DOUBLE, // type
OutputParameterInfo.DrawingStyle.HISTOGRAM)}; // drawingStyle
outputParameterInfos[0].setColor(Color.RED);
outputParameterInfos[1].setColor(Color.GREEN);
}
@Override
public void onStart(IIndicatorContext context) {
this.context = context;
this.console = context.getConsole();
}
@Override
public IndicatorResult calculate(int startIndex, int endIndex) {
double[] inputBidVolumes = ((double[][])inputs[0])[4];
double[] inputAskVolumes = ((double[][])inputs[1])[4];
double[] outputBidVolumes = ((double[])outputs[0]);
double[] outputAskVolumes = ((double[])outputs[1]);
//calculating startIndex taking into account lookback value
if (endIndex > inputBidVolumes.length) {
endIndex = inputBidVolumes.length;
}
if (startIndex < lookback - 1) {
startIndex = lookback - 1;
}
if (startIndex > endIndex) {
return new IndicatorResult(0, 0);
}
console.getOut().printf("startIndex=%d\n", startIndex);
console.getOut().printf("endIndex=%d\n", endIndex);
console.getOut().printf("inputBidVolumes.length=%d\n", inputBidVolumes.length);
console.getOut().printf("inputAskVolumes.length=%d\n", inputAskVolumes.length);
console.getOut().printf("outputBidVolumes.length=%d\n", outputBidVolumes.length);
console.getOut().printf("outputAskVolumes.length=%d\n", outputAskVolumes.length);
int outputIndex = 0;
for (int inputIndex = startIndex; inputIndex < endIndex; inputIndex++) {
outputAskVolumes[outputIndex] = inputAskVolumes[inputIndex];
outputBidVolumes[outputIndex] = -inputBidVolumes[inputIndex];
outputIndex++;
}
return new IndicatorResult(startIndex, outputIndex);
}
@Override
public IndicatorInfo getIndicatorInfo() {
return indicatorInfo;
}
@Override
public InputParameterInfo getInputParameterInfo(int index) {
if (index <= inputParameterInfos.length) {
return inputParameterInfos[index];
} else {
return null;
}
}
@Override
public int getLookback() {
return lookback;
}
@Override
public int getLookforward() {
return lookforward;
}
@Override
public OptInputParameterInfo getOptInputParameterInfo(int index) {
if (index <= optInputParameterInfos.length) {
return optInputParameterInfos[index];
} else {
return null;
}
}
@Override
public OutputParameterInfo getOutputParameterInfo(int index) {
if (index <= outputParameterInfos.length) {
return outputParameterInfos[index];
} else {
return null;
}
}
@Override
public void setInputParameter(int index, Object array) {
if (inputs == null) {
inputs = new Object[inputParameterInfos.length];
}
inputs[index] = array;
}
@Override
public void setOptInputParameter(int index, Object value) {
}
@Override
public void setOutputParameter(int index, Object array) {
if (outputs == null) {
outputs = new Object[outputParameterInfos.length];
}
outputs[index] = array;
}
}