Dukascopy
 
 
Wiki JStore Search Login

Attention! Read the forum rules carefully before posting a topic.

    Try to find an answer in Wiki before asking a question.
    Submit programming questions in this forum only.
    Off topics are strictly forbidden.

Any topics which do not satisfy these rules will be deleted.

"Merge All" strategy from Dukascopy errors
 Post subject: "Merge All" strategy from Dukascopy errors Post rating: 0   Post Posted: Tue 09 Jul, 2013, 19:25 

User rating: 0
Joined: Mon 19 Dec, 2011, 14:21
Posts: 12
Location: SwitzerlandSwitzerland
The strategy found in Jstore to merge all open positions doesn't work properly. I always receive this error:

18:16:21 com.dukascopy.api.JFException: Label not unique(code 1). (Order already exists) [mergeTarget]:label=mergeTarget;getId()=52531326;groupId=52531326;openingOrderId=205376552;pendingOrderId=null;pendingOrderCommand=null;parentOrderId=205376552;tpOrderId=null;slOrderId=null;state=FILLED;instrument=EUR/JPY;openPrice=129.064;requestedAmount=0.002;amount=0.002;lastServerRequest=NONE;awaitingResubmit=false;localCreationTime=1373393736703;
fill history:
[time=2013-07-09 18:15:36, price=129,06400, amount=0,00] @ jforex.samples.MergeAll.mergeOrders(MergeAll.java:77)
/**
 *
* @author Denis Larka
* Merge all positions
 */
package jforex.samples;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.dukascopy.api.*;

/**
 *
 */

public class MergeAll implements IStrategy {

    private IEngine engine = null;
    private boolean mergeInProgress = false;

    public void onStart(IContext context) throws JFException {

        this.engine = context.getEngine();

    }

    public void onAccount(IAccount account) throws JFException {

    }

    public void onMessage(IMessage message) throws JFException {
        if (message.getType() == IMessage.Type.ORDERS_MERGE_OK || message.getType() == IMessage.Type.ORDERS_MERGE_REJECTED) {
            mergeInProgress = false;
        }
    }

    public void onStop() throws JFException {
    }

    public void onTick(Instrument instrument, ITick tick) throws JFException {

        megaMerge();
    }

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

    private void megaMerge() throws JFException {
        if (!mergeInProgress) {
            List<IOrder> allPositions = engine.getOrders();

            //collecting all instrument we have opened
            Map<Instrument, List<IOrder>> allByInstruments = new HashMap<Instrument, List<IOrder>>();
            for (IOrder order : allPositions) {
                Instrument instr = order.getInstrument();
                List<IOrder> orders = allByInstruments.get(instr);
                if (orders == null) {
                    orders = new ArrayList<IOrder>();
                    allByInstruments.put(instr, orders);
                }
                orders.add(order);
            }

            //now merging

            for (Instrument instr : allByInstruments.keySet()) {
                List<IOrder> list = allByInstruments.get(instr);
                if (list.size() > 1) {
                    mergeOrders(list);
                    mergeInProgress = true;
                }
            }

        }
    }

    public void mergeOrders(List<IOrder> positions) throws JFException {

        if (positions == null || positions.size() == 0) {
            return;
        }
        engine.mergeOrders("mergeTarget", positions.toArray(new IOrder[0]));

    }

}


Do you know what's wrong ?
Is it because I take too many positions too fast ?


 
 Post subject: Re: "Merge All" strategy from Dukascopy errors Post rating: 0   Post Posted: Tue 09 Jul, 2013, 20:39 
User avatar

User rating: 98
Joined: Mon 23 Jul, 2012, 02:02
Posts: 656
Location: United States, Durham, NC
Unless I missed something.....

I don't know what the specific Bug is, but...

A quick scan over the program, and it seems to me that driving the "megamerge" operation
from a tick callback is a bit too extreme.

There is usually no need for "instantaneous merge" from one tick to the next...

I would certainly have designed the software to perform the megamerge a bit less frequently....

Just my 2 cents,
HyperScalper


 
 Post subject: Re: "Merge All" strategy from Dukascopy errors Post rating: 1   Post Posted: Tue 09 Jul, 2013, 20:44 
User avatar

User rating: 98
Joined: Mon 23 Jul, 2012, 02:02
Posts: 656
Location: United States, Durham, NC
Also, it doesn't look to me like there is any consideration given to Take Profit or Stop Loss situations
on positions.

I thought they needed to be removed in order for Merging to take place. I certainly check
for that in my own auto merge code.

And if the merge label is always "mergeTarget" just maybe there is a label conflict with
an existing "mergeTarget". But maybe not.......

(That's what you get for using somebody else's software :) JUST KIDDING

fwiw,
HyperScalper


 
 Post subject: Re: "Merge All" strategy from Dukascopy errors Post rating: 2   Post Posted: Tue 09 Jul, 2013, 20:49 
User avatar

User rating: 98
Joined: Mon 23 Jul, 2012, 02:02
Posts: 656
Location: United States, Durham, NC
How about:

int mergeTargetSuffix = 1;

..... then in void mergeOrders, use a new mergeTargetN label each successive merge.

engine.mergeOrders("mergeTarget"+mergeTargetSuffix++ , positions.toArray(new IOrder[0]));

But then I'm not sure there's a real conflict.

Don't see much, if any Exception handling, so guess it just crashes?

HyperScalper


 
 Post subject: Re: "Merge All" strategy from Dukascopy errors Post rating: 2   Post Posted: Tue 09 Jul, 2013, 22:22 
User avatar

User rating: 164
Joined: Mon 08 Oct, 2012, 10:35
Posts: 676
Location: NetherlandsNetherlands
hyperscalper is right,

The label is always 'mergeTarget', so after the 1st merged order you cannot create another one with the same label.

He is also right about TP and SP. They have to be cleared before merging.

There is a pretty detailed wiki page about merging, with examples. Check it out here.
Note: one of the examples in the wiki also have this label issue, but I think you will figure it out now...


 
 Post subject: Re: "Merge All" strategy from Dukascopy errors Post rating: 0   Post Posted: Wed 10 Jul, 2013, 20:29 

User rating: 0
Joined: Mon 19 Dec, 2011, 14:21
Posts: 12
Location: SwitzerlandSwitzerland
Hyperscalper Thank you, I have added your code and the strategy runs perfectly. The MergeAll strategy is able to merge the positions even if new positions are added very fast by the strategy. And even with many errors messages when it's going to fast, it always run and finally merge very fast. So this is perfect.

The only limitation for me was that I wanted the strategy to merge only the long positions into one and the short position into another, That means I keep a Long position an a short position.
The heart of my strategy doesn't run properly. It takes many positions on ticks and very fast and has to merge them but the merging doesn't work and I can't find the cause....

My strategy has no Take Profit and no Stop Loss but the standard Iorder submitAndMergeOrder with WaitForUpdate(2000) that I used to take never run properly. You always have many orders that can't be merged because of various errors of the the strategy (even when you trade a system Onbar). So I am searching for another solution. I want to run my system on 5 minutes timeframe and alway keep a long position and a short position. I run the same strategy on 1Hour time frame and also keep a long position and a short position

I give the code here without the logic of the buy and sell and perhaps an expert can help and see very fast where the problem lies. I think the logic of adding all the long positions in a basket and then merge them when there is for example 5 positions i a good idea. The short positions are put in another basket and then merged.


I have searched many hours in the wiki, in the Jstore, in this forum and didn't find a bit of code to help me. I am a super novice in JAVA, so I learn the hard way...

If someone would help, it would be great !
Thank you.

package jforex.strategies.indicators;

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

import com.dukascopy.api.*;
import com.dukascopy.api.IEngine.OrderCommand;
import com.dukascopy.api.IIndicators.AppliedPrice;
import com.dukascopy.api.indicators.IIndicator;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.TimeUnit;

public class TickHedgeM5 implements IStrategy {
   
    private IEngine engine;
    private IConsole console;
    private IHistory history;
    private IIndicators indicators;
    private IAccount account;
    private int counter = 0;
    private List<IOrder> longOrders = new LinkedList<IOrder>();
    private List<IOrder> shortOrders = new LinkedList<IOrder>();
   
    private JFUtils utils;
       
   
   
    @Configurable("Max # of long orders")
    public int maxLongCount = 5;
    @Configurable("Max # of short orders")
    public int maxShortCount = 5;
     @Configurable("Trade Long Orders")
    public boolean tradeLong = true;
    @Configurable("Trade Short Orders")
    public boolean tradeShort = true;
    @Configurable("Instrument")
    public Instrument instrument = Instrument.EURUSD;
   
   
    public OfferSide offerSide = OfferSide.BID;
    @Configurable("Slippage")
    public double slippage = 0;
    @Configurable("Amount")
    public double amount = 0.001;
   
    public Filter filter = Filter.ALL_FLATS;
    public AppliedPrice appliedPrice = AppliedPrice.CLOSE;
    public int timePeriod = 14;
   
    @Configurable("Open hour")
    public int openHour = 6;
    @Configurable("Open min")
    public int openMin = 0;
    @Configurable("Close hour")
    public int closeHour = 15;
    @Configurable("Close min")
    public int closeMin = 30;

    @Override
    public void onStart(IContext context) throws JFException {
        this.console = context.getConsole();
        this.indicators = context.getIndicators();
        this.history = context.getHistory();
        this.engine = context.getEngine();
       
        IChart chart = context.getChart(instrument);
        if (chart != null) {           
            chart.addIndicator(indicators.getIndicator("CCI"), new Object[]{timePeriod});
        }
    }
   
    @Override
    public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
        if (period != this.selectedPeriod || instrument != this.instrument) {
            return;
        }
        }
        public void onTick(Instrument instrument, ITick tick) throws JFException {
        if (instrument != this.instrument) {
            return;
        }
       
         if (!isRightTime(tick.getTime(), openHour, openMin, closeHour, closeMin)) {
            return;
        }
       
        Vector<IOrder> newOrders;
        Vector<IOrder> removeOrders;
       
        removeOrders = new Vector<IOrder>(maxLongCount);
        for(IOrder order : longOrders) {
            if (!isActive(order)) {
                removeOrders.add(order);
            }
        }
        longOrders.removeAll(removeOrders);
       
        removeOrders = new Vector<IOrder>(maxShortCount);
        for(IOrder order : shortOrders) {
            if (!isActive(order)) {
                removeOrders.add(order);
            }
        }
        shortOrders.removeAll(removeOrders);
       

               
       // SIGNAL
        boolean sellSign1 = false;
        boolean buySign1 = false;
       
 
 // BUY AND SELL CONDITIONS COME HERE
 
 
     // PLACE ORDERS
        if (buySign1) {

            if(longOrders.size() == 0) {
                longOrders.add(submitOrder(OrderCommand.BUY));
               
            } else {
               
                // for same signal direction
                // open new order and merge with it
                removeOrders = new Vector<IOrder>();
                newOrders = new Vector<IOrder>();

                for(IOrder order : longOrders) {
                 
                        IOrder newOrder = submitOrder(OrderCommand.BUY);
                       
                        IMessage message = newOrder.waitForUpdate(2, TimeUnit.SECONDS);
                       
            // order is FILLED on successful merge with amount > 0
                        if (newOrder.getState() == IOrder.State.FILLED) {
                            IOrder mergedOrder = engine.mergeOrders(getLabel(instrument), order, newOrder);
                            message = mergedOrder.waitForUpdate(2, TimeUnit.SECONDS);

                            // order is FILLED on successful merge with amount > 0
                            if (mergedOrder.getState() == IOrder.State.FILLED) {
                                removeOrders.add(order);
                                newOrders.add(mergedOrder);
                            }
                        }
                    }
               
                longOrders.removeAll(removeOrders);
                longOrders.addAll(newOrders);
            }

           

           
        } else if(sellSign1) {
           
            if(shortOrders.size() == 0) {
                shortOrders.add(submitOrder(OrderCommand.SELL));
               
            } else {
               
                // for same signal direction
                // open new order and merge with it
                removeOrders = new Vector<IOrder>();
                newOrders = new Vector<IOrder>();

                for(IOrder order : shortOrders) {
                   

                        IOrder newOrder = submitOrder(OrderCommand.SELL);
                        IMessage message = newOrder.waitForUpdate(2, TimeUnit.SECONDS);
                       
            // order is FILLED on successful merge with amount > 0
                        if (newOrder.getState() == IOrder.State.FILLED) {
                            IOrder mergedOrder = engine.mergeOrders(getLabel(instrument), order, newOrder);
                            message = mergedOrder.waitForUpdate(2, TimeUnit.SECONDS);

                            // order is FILLED on successful merge with amount > 0
                            if (mergedOrder.getState() == IOrder.State.FILLED) {
                                removeOrders.add(order);
                                newOrders.add(mergedOrder);
                            }
                        }
                    }
               
                shortOrders.removeAll(removeOrders);
                shortOrders.addAll(newOrders);
            }

           
           
        }

    }
   
 
 
    private IOrder submitOrder(OrderCommand orderCmd) throws JFException {
        return engine.submitOrder(getLabel(instrument), instrument, orderCmd, amount, 0, slippage, 0, 0);
    }

    private void closeOrder(IOrder order) throws JFException {
        if (order != null && isActive(order)) {
            order.close();
        }
    }

    private boolean isActive(IOrder order) throws JFException {
        if (order != null && order.getState() != IOrder.State.CLOSED && order.getState() != IOrder.State.CREATED && order.getState() != IOrder.State.CANCELED) {
            return true;
        }
        return false;
    }

    private double getPipPrice(double pips) {
        return pips * this.instrument.getPipValue();
    }

    private String getLabel(Instrument instrument) {
        String label = instrument.name();
        label = label + "_5M_" + (counter++);
        label = label.toUpperCase();
        return label;
    }
   
     public boolean isRightTime(long time, int fromHour, int fromMin, int toHour, int toMin) {
        Calendar cal = new GregorianCalendar();
        cal.setTimeZone(TimeZone.getTimeZone("GMT"));
        cal.setTimeInMillis(time);
        cal.set(Calendar.HOUR_OF_DAY, fromHour);
        cal.set(Calendar.MINUTE, fromMin);

        Calendar cal2 = new GregorianCalendar();
        cal2.setTimeZone(TimeZone.getTimeZone("GMT"));
        cal2.setTimeInMillis(time);
        cal2.set(Calendar.HOUR_OF_DAY, toHour);
        cal2.set(Calendar.MINUTE, toMin);

        if (cal.getTimeInMillis() <= time
                && time <= cal2.getTimeInMillis()) {
            return true;
        }
        return false;
    }
       
    private double getRoundedPrice(double price) {
        BigDecimal bd = new BigDecimal(price);
        bd = bd.setScale(instrument.getPipScale() + 1, RoundingMode.HALF_UP);
        return bd.doubleValue();
    }

   
    public void onMessage(IMessage message) throws JFException {
    }

    public void onAccount(IAccount account) throws JFException {
    }

    public void onStop() throws JFException {
    }

    /**************** debug print functions ***********************/
    private void print(Object... o) {
        for (Object ob : o) {
            //console.getOut().print(ob + "  ");
            if (ob instanceof Double) {
                print2(toStr((Double) ob));
            } else if (ob instanceof double[]) {
                print((double[]) ob);
            } else if (ob instanceof double[]) {
                print((double[][]) ob);
            } else if (ob instanceof Long) {
                print2(toStr((Long) ob));
            } else if (ob instanceof IBar) {
                print2(toStr((IBar) ob));
            } else {
                print2(ob);
            }
            print2(" ");
        }
        console.getOut().println();
    }

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

    private void print2(Object o) {
        console.getOut().print(o);
    }
   
    private void print(double d) {
        print(toStr(d));
    }

    private void print(double[] arr) {
        print(toStr(arr));
    }

    private void print(double[][] arr) {
        print(toStr(arr));
    }
    private void print(IBar bar) {
        print(toStr(bar));
    }

    private void printIndicatorInfos(IIndicator ind) {
        for (int i = 0; i < ind.getIndicatorInfo().getNumberOfInputs(); i++) {
            print(ind.getIndicatorInfo().getName() + " Input " + ind.getInputParameterInfo(i).getName() + " " + ind.getInputParameterInfo(i).getType());
        }
        for (int i = 0; i < ind.getIndicatorInfo().getNumberOfOptionalInputs(); i++) {
            print(ind.getIndicatorInfo().getName() + " Opt Input " + ind.getOptInputParameterInfo(i).getName() + " " + ind.getOptInputParameterInfo(i).getType());
        }
        for (int i = 0; i < ind.getIndicatorInfo().getNumberOfOutputs(); i++) {
            print(ind.getIndicatorInfo().getName() + " Output " + ind.getOutputParameterInfo(i).getName() + " " + ind.getOutputParameterInfo(i).getType());
        }
        console.getOut().println();
    }

    public static String toStr(double[] arr) {
        String str = "";
        for (int r = 0; r < arr.length; r++) {
            str += "[" + r + "] " + (new DecimalFormat("#.#######")).format(arr[r]) + "; ";
        }
        return str;
    }

    public static String toStr(double[][] arr) {
        String str = "";
        if (arr == null) {
            return "null";
        }
        for (int r = 0; r < arr.length; r++) {
            for (int c = 0; c < arr[r].length; c++) {
                str += "[" + r + "][" + c + "] " + (new DecimalFormat("#.#######")).format(arr[r][c]);
            }
            str += "; ";
        }
        return str;
    }

    public String toStr(double d) {
        return (new DecimalFormat("#.#######")).format(d);
    }

    public String toStr(Long time) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") {

            {
                setTimeZone(TimeZone.getTimeZone("GMT"));
            }
        };
        return sdf.format(time);
    }
   
    private String toStr(IBar bar) {
        return toStr(bar.getTime()) + "  O:" + bar.getOpen() + " C:" + bar.getClose() + " H:" + bar.getHigh() + " L:" + bar.getLow();
    }

    private void printTime(Long time) {
        console.getOut().println(toStr(time));
    }

}


 
 Post subject: Re: "Merge All" strategy from Dukascopy errors Post rating: 0   Post Posted: Thu 11 Jul, 2013, 03:37 
User avatar

User rating: 98
Joined: Mon 23 Jul, 2012, 02:02
Posts: 656
Location: United States, Durham, NC
Well, thanks you Guys. I'm happy to contribute something.....

In my own system, I have an advanced mode where I can Merge All or just
Merge Long or Merge Short positions. In an Advanced Hedging type of approach,
by using ECN PLACE_BID and PLACE_OFFER order types, you are in positions
on both sides of the market, so Auto Merge is necessary to resolve the
situation. Here's why I have this possibility:

When you Enter using a PLACE_BID or PLACE_OFFER order type, you are
Bidding/Offering live on the market. With that order type, you have the
possibility of Wholesale-ish pricing. BUT, when you CLOSE a position,
you are closing "retail". So by NOT using CLOSE operations, but by
using Merge operations instead, you can squeeze more out of every
scalp :) Maybe that didn't make much sense..... So I use MERGE
a lot for very precise scalping.

In other words, Position A is open, and opposite Position B is also
open. Instead of closing A and B (retail), we MERGE A and B together....
thus preserving a pricing advantage :)

It's not a great design to potentially Merge orders driven by a tick event.

At least the code could timestamp its last Merge operation, and then delay
any further Merge attempts for a "reasonable" period of time, ya know :)

There would be no practical reason to want to do that......

Good Trading !!!
HyperScalper


 

Jump to:  

  © 1998-2026 Dukascopy® Bank SA
On-line Currency forex trading with Swiss Forex Broker - ECN Forex Brokerage,
Managed Forex Accounts, introducing forex brokers, Currency Forex Data Feed and News
Currency Forex Trading Platform provided on-line by Dukascopy.com