ポジションはIEngine.mergeOrdersメソッドでマージする事が出来ます。
マージ可能なオーダーリストとマージ後のラベルを指定する必要があります。
マージの前提条件
2つのオーダーをマージするには、以下条件を満たす必要があります。
- 両方のオーダーが同じ通貨ペアである事
- 両方のオーダーのオーダー状態がIOrder.State.FILLEDである事
- 両方のオーダーにリミットやストップロスの設定がされていない事
マージの実行
オーダーマージの実行と結果については、以下のような特徴があります。
- マージ後のオーダー方向・取引量・オーダー状態・メッセージは、オーダーマージ状態ダイアグラムによって指定されます。
- マージされたオーダーのオープン価格(IOrder.getOpenPrice)は、マージ可能なオーダーのオープン価格の加重平均になります。
- マージ操作による手数料はありませんが、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.
- マージによってスリップページが発生する事はありません。
サンプル(シンプルなマージ)
2つのオーダーをマージします。
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.
サンプル(ストップロス・リミット設定のオーダーをマージ)
ストップロスかリミット設定された3つのオーダーをマージする場合、
ストップロスかリミット設定されているとマージする事が出来ない為、ストップロスとリミットの両方を削除しなければなりません。
マージ完了後に、マージされたオーダーに新たにストップとリミットを設定する必要があります。
以下例では、異なるストップロス・リミット設定を持つ3つのオーダーを作成します。
double price = history.getLastTick(Instrument.EURUSD).getBid();
IOrder order1 = engine.submitOrder("order1", Instrument.EURUSD, OrderCommand.BUY , 0.001, 0, 20, price - 0.0010, price + 0.0010);
IOrder order2 = engine.submitOrder("order2", Instrument.EURUSD, OrderCommand.SELL, 0.002, 0, 20, 0 , 0 );
IOrder order3 = engine.submitOrder("order3", Instrument.EURUSD, OrderCommand.BUY , 0.002, 0, 20, price - 0.0010, 0 );
以下例では、ストップロスとリミットの設定があったら削除します。
for(IOrder o: orders){
if(Double.compare(o.getStopLossPrice(),0) != 0){
o.setStopLossPrice(0);
print(o.getLabel() + " のストップロスを削除しました。");
o.waitForUpdate(2000);
}
if(Double.compare(o.getTakeProfitPrice(),0) != 0){
o.setTakeProfitPrice(0);
print(o.getLabel() + " のリミットを削除しました。");
o.waitForUpdate(2000);
}
}
以下例は、3つのオーダーをマージし、マージしたオーダーにストップロス設定を行います。
IOrder mergedOrder = engine.mergeOrders("mergedOrder" + ++mergeCount, order1, order2, order3);
IMessage message = mergedOrder.waitForUpdate(2, TimeUnit.SECONDS);
print("ポジションマージされました: " + message.getType() + " - " + message);
if(mergedOrder.getState() == IOrder.State.FILLED){
double slPrice = mergedOrder.isLong() ? price - 0.0010 : price + 0.0010;
mergedOrder.setStopLossPrice(slPrice);
}
サンプルソースコード:
MergeRemoveSlTp.java
サンプル(マージして加重平均ストップロスで更新)
マージ可能なオーダーのストップロスの加重平均を算出し、マージしたオーダーに算出したストップロスを設定します
(ストップロスの比重はマージ可能なオーダーの取引数量に依存します)。
private void mergeWithSlAndTp(IOrder... orders) throws JFException{
ITick tick = history.getLastTick(instrument);
double slAmountWeightedTotal = 0;
double slAmountWeighted;
int slCount = 0;
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 のストップロス削除。" +
"取引数量で加重したストップロス=%.8f, 加重ストップロスサマリー=%.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("マージされたオーダーのストップロス=%.5f", mergedOrder.getStopLossPrice()));
}
サンプルソースコード:
MergeWithSlAdjustment.java
サンプル(ポジション毎のマージ回数制限)
異なるポジションを4回以上マージされないようにする例です
private Map<IOrder, Integer> mergeCounts = new HashMap<IOrder, Integer>();
@Override
public void onStart(IContext context) throws JFException {
engine = context.getEngine();
console = context.getConsole();
context.setSubscribedInstruments(java.util.Collections.singleton(instrument), true);
console.getOut().println("Start");
for (int i = 0; i < 8; 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 >= 4){
console.getWarn().println("マージ回数は4回を超えて実施する事は出来ません!");
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