Examples

Maximum high

The strategy on its start gets last 10 bars over the designated period and gets the maximum High price of those bars.

 @Override
    public void onStart(IContext context) throws JFException {
        console = context.getConsole();
        history = context.getHistory();

        long lastTickTime = history.getLastTick(instrument).getTime();
        long lastBarTime = history.getBarStart(period, lastTickTime);
        List<IBar> bars = history.getBars(instrument, period, OfferSide.BID, Filter.NO_FILTER, 10, lastBarTime, 0);

        double maxHigh = Double.MIN_VALUE;
        for(IBar bar : bars){
            if(maxHigh < bar.getHigh()){
                maxHigh = bar.getHigh();
            }
            console.getOut().println(bar);
        }
        console.getOut().println("bars: " + bars.size() + " high=" + maxHigh);        
    }

High and low with supplement lines

Consider an elaborated previous example which also finds the lowest low value over the given candle interval. The strategy also draws two price markers that correspond to the prices and two time markers to show the time interval.

high_low_bars_with_suplement_lines.jpg

Consider the strategy source code:

 @Override
    public void onStart(IContext context) throws JFException {
        console = context.getConsole();
        history = context.getHistory();

        long lastTickTime = history.getLastTick(instrument).getTime();
        long lastBarTime = history.getBarStart(period, lastTickTime);
        List<IBar> bars = history.getBars(instrument, period, OfferSide.BID, Filter.NO_FILTER, 10, lastBarTime, 0);

        double maxHigh = Double.MIN_VALUE;
        double minLow = Double.MAX_VALUE;
        for(IBar bar : bars){
            if(maxHigh < bar.getHigh()){
                maxHigh = bar.getHigh();
            }
            if(minLow > bar.getLow()){
                minLow = bar.getLow();
            }
            console.getOut().println(bar);
        }

        console.getOut().println(String.format("over last %s bars - high=%.5f, low=%.5f", bars.size(), maxHigh, minLow));

        chart = context.getChart(instrument);
        if(chart == null){
            console.getWarn().println("Chart is not open, will not plot lines");
            return;
        }

        IChartObjectFactory factory = chart.getChartObjectFactory();

        IHorizontalLineChartObject highLine = factory.createPriceMarker("highLine", maxHigh);
        IHorizontalLineChartObject lowLine = factory.createPriceMarker("minLow", minLow);
        IVerticalLineChartObject oldestLine = factory.createTimeMarker("oldestBarTime", bars.get(0).getTime());
        IVerticalLineChartObject latestLine = factory.createTimeMarker("latestBarTime", bars.get(bars.size() - 1).getTime());

        highLine.setColor(Color.GREEN);
        lowLine.setColor(Color.RED);

        oldestLine.setLineWidth(3);  
        oldestLine.setOpacity(0.5f);
        oldestLine.setLineStyle(LineStyle.DASH);
        latestLine.setLineWidth(3);
        latestLine.setOpacity(0.5f);
        latestLine.setLineStyle(LineStyle.DASH);

        chart.add(highLine);
        chart.add(lowLine);
        chart.add(oldestLine);
        chart.add(latestLine);          
    }
    //... 
    public void onStop() throws JFException {
        chart.removeAll();        
    }
}

HistoricalHighLowWithLog2.java

Max volumes by period

Consider a strategy which finds the 1 hour bar with the maximum value for 7 days, starting from a given date. Also the strategy demonstrates how to retrieve historical data in chunks, instead of loading all the data at once (which in more extreme cases might lead to performance problems or even an Out of memory exception).

package jforex.history;

import com.dukascopy.api.*;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import java.util.TimeZone;

/**
 * The strategy demonstrates how to retrieve historical data in chunks, instead of loading all the data at once.
 * In particular, strategy on its start prints historical volumes of 1 hour bars over 7 days following
 * the designated start date in startDateStr.
 *
 */

public class HistoricalVolumesInChunks implements IStrategy {

    private IConsole console;
    private IHistory history;

