Hello Support,
I would like to report very annoying bug.
In HISTORICAL TESTER the range bars are not working as they should.
Here is very simple primitive script which just displays the range bars values and EMA14 values (received from the JForexAPI) during the Backtest:
package rangebartest;
import com.dukascopy.api.Configurable;
import com.dukascopy.api.DataType;
import com.dukascopy.api.Filter;
import com.dukascopy.api.IAccount;
import com.dukascopy.api.IBar;
import com.dukascopy.api.IConsole;
import com.dukascopy.api.IContext;
import com.dukascopy.api.IIndicators;
import com.dukascopy.api.IIndicators.AppliedPrice;
import com.dukascopy.api.IIndicators.MaType;
import com.dukascopy.api.IMessage;
import com.dukascopy.api.IStrategy;
import com.dukascopy.api.ITick;
import com.dukascopy.api.Instrument;
import com.dukascopy.api.JFException;
import com.dukascopy.api.OfferSide;
import com.dukascopy.api.Period;
import com.dukascopy.api.PriceRange;
import com.dukascopy.api.feed.FeedDescriptor;
import com.dukascopy.api.feed.IFeedDescriptor;
import com.dukascopy.api.feed.IRangeBar;
import com.dukascopy.api.feed.IRangeBarFeedListener;
import java.util.Calendar;
import java.util.TimeZone;
public class RangeBarTest implements IStrategy, IRangeBarFeedListener {
@Configurable("Instrument") public Instrument selectedInstrument = Instrument.EURUSD;
@Configurable("OfferSide") public OfferSide selectedOfferSide = OfferSide.BID;
@Configurable("Range bar size (in pips)") public int selectedRangeBarSizeInPips = 5;
@Configurable("MA TimePeriod") public int selectedMATimePeriod = 14;
@Configurable("MA Type") public MaType selectedMAType = MaType.EMA;
@Configurable("MA Filter") public Filter selectedFilter = Filter.WEEKENDS;
private IContext context;
private IIndicators indicators;
private IConsole console;
private PriceRange selectedPriceRange;
private IFeedDescriptor feedDescriptor;
@Override
public void onStart(IContext context) throws JFException {
try {
if (context.getChart(selectedInstrument)==null) {
throw new Exception("At first please open the "+selectedInstrument+" price chart in your JForex");
}
if (context.getChart(selectedInstrument).getPriceRange()==null) {
throw new Exception("The "+selectedInstrument+" price chart has to be in RANGE BARS");
}
if (context.getChart(selectedInstrument).getPriceRange().getPipCount()!=selectedRangeBarSizeInPips) {
throw new Exception("The "+selectedInstrument+" RANGE bars has to have "+selectedRangeBarSizeInPips+" pips size.");
}
this.context = context;
this.indicators = context.getIndicators();
this.console = context.getConsole();
this.selectedPriceRange = PriceRange.valueOf(selectedRangeBarSizeInPips);
this.context.subscribeToRangeBarFeed(selectedInstrument, selectedOfferSide, selectedPriceRange, this);
this.feedDescriptor = new FeedDescriptor();
feedDescriptor.setDataType(DataType.PRICE_RANGE_AGGREGATION);
feedDescriptor.setFilter(selectedFilter);
feedDescriptor.setInstrument(selectedInstrument);
feedDescriptor.setOfferSide(selectedOfferSide);
feedDescriptor.setPriceRange(selectedPriceRange);
} catch (Exception ex) {
throw new JFException(ex.getMessage(), ex.getCause());
}
}
@Override
public void onTick(Instrument instrument, ITick tick) throws JFException {
}
@Override
public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
}
@Override
public void onMessage(IMessage message) throws JFException {
}
@Override
public void onAccount(IAccount account) throws JFException {
}
@Override
public void onStop() throws JFException {
}
@Override
public void onBar(Instrument instrument, OfferSide offerSide, PriceRange priceRange, IRangeBar bar) {
try {
if (instrument.equals(selectedInstrument) && offerSide.equals(selectedOfferSide) && priceRange.getPipCount()==selectedRangeBarSizeInPips) {
Double ma0 = Double.NaN;
ma0 = (Double)indicators.calculateIndicator(feedDescriptor, new OfferSide[] {selectedOfferSide}, selectedMAType.name(), new AppliedPrice[] {AppliedPrice.CLOSE}, new Object[] {selectedMATimePeriod}, 0)[0];
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
calendar.setTimeInMillis(bar.getTime());
String startTime = toStringCalendar(calendar);
calendar.setTimeInMillis(bar.getEndTime());
String endTime = toStringCalendar(calendar);
console.getOut().println("START_TIME="+startTime+" END_TIME="+endTime+" OPEN="+bar.getOpen()+" CLOSE="+bar.getClose()+" HIGH="+bar.getHigh()+" LOW="+bar.getLow()+" MovinAverage="+ma0);
}
} catch (Exception ex) {
console.getErr().println(ex.getMessage());
}
}
private String toStringCalendar(Calendar calendar) {
String result = null;
int day = calendar.get(Calendar.DAY_OF_MONTH);
String s_day = null;
if (((int)day/10)==0) {
s_day = "0"+day;
} else {
s_day = ""+day;
}
int month = calendar.get(Calendar.MONTH)+1;
String s_month = null;
if (((int)month/10)==0) {
s_month = "0"+month;
} else {
s_month = ""+month;
}
int year = calendar.get(Calendar.YEAR);
String s_year = null;
if (((int)year/10)==0) {
s_year = "0"+year;
} else {
s_year = ""+year;
}
int hour = calendar.get(Calendar.HOUR_OF_DAY);
String s_hour = null;
if (((int)hour/10)==0) {
s_hour = "0"+hour;
} else {
s_hour = ""+hour;
}
int minute = calendar.get(Calendar.MINUTE);
String s_minute = null;
if (((int)minute/10)==0) {
s_minute = "0"+minute;
} else {
s_minute = ""+minute;
}
int second = calendar.get(Calendar.SECOND);
String s_second = null;
if (((int)second/10)==0) {
s_second = "0"+second;
} else {
s_second = ""+second;
}
int milisecond = calendar.get(Calendar.MILLISECOND);
String s_milisecond = null;
if (((int)milisecond/100)==0) {
s_milisecond = "0"+milisecond;
} else {
s_milisecond = ""+milisecond;
}
if (((int)milisecond/10)==0) {
s_milisecond = "0"+s_milisecond;
}
result = s_day+"."+s_month+"."+s_year+" "+s_hour+":"+s_minute+":"+s_second+"."+s_milisecond;
return result;
}
}
And here (in IMG1.png) is what user can see during the backtest.
As you can see there is difference between bars what user see on the chart and between bars what strategy receives from the API.
I think that this fact is also the reason why strategy receives different values of EMA14 comparing with the values of EMA14 what user can see on the chart.
Interesting thing what I found out:
It also happend (pretty often) that the strategy will receive following exception from the JForexAPI (see IMG2.png)
And the interesting thing is that AFTER this exception is thrown, then the strategy is receiving CORRECT bars from the API for a while.
In other words: After this exception is thrown then the bars, what user see in the chart, are the same like the bars, what strategy receives from the API. But just for a while. Later the difference between them is there again. I hope this interesting regular behavior will help you to determine where in the API is the bug hidden.
Because if you will take a look at the code of the strategy you will understand that there is no reason for the exception thrown by the API. Because the code is asking the API for last (SHIFT=0) value of EMA, there is no time definition used.
Interesting thing:
Please take a look at the picture img3.png
This is comparasion of pricechart generated by BACKTEST and live pricechart moved to the same time period.
As you can see - the backtest is displaying little bit differente pricechart comparing with the live price chart.
I fully understand that if the live price chart starts the calculation of the range bars from time specific time, and the backtest starts the calculation of the chart from different time, then this difference is fully logical and it is no bug (Because it is based on the logic principles of the "range bars").
So I really understand that this is no "error" and this might be caused just by the fact that live price chart has different starting point of his "range bars calculation" comparing with the chart generated by the backtest.
But still - this might be very confusing for the user. Because if the user is trading some range bar strategy on live charts for a week, and at the end of the week he will make a backtest of the week, then he receives completely different results.
Because if some strategy is based on values of some indicators, and indicators are calculated from the values of the bars then:
If you are trading one strategy on live charts and have some results of your trading and later you are doing a backtest of the same trading strategy on the same time period but the rangeBar chart generated by the "historical tester" is different comparing with the live chart, then also the indicator values are different and then also the results of your trading will be different.
So all what I want to tell is this:
If there will be some FIXED point from where the calculation of the range bars starts - this problem would be solved. If historical tester will simulate the range bars from some FIXED historical point then the chart generated by this historical tester will be exactly the same like the live chart (which starts his range bar calculation from the same historical point).
I hope my description is understandable

And last interesting thing which could help you to find out where the bug in API is hidden is this:
If you run backtest with visual mode, but you will TURN OFF the equity, balance etc - your visual mode should be just pure range bar graph - then the strategy is running thats true, but the price chart is totally EMPTY. Its BLANK.
Take a look at the IMG4.png
This will maybe also help you with the looking for the bug.
I am looking forward for your response.
Thank you, have a nice day.
Best regards,
Marek Lorenc
LorencSoftware