IHistory interface
The IHistory interface, that you receive with IContext.getHistory(), gives you various methods that allow you access Dukascopy's historical data such as prices and orders. It's methods can be separated in to four main blocks:
- Methods on how to access the latest feed data such as getTimeOfLastTick and getStartTimeOfCurrentBar
- Methods on how to to access historical feed data such as getBars and readBars
- Methods on how to access historical orders data - getOrdersHistory, readOrdersHistory
- Methods that help calculate the time for a specific bar or period of time
All methods in the IHistory interface are thread safe and can be called upon from multiple threads without external synchronization.
Methods to access the latest feed data
Methods from the first block are mostly used to get the latest tick or it's time. Because of the nature of the Forex market, where prices can change several times in a second, it's is possible to have a situation where a tick has passed to the onTick method and is no longer the latest tick available to the system. This can happen during the processing of an onTick method which is rather long: such as requesting historical data that is not yet locally cached, sending new orders and waiting for their acceptance by the server or any other operation that takes time. In such case get the LastTick(Instrument) it can be called upon to get latest available tick.
double bid = tick.getBid(); order = engine.submitOrder("tenSecondsOrder", instrument, IEngine.OrderCommand.PLACE_BID, 0.1, bid - instrument.getPipValue() * 50, 0, 0, 0, tick.getTime() + 10000); while (order.getState() != IOrder.State.FILLED && order.getState() != IOrder.State.CANCELED && !context.isStopped()) { //wait for 30 seconds, by this time order should be filled or canceled order.waitForUpdate(30000); } long currentTime = history.getTimeOfLastTick(instrument); console.getOut().println((currentTime - tick.getTime()) + " milliseconds difference between tick passed in method and latest tick"); context.stop();
Another possible case is accessing the latest feed data through the method that are not the onTick or onBar methods, but the IContext method.
double ask = history.getLastTick(Instrument.EURUSD).getAsk(); double pip = Instrument.EURUSD.getPipValue(); order = engine.submitOrder("myorderlabel", Instrument.EURUSD, IEngine.OrderCommand.BUY, 0.1, 0, -1, ask - 300 * pip, ask + 300 * pip);
Methods to access a historical feed data
Methods in this section can be separated in to two groups: asynchronous and synchronous loading.
Methods that load their data in the same thread synchronously start with the get prefix: getBar, getTicks, getBars. Even if the method requires time to access the server, download history files in the local cache, it will all be done in the same thread blocking strategy execution for some time. Also methods getBars and getTicks return List with bar and ticks correspond, thus taking some amount of the memory. It's not recommended to call upon this methods knowing that it could return thousands or even millions of ticks and cause OutOfMemoryException. Though normally it's ok to call getBar with shift parameter in 0-10 range, it's very unlikely it will require server access to get the data.
long prevBarTime = history.getPreviousBarStart(Period.TEN_SECS, tick.getTime());
List<IBar> bars = history.getBars(instrument, Period.TEN_SECS, OfferSide.BID, history.getTimeForNBarsBack(Period.TEN_SECS, prevBarTime, 10), prevBarTime);
Asynchronous methods start with "read" prefix and require a bit more coding. You will need to implement two listeners that will be called upon in another thread passing in ticks or bars and notifying you when data loading is complete. This method will return immediately after execution most likely before any data is received in listeners. This is useful when following logic doesn't require the data requested, or that logic can be separated and executed after listener is notified of data loading completion.
history.readBars(Instrument.EURUSD, Period.TEN_SECS, OfferSide.BID, history.getBarStart(Period.DAILY, tick.getTime()) - Period.DAILY.getInterval(), // yesterday start history.getBarStart(Period.DAILY, tick.getTime()) - Period.TEN_SECS.getInterval(), // yesterday end new LoadingDataListener() { public void newTick(Instrument instrument, long time, double ask, double bid, double askVol, double bidVol) { //no ticks expected, because we are loading bars } public void newBar(Instrument instrument, Period period, OfferSide side, long time, double open, double close, double low, double high, double vol) { ++numberOfBarsLoaded; } }, new LoadingProgressListener() { public void dataLoaded(long startTime, long endTime, long currentTime, String information) { console.getOut().println(information); } public void loadingFinished(boolean allDataLoaded, long startTime, long endTime, long currentTime) { if (allDataLoaded) { console.getOut().println("All data loaded succesfully, number of bars loaded: " + numberOfBarsLoaded); context.stop(); } else { console.getOut().println("For some reason loading failed or was canceled by the user"); context.stop(); } } public boolean stopJob() { return false; } });
By the type of data requested methods can by separated in to three types:
- Methods that return only one bar based on the shift parameter. There is only one such method - getBar(Instrument instrument, Period period, OfferSide side, int shift). Shift equals 0 and refers to the current candle that's not is not yet fully formed, 1 - latest fully formed candle, 2 - latest - 1 candle
- Methods that accept the start and end time of the interval that needs to be loaded. This method has long from, long to parameters. Like any other time parameters in the IHistory interface they refer to the first and last candle that will be loaded (including candle that starts at the "to" time)
- Methods that accept the number of candles before or/and after some time. This method also has a Filter parameter and is able to filter flat candles in returned data. A Time parameter refers to the last candle in "before" number of candles, and before first candle in "after" number of candles.
Methods to access the historical orders data
This section consist only of two methods getOrdersHistory and readOrdersHistory, that do the same in general but are synchronous and asynchronous respectively. The reasons for using synchronous or asynchronous method are mostly the same as for feed data access methods, but with one big difference: there is no local cache for orders! This is done for security reasons and so that every request for the orders will result in a request to the server. Because of that every call to the getOrdersHistory will block the calling thread for the time needed to request orders from the server.
There are also two limitations linked to the use of these methods. First, these methods should not be called upon more than once within a 5 second interval between calls, or else exception will be thrown.
Second, when the loading is started and one of the methods is in process, another call for the orders will throw an exception even if 5 seconds passed after the previous call.
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); format.setTimeZone(TimeZone.getTimeZone("GMT")); console.getOut().println("reading orders"); history.readOrdersHistory(instrument, format.parse("2009-04-15 09:23:00").getTime(), format.parse("2009-04-15 09:29:00").getTime(), new LoadingOrdersListener() { public void newOrder(Instrument instrument, IOrder orderData) { console.getOut().println(orderData); } }, new LoadingProgressListener() { public void dataLoaded(long startTime, long endTime, long currentTime, String information) { } public void loadingFinished(boolean allDataLoaded, long startTime, long endTime, long currentTime) { console.getOut().println("All orders loaded"); } public boolean stopJob() { return false; } });
Time calculation functions
Every function that requires time of the bar as it's parameter, checks that parameter is the start time of the bar. For example function getBars with period TEN_SECS will throw an exception if called with time "2009-01-01 14:45:02", because 2 seconds is the wrong start time for ten seconds candle. The correct time should be 00 seconds or 10 seconds and so on. For easy calculation there are various methods like getBarStart, getNextBarStart or getTimeForNBarsBack, they are especially helpful when the start time is not just the start of the day, hour or minute (weekly candles for example). Excerpt from GetBarsExample.java has two uses of these functions:
long prevBarTime = history.getPreviousBarStart(Period.TEN_SECS, tick.getTime());
List<IBar> bars = history.getBars(instrument, Period.TEN_SECS, OfferSide.BID, history.getTimeForNBarsBack(Period.TEN_SECS, prevBarTime, 10), prevBarTime);