Hello
I found one bug in JForex.
I have created my custom indicator for correlation calculation.
My indicator calculates correlation of any currency pair against EURUSD currency pair.
You can put this indicator into any chart (for example GBPUSD) and it will calculate the correlation value of this chart against EURUSD
I have implemented my custom algorithms for synchronization of data sources (first currency pair is first data source and EURUSD currency pair is second data source).
The bug I found:
You can see the bug when you will put the indicator into any chart (for example GBPUSD) and then right-click on the indicator linechart and EDIT. In the EDIT window when you will change the PERIOD optInputParametr it will throw exception into MESSAGES tabbedPane.
When you will change the PERIOD value to higher value, it will throw different exception like if you will hange the PERIOD value to lower value.
Please take a look at that.
I was looking at that and the exception is saying that indicator returned less values than expected. Then I tried to add one value more (Double.NaN) and that throws another exception saying that there is too much values... Thats weird right?
I attach the source code of the indicator:
package jforex;
import com.dukascopy.api.IBar;
import com.dukascopy.api.Instrument;
import com.dukascopy.api.indicators.*;
import java.util.ArrayList;
import java.util.List;
public class LS_I_DirectionalCorrelation_vs_EURUSD implements IIndicator {
private IIndicatorContext context;
private static final String name = "LS_I_DirectionalCorrelation_vs_EURUSD";
private static final String group = "LorencSoftware.indicators";
private int period;
private double[] outputs;
private IBar[][] inputs;
private IndicatorInfo indicatorInfo;
private InputParameterInfo[] inputParameterInfos;
private OptInputParameterInfo optInputParameterInfo;
private OutputParameterInfo outputParameterInfo;
// synchronized data
private int synchronizedData_newStartIndex = 0;
private int synchronizedData_newEndIndex = 0;
private final List<IBar> synchronizedData_defaultChart = new ArrayList<IBar>();
private final List<IBar> synchronizedData_eurusdChart = new ArrayList<IBar>();
// synchronized data
public void onStart(IIndicatorContext context) {
this.context = context;
outputs = null;
inputs = new IBar[2][];
indicatorInfo = new IndicatorInfo(name, name, group, false, false, false, 2, 1, 1);
InputParameterInfo inputParamInfo_defaultChart = new InputParameterInfo("Default chart", InputParameterInfo.Type.BAR);
InputParameterInfo inputParamInfo_eurusd = new InputParameterInfo("EURUSD chart", InputParameterInfo.Type.BAR);
inputParamInfo_eurusd.setInstrument(Instrument.EURUSD);
inputParamInfo_eurusd.setPeriod(inputParamInfo_defaultChart.getPeriod());
inputParameterInfos = new InputParameterInfo[2];
inputParameterInfos[0] = inputParamInfo_defaultChart;
inputParameterInfos[1] = inputParamInfo_eurusd;
optInputParameterInfo = new OptInputParameterInfo("Period",
OptInputParameterInfo.Type.OTHER, new IntegerRangeDescription(14, 1, 100, 1));
outputParameterInfo = new OutputParameterInfo("Correlation vs EURUSD",
OutputParameterInfo.Type.DOUBLE, OutputParameterInfo.DrawingStyle.LINE);
}
public IndicatorResult calculate(int startIndex, int endIndex) {
if (startIndex - getLookback() < 0) {
startIndex -= startIndex - getLookback();
}
if (startIndex>endIndex) {
return new IndicatorResult(startIndex, 0);
}
synchronizedData_createCustomSynchronizedCharts(startIndex, endIndex);
int i = 0;
int j = 0;
if (synchronizedData_defaultChart.isEmpty()) { // if there are no synchronized bars
for (i = startIndex; i <= endIndex; i++) {
outputs[j] = Double.NaN;
j++;
}
return new IndicatorResult(startIndex, j);
}
for (i = startIndex; i < synchronizedData_newStartIndex; i++) { // Set NaN for all left-side nonsynchronized bars
outputs[j] = Double.NaN;
j++;
}
int x = getLookback();
for (i = synchronizedData_newStartIndex; i<=synchronizedData_newEndIndex; i++) {
double[] defaultChartValues = new double[period];
double[] eurusdChartValues = new double[period];
for (int ii = 0; ii<period; ii++) {
defaultChartValues[ii] = synchronizedData_defaultChart.get(x-ii).getClose();
eurusdChartValues[ii] = synchronizedData_eurusdChart.get(x-ii).getClose();
}
outputs[j] = correl(defaultChartValues, eurusdChartValues);
j++;
x++;
}
for (i=(synchronizedData_newEndIndex+1); i<=endIndex; i++) { // Set NaN for all right-side nonsynchronized bars
outputs[j] = Double.NaN;
j++;
}
context.getConsole().getOut().println("startIndex="+startIndex+" endIndex="+endIndex+" synchronizedData_newStartIndex="+synchronizedData_newStartIndex+" synchronizedData_newEndIndex="+synchronizedData_newEndIndex+" j="+j+" outputs.length="+outputs.length);
return new IndicatorResult(startIndex, j);
}
// SET METHODS
public void setInputParameter(int index, Object array) {
if (index < inputs.length) {
inputs[index] = (IBar[]) array;
}
}
public void setOutputParameter(int index, Object array) {
outputs = (double[]) array;
}
public void setOptInputParameter(int index, Object value) {
Integer i_value = (Integer) value;
this.period = i_value;
}
// GET METHODS
public IndicatorInfo getIndicatorInfo() {
return indicatorInfo;
}
public InputParameterInfo getInputParameterInfo(int index) {
if (index < inputParameterInfos.length) {
return inputParameterInfos[index];
}
return null;
}
public OptInputParameterInfo getOptInputParameterInfo(int index) {
return optInputParameterInfo;
}
public OutputParameterInfo getOutputParameterInfo(int index) {
return outputParameterInfo;
}
// LOOKBACK AND LOOKFORWARD
public int getLookback() {
return period-1;
}
public int getLookforward() {
return 0;
}
//CUSTOM METHODS
private void synchronizedData_createCustomSynchronizedCharts(int startIndex, int endIndex) {
synchronizedData_defaultChart.clear();
synchronizedData_eurusdChart.clear();
synchronizedData_newStartIndex = 0;
synchronizedData_newEndIndex = 0;
for (int i = (startIndex-getLookback()); i<=endIndex; i++) {
IBar mainBar = inputs[0][i];
IBar equivalentBar = synchronizedData_findIBarEquivalent(mainBar, inputs[1]);
if (equivalentBar!=null) {
if (synchronizedData_defaultChart.isEmpty()) { // if this is first synchronized bar
synchronizedData_newStartIndex = i;
}
synchronizedData_defaultChart.add(mainBar);
synchronizedData_eurusdChart.add(equivalentBar);
synchronizedData_newEndIndex = i;
} else {
if (synchronizedData_defaultChart.isEmpty()) {
continue;
} else {
break;
}
}
}
if (synchronizedData_defaultChart.isEmpty()==false) {
synchronizedData_newStartIndex += getLookback();
if (synchronizedData_newStartIndex>synchronizedData_newEndIndex) { // if there is not enought of synchronized bars
synchronizedData_defaultChart.clear();
synchronizedData_eurusdChart.clear();
synchronizedData_newStartIndex = 0;
synchronizedData_newEndIndex = 0;
}
}
}
private IBar synchronizedData_findIBarEquivalent(IBar templateBar, IBar[] whereToFind) {
for (IBar b : whereToFind) {
if (b.getTime()==templateBar.getTime()) {
return b;
}
}
return null;
}
private double correl(double[] xArray, double[] yArray) {
double x = average(xArray);
double y = average(yArray);
double up = 0;
for (int i = 0; i<xArray.length; i++) {
up += ( (x-xArray[i]) * (y-yArray[i]) );
}
double firstSumOfDown = 0;
double secondSumOfDown = 0;
for (int i=0; i<xArray.length; i++) {
firstSumOfDown += twice((x-xArray[i]));
}
for (int i=0; i<yArray.length; i++) {
secondSumOfDown += twice((y-yArray[i]));
}
double down = firstSumOfDown*secondSumOfDown;
down = Math.sqrt(down);
return (up/down);
}
private double twice(double d) {
return d*d;
}
private double average(double[] pointsArray) {
double result = 0;
for (int i=0; i<pointsArray.length; i++) {
result += pointsArray[i];
}
return result / pointsArray.length;
}
}