    private Period period = Period.ONE_HOUR;
    private Instrument instrument = Instrument.EURUSD;

    private SimpleDateFormat gmtSdfShort = new SimpleDateFormat("yyyy-MM-dd");
    private SimpleDateFormat gmtSdfLong = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private String startDateStr = "2011-07-20";
    private int days = 7;

    @Override
    public void onStart(IContext context) throws JFException {
        console = context.getConsole();
        history = context.getHistory();

        gmtSdfShort.setTimeZone(TimeZone.getTimeZone("GMT"));
        gmtSdfLong.setTimeZone(TimeZone.getTimeZone("GMT"));

        long startDate = 0;
        try {
            startDate = gmtSdfShort.parse(startDateStr).getTime();
        } catch (ParseException e1) {
            //stop strategy
            console.getErr().println(e1);
            context.stop();
            return;
        }

        for (int day = 0; day < days; day++ ){
            long startTime = 0;
            long endTime = 0;

            startTime = startDate + Period.DAILY.getInterval() * day;
            endTime = startTime + Period.DAILY.getInterval() - 1; //minus 1 milli to be in the same day

            long startBarTime = history.getBarStart(period, startTime);
            long endBarTime = history.getBarStart(period, endTime);
            List<IBar> bars = history.getBars(instrument, period, OfferSide.BID, startBarTime, endBarTime);

            double maxVolume = 0;
            long maxVolumeTime = 0;
            for(IBar bar : bars){
                if(maxVolume < bar.getVolume()){
                    maxVolume = bar.getVolume();
                    maxVolumeTime = bar.getTime();
                }
                //print(bar);
            }

            print(gmtSdfShort.format(startTime) + " max volume of " +period+ " bars: " + maxVolume + " at: " + gmtSdfLong.format(maxVolumeTime));
        }

    }

    @Override
    public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {}

    private void print(Object o) {
        console.getOut().println(o);
    }

    @Override
    public void onTick(Instrument instrument, ITick tick) throws JFException {    }
    @Override
    public void onMessage(IMessage message) throws JFException {    }
    @Override
    public void onAccount(IAccount account) throws JFException {    }
    @Override
    public void onStop() throws JFException {    }

}

HistoricalVolumesInChunks.java

Order transaction history

In the following example strategy as transactions we consider order operations that affect account balance:

  • order fill (checked with active orders),
  • order close (checked with history orders).

The following strategy shows how to retrieve:

  • Last transaction time and amount.
  • Transactions over a given time period.
package jforex.strategies;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;

import com.dukascopy.api.*;

/** 
 * The strategy prints order transaction history over the selected period.
   Order changes that we consider as transactions are:
     - order fill (checked with active orders)
     - order close (checked with history orders)
   since they are the only ones that affect balance.
*/
public class LastTransaction implements IStrategy {

    @Configurable("Transaction history period")
    public Period historyPeriod = Period.DAILY;

    private IHistory history;
    private IConsole console;
    private IEngine engine;

    @SuppressWarnings("serial")
    public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS") {
        {
            setTimeZone(TimeZone.getTimeZone("GMT"));
        }
    };
    public static DecimalFormat df = new DecimalFormat("0.000##");

    @Override
    public void onStart(IContext context) throws JFException {
        history = context.getHistory();
        engine = context.getEngine();
        console = context.getConsole();

        List<IOrder> activeOrders = engine.getOrders();
        List<IOrder> historyOrders = new ArrayList<IOrder>();
        long from = System.currentTimeMillis() - historyPeriod.getInterval();
        long to = System.currentTimeMillis();
        for (Instrument instrument : context.getSubscribedInstruments()){
            historyOrders.addAll(history.getOrdersHistory(instrument, from, to));
        }

        print("History (Closed/Cancelled) order count from " + sdf.format(from) + " to " + sdf.format(to) + " is " + historyOrders.size());
        print("Active (Created/Opened/Filled) order count at " + sdf.format(to) + " is " + activeOrders.size());

        //get all transactions within a period
        for (IOrder order : activeOrders){
            if(order.getFillTime() > from && order.getFillTime() < to){
                print(order.getId() + " fill transaction time: " + sdf.format(order.getFillTime()) + " amount: " + df.format(order.getAmount()));
            }
        }
        for (IOrder order : historyOrders){
            if(order.getCloseTime() > from && order.getCloseTime() < to){
                print(order.getId() + " close transaction time: " + sdf.format(order.getCloseTime()) + " amount: " + df.format(order.getAmount()));
            }
        }

        //get last transaction        
        long timeTrans = 0;
        IOrder orderTrans = null;
        for (IOrder order : activeOrders){
            if(order.getFillTime() > timeTrans){
                timeTrans = order.getFillTime();
                orderTrans = order;
            }
        }
        for (IOrder order : historyOrders){
            if(order.getCloseTime() > timeTrans){
                timeTrans = order.getCloseTime();
                orderTrans = order;
            }
        }
        if (orderTrans == null){
            print("No trasactions were found within the selected period");
        } else {
            print("Last transaction time: " + sdf.format(timeTrans) + " amount: " + df.format(orderTrans.getAmount()));
        }

        context.stop();
    }

    private void print(Object o){
        console.getOut().println(o);
    }

    @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 {}

}

LastTransaction.java

Custom Renko Aggregation

For performance reasons the platform of bigger renko brick sizes (greater than 4 pips) does not assemble the bricks from ticks, but rather from bars. The following strategy shows how one can assemble one's own renko bars from ticks for multiple instruments over a custom period and write them to a file - renkos for each instrument get written into a separate file. The strategy uses asynchronous tick reading methods, meaning that all data reads take place in parallel - in different threads.

Also the used approach is memory-efficient, since the strategy works only with one tick and one renko bar at a time, as opposed to cases, when ticks get loaded by IHistory.getTicks over big intervals. Note that the renko assembling algorithm is simplified, thus the results differ from the ones of the platform.

package jforex.data.async;

import java.awt.Color;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;

import com.dukascopy.api.*;
import com.dukascopy.api.drawings.IRectangleChartObject;
import com.dukascopy.api.feed.IRenkoBar;

/**
 * The following strategy shows how one can assamble his own renko bars
 * for multiple instruments over a custom period and write them to a file -
 * renkos for each instrument get written into a separate file.
 * 
 * The strategy uses asynchronous reading methods, meaning that all data
 * reads take place in parallel - in different threads.
 * 
 * Also the used approach is memory-efficient, since the strategy works
 * only with one tick and one renko bar at a time, as opposed to cases,
 * when ticks get loaded by IHistory.getTicks over big intrevals.
 * 
 * Note that the renko assembling algorithm is simplified, thus the results
 * differ from the ones of the platorm.
 *
 */
@RequiresFullAccess
public class ReadTicksForRenkoToFile implements IStrategy {

    private IHistory history;
    private IConsole console;
    private IContext context;

    @SuppressWarnings("serial")
    private final SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss") {
        {
            setTimeZone(TimeZone.getTimeZone("GMT"));
        }
    };

    @Configurable("from dd-MM-yyyy HH:mm:ss")
    public String fromStr = "18-07-2012 00:00:00";
    @Configurable("to dd-MM-yyyy HH:mm:ss")
    public String toStr = "20-07-2012 10:00:00";
    @Configurable("")
    public OfferSide side = OfferSide.BID;
    @Configurable("renko brick size")
    public int brickSize = 10;
    @Configurable("log to file (otherwise to messages tab)")
    public boolean logToFile = true;
    @Configurable("plot to chart (as rectangles)")
    public boolean plotOnChart = true;

    private final Set<Instrument> instruments = new HashSet<Instrument>(Arrays.asList(new Instrument[] { 
            Instrument.CHFJPY,
            Instrument.EURJPY, 
            Instrument.EURUSD, 
            Instrument.USDJPY }
    ));

    private Map<Instrument, Boolean> insrtDataLoaded = new HashMap<Instrument, Boolean>();
    private List<MyTickListener> tickListeners = new ArrayList<MyTickListener>();

    @Override
    public void onStart(IContext context) throws JFException {
        history = context.getHistory();
        console = context.getConsole();
        this.context = context;

        context.setSubscribedInstruments(instruments, true);

        long from = 0, to = 0;
        try {
            from = sdf.parse(fromStr).getTime();
            to = sdf.parse(toStr).getTime();
        } catch (ParseException e) {
            console.getErr().println(e + " on date parsing. The straetgy will stop.");
            context.stop();
        }

        for (Instrument instrument : instruments) {
            insrtDataLoaded.put(instrument, false);
                MyTickListener tickListener = logToFile 
                    ? new MyTickListener(new File (String.format("renkos_%s_%spips_%s_to_%s.txt",instrument,brickSize,fromStr,toStr).replaceAll("[-\\/:]", "_")))
                    : new MyTickListener(instrument);

            tickListeners.add(tickListener);
            history.readTicks(instrument, from, to, tickListener, new MyLoadingProgressListener(instrument) );
        }

    }

    public class MyTickListener implements LoadingDataListener {        

        MockRenko renko = null;
        MockRenko prevRenko = null;
        private PrintStream printStream;
        private FileOutputStream fileOutputStream;

        public MyTickListener (File logFile){
            try {        
                fileOutputStream = new FileOutputStream(logFile, false);
                console.getInfo().println("log to file: " + logFile.getAbsolutePath());
                printStream = new PrintStream(fileOutputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public MyTickListener(final Instrument instrument) {
            printStream = new InstrPrefixedStream(instrument, console.getOut());
        }        

        @Override
        public void newTick(Instrument instrument, long time, double ask, double bid, double askVol, double bidVol) {

            double price = side == OfferSide.BID ? bid : ask;
            double renkoHeight = instrument.getPipValue() * brickSize;
            double volume = side == OfferSide.BID ? bidVol : askVol;
            if (renko == null) {
                renko = new MockRenko(price, volume, time, renkoHeight);
                return;
            }
            if (renko.high < price) {
                renko.high = price;
            }
            if (renko.low > price) {
                renko.low = price;
            }
            renko.close = price; 
            renko.vol += volume;
            renko.endTime = time;
            renko.tickCount++;

            if(renko.isComplete()){
                renko.postProcess();

                printStream.println(renko);
                if(plotOnChart){
                    plotOnChart(renko, instrument);
                }

                //new bar start at the same price, but on the next millisecond
                renko = MockRenko.getNextRenko(renko);
                return;
            }
        }

        @Override
        public void newBar(Instrument instrument, Period period, OfferSide side, long time, double open, double close, double low,
                double high, double vol) {
            // no bars expected

        }

        private void plotOnChart(MockRenko renko, Instrument instrument){
            IChart chart = context.getChart(instrument);
            if (chart != null) {
                IRectangleChartObject obj = chart.getChartObjectFactory().createRectangle(UUID.randomUUID().toString(),
                        renko.startTime, renko.low, renko.endTime, renko.high);
                obj.setColor(renko.close > renko.open ? Color.GREEN : Color.RED);
                obj.setText(String.format("O=%.5f, C=%.5f, H=%.5f, L=%.5f", renko.open, renko.close, renko.high, renko.low));
                chart.addToMainChart(obj);
            }
        }

        public void dispose(){
            printStream.close();
        }
    }    

    /**
     * Input stream which prefixes outputs with an instrument
     */
    public class InstrPrefixedStream extends PrintStream {

        private final Instrument instrument;

        public InstrPrefixedStream(Instrument instrument, PrintStream out) {
            super(out);
            this.instrument = instrument;
        }
        @Override
        public void println(Object x) {
            super.println(instrument + " " + x);
        }  

    }

    public class MyLoadingProgressListener implements LoadingProgressListener {

        private final Instrument instrument;
        public MyLoadingProgressListener(Instrument instrument){
            this.instrument = instrument;
        }

        @Override
        public void dataLoaded(long start, long end, long currentPosition, String information) {}

        @Override
        public void loadingFinished(boolean allDataLoaded, long start, long end, long currentPosition) {
            print("loadingFinished: instrument=%s, allDataLoaded=%s, start=%s, end=%s, currentPosition=%s", instrument,
                    allDataLoaded, sdf.format(start), sdf.format(end), sdf.format(currentPosition));
            insrtDataLoaded.put(instrument, true);
        }

        @Override
        public boolean stopJob() {
            return false;
        }
    }

    private void print(String format, Object... args) {
        console.getOut().println(String.format(format, args));
    }

    @Override
    public void onTick(Instrument instrument, ITick tick) throws JFException {
        if (!insrtDataLoaded.values().contains(Boolean.FALSE)) {
            print("All renkos loaded, stopping the strategy.");
            for(MyTickListener tickListener : tickListeners){
                tickListener.dispose();
            }
            context.stop();
        }
    }

    @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 {
    }

}

class MockRenko implements IRenkoBar {

    public double open;
    public double close;
    public double low;
    public double high;
    public double vol;
    public long startTime;
    public long endTime;
    public int tickCount;

    private MockRenko prevRenko;
    private double height;

    @SuppressWarnings("serial")
    private final SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss") {
        {
            setTimeZone(TimeZone.getTimeZone("GMT"));
        }
    };

    public static MockRenko getNextRenko(MockRenko prevRenko) {
        MockRenko renko = new MockRenko(prevRenko.close, 0, prevRenko.endTime + 1, prevRenko.height);
        renko.prevRenko = prevRenko;
        return renko;
    }

    public MockRenko(double price, double volume, long time, double height) {
        this.height = height;
        open = close = low = high = getRoundedPrice(price);
        vol = volume;
        startTime = time;
        endTime = time;
        tickCount = 1;
    }       

    public boolean isComplete(){
        return isGreenComplete() || isRedComplete();
    }

    private boolean isGreenComplete(){
        return prevRenko == null 
            ? high - open >= height
            : high - prevRenko.high >= height;
    }

    private boolean isRedComplete(){
        return prevRenko == null 
            ? open - low >= height
            : prevRenko.low - low >= height;
    }

    public void postProcess(){
        //on trend change high-low difference is double the renko height - adjust it here
        if(isGreenComplete()){
            low = high - height;
        } else {
            high = low + height;
        }
        //make "solid" bricks with prices rounded to the brick height
        low = getRoundedPrice(low);
        high = getRoundedPrice(high);
        close = getRoundedPrice(close);
        open = getRoundedPrice(open);
    }

    private double getRoundedPrice(double price){
        //rounded to the closest pip value that is divisible with brickSize
        double delta1 = price % height;
        double delta2 = height - price % height;
        double priceRounded = delta1 <= delta2
                ? price - delta1
                : price + delta2;
        return priceRounded;
    }

    @Override
    public double getOpen() {
        return open;
    }

    @Override
    public double getClose() {
        return close;
    }

    @Override
    public double getLow() {
        return low;
    }

    @Override
    public double getHigh() {
        return high;
    }

    @Override
    public double getVolume() {
        return vol;
    }

    @Override
    public long getTime() {
        return startTime;
    }

    @Override
    public String toString() {            
        return String.format("StartTime: %s EndTime: %s O: %.5f C: %.5f H: %.5f L: %.5f V: %.5f TickCount: %s",
                sdf.format(startTime), sdf.format(endTime), open, close, high, low, vol, tickCount);
    }

    @Override
    public long getEndTime() {
        return endTime;
    }

    @Override
    public long getFormedElementsCount() {
        return tickCount;
    }

    @Override
    public IRenkoBar getInProgressBar() {
        return null;
    }

    @Override
    public Double getWickPrice() {
        return null;
    }
}

ReadTicksForRenkoToFile.java

The information on this web site is provided only as general information, which may be incomplete or outdated. Click here for full disclaimer.