Merge Positions

Orders get merged by using the IEngine.mergeOrders method, where one has to specify label of the merged order and a list of mergeable orders.

Merge preconditions

In order to merge two orders the following conditions must be met:

  1. both orders must be opened for the same instrument;
  2. both orders must be in IOrder.State.FILLED state;
  3. no stop loss/take profit conditions should exist for both orders (See Set Take Profit price and Set Stop Loss price sections for more details).

Merge Execution

The following characteristics about the order merge execution and result are the case:

  1. Order direction, amount, state and received messages after merge are specified by Order Merge States diagram.
  2. Merged order open price, i.e. IOrder.getOpenPrice, is the weighted average of mergeable order open prices.
  3. There is no commission applied on merge operation, since technically no orders get neither filled nor closed (except the case when resulting order amount is 0, see diagram in 1). Hence one may reduce commission on order close by merging opposite direction orders before their close.
  4. There is no slippage, see the first sentence of 3.

Examples

Simple merge

Consider merge of 2 orders:

IEngine engine = context.getEngine();
IOrder firstOrder = engine.getOrder("firstOrder"); 
IOrder secondOrder = engine.getOrder("secondOrder");
IOrder mergedOrder = engine.mergeOrders("mergedOrder", firstOrder, secondOrder);

In a case of normal execution both orders get closed and the appropriate onMessage events get triggered.

Merge with SL and TP update

Consider having 3 orders which have SL or TP prices, since orders with SL or TP can't be merged, both price conditions have to be removed and after successful merge set all over new to the merged order. Consider having 3 orders with different SL and/or TP conditions:

        double price = history.getLastTick(Instrument.EURUSD).getBid();
        //0.01 BUY market order with SL and TP of 10 pips
        IOrder order1 = engine.submitOrder("order1", Instrument.EURUSD, OrderCommand.BUY, 0.01, 0, 20, price - 0.0010, price + 0.0010);
        //0.02 SELL market order without SL and TP
        IOrder order2 = engine.submitOrder("order2", Instrument.EURUSD, OrderCommand.SELL, 0.02, 0, 20, 0, 0);
        //0.02 BUY market order with SL of 10 pips
        IOrder order3 = engine.submitOrder("order3", Instrument.EURUSD, OrderCommand.BUY, 0.02, 0, 20, price - 0.0010, 0);

        //wait for market orders to get FILLED
        order1.waitForUpdate(2000, IOrder.State.FILLED);
        order2.waitForUpdate(2000, IOrder.State.FILLED);
        order3.waitForUpdate(2000, IOrder.State.FILLED);

Consider removing order SL and TP price conditions (if any):

    private void removeTakeProfitStopLoss(IOrder... orders) throws JFException {

        //remove sl and tp attached orders if any
        for(IOrder o: orders){
            if(Double.compare(o.getStopLossPrice(),0) != 0){
                o.setStopLossPrice(0);
                print(o.getLabel() + " remove stop loss.");
                o.waitForUpdate(2000);                
            }
            if(Double.compare(o.getTakeProfitPrice(),0) != 0){
                o.setTakeProfitPrice(0);
                print(o.getLabel() + " remove take profit.");
                o.waitForUpdate(2000);                
            }
        }        
    }

Consider merging the three orders and applying SL price condition to the merged order:

    private void mergeOrders(IOrder order1, IOrder order2, IOrder order3, double price) throws JFException {
        IOrder mergedOrder = engine.mergeOrders("mergedOrder", order1, order2, order3);
        IMessage message = mergedOrder.waitForUpdate(2, TimeUnit.SECONDS);
        //we have received either MESSAGE_MERGE_OK or MESSAGE_MERGE_REJECTED
        print("Message after merge: " + message.getType() + " - " + message);
        //order is FILLED on successful merge with amount > 0 
        if(mergedOrder.getState() == IOrder.State.FILLED){
            double slPrice = mergedOrder.isLong() ? price - 0.0010 : price + 0.0010;
            mergedOrder.setStopLossPrice(slPrice);
        }
    }

MergeRemoveSlTp.java

Merge with weighted average SL update

Consider adjusting the previous example such that the SL price of the resulting merged order gets calculated as the weighted average SL of the mergeable orders (i.e. SL price weight depends on the mergeable order's amount):

private void mergeWithSlAndTp(IOrder... orders) throws JFException{
    ITick tick = history.getLastTick(instrument);
    double slAmountWeightedTotal = 0; //SL amount - aggregation of market price distance to SL's and weighted by order amount
    double slAmountWeighted;
    int slCount = 0;

    //remove sl attached orders if any
    for(IOrder o: orders){
        double price = o.isLong() ? tick.getBid() : tick.getAsk();
        if(Double.compare(o.getStopLossPrice(),0) != 0){
            slAmountWeighted = Math.abs(price - o.getStopLossPrice()) * o.getAmount();
            slAmountWeightedTotal += slAmountWeighted; 
            print( String.format("%s remove stop loss. amount-weighted SL=%.8f, already aggregated SL amount=%.8f", 
                o.getLabel(), slAmountWeighted, slAmountWeightedTotal));
            o.setStopLossPrice(0);

            o.waitForUpdate(2000);              
            slCount++;
        }
    }

    double slAmountWeightedAverage = slAmountWeightedTotal / slCount;

    IOrder mergedOrder = engine.mergeOrders("mergedOrder", orders);
    mergedOrder.waitForUpdate(2000);

    if(mergedOrder.getState() != IOrder.State.FILLED){
        return;
    }

    double slPriceDelta = slAmountWeightedAverage / mergedOrder.getAmount();
    double slPrice = mergedOrder.isLong() 
        ? tick.getBid() - slPriceDelta
        : tick.getAsk() + slPriceDelta;
    mergedOrder.setStopLossPrice(slPrice);
    mergedOrder.waitForUpdate(2000);

    print(String.format("mergedOrder sl=%.5f", mergedOrder.getStopLossPrice()));
}

MergeWithSlAdjustment.java

Limit merge count per position

Consider a strategy which prevents the position to be merged from more than 5 different positions.

private Map<IOrder, Integer> mergeCounts = new HashMap<IOrder, Integer>();  
@Override
public void onStart(IContext context) throws JFException {
    engine = context.getEngine();
    console = context.getConsole();
    console.getOut().println("Start");

    // create 5 buy orders and 5 sell orders
    for (int i = 0; i < 10; i++) {
        IOrder order = engine.submitOrder('o' + String.valueOf((char)((int)'A' + i)), instrument, i % 2 == 1 ? OrderCommand.BUY : OrderCommand.SELL, 0.001 * (i + 1));
        order.waitForUpdate(IOrder.State.FILLED);
    }

    while (engine.getOrders().size() > 1){
        int previousMergeCountTotal = 0;
        String label = "";
        List<IOrder> mergeableOrders = Arrays.asList(engine.getOrders().get(0), engine.getOrders().get(1));
        for(IOrder mergeableOrder : mergeableOrders){
            Integer previousMergeCount = mergeCounts.get(mergeableOrder);
            if(previousMergeCount != null){
                previousMergeCountTotal += previousMergeCount;
            }
            label += mergeableOrder.getLabel();
        }
        if(previousMergeCountTotal >= 5){
            console.getWarn().println("Aggregated merge count can't exceed 5!");
            break;
        } else {
            IOrder mergedOrder = engine.mergeOrders(label,mergeableOrders.toArray(new IOrder[]{}));
            IMessage message = mergedOrder.waitForUpdate(2, TimeUnit.SECONDS);
            if(message.getType() == IMessage.Type.ORDERS_MERGE_OK){
                mergeCounts.put(mergedOrder, previousMergeCountTotal+1);
            }
            console.getInfo().println(mergeCounts);
        }

    }
}

Merge10WLimit.java

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