オーダーのメッセージや状態変化のワークフローについてはオーダー状態のページを参照して下さい。
このページでは、オーダー変更のやり方について焦点を当てた内容になります。
オーダー変更のモニタリングする方法は2種類あります。
- onMessageのコールバックメソッド
- IOrder.waitForUpdateメソッド
基本的に、どちらの方法でも同様に機能しますが、
- 急を要する操作(ストップロス設定やクローズオーダー等)の場合、IOrder.waitForUpdateはas the advantage of keeping all the logic in the same place
- 急を要さない操作(ストップロス設定の解除や条件付きオーダー)の場合、IOrder.waitForUpdateを使用するべきではありません。
なぜなら、mostly it is non-deterministic when the operation will take place
and the execution of the method holds up the execution of the rest of the strategy logic.
- onMessageは特定アカウントの全てのオーダー変化についてメッセージを受け取れる利点がありますが、
IOrder.waitForUpdateは1つのオーダーでのみ機能します。
onMessageの使用例
onMessageメソッドは全てのオーダー変化メッセージ(オーダー状態か値が変化した時)を受信します。
このコールバックメソッドはalso the place where one can apply some strategy logic on certain order changes.
成行オーダー約定のフロー(onMessage)
以下例は、2つの成行オーダー送信し、サーバー側で変化があると直ぐにログメッセージ出力されます。
public void onStart(IContext context) throws JFException {
this.engine = context.getEngine();
this.console = context.getConsole();
context.setSubscribedInstruments(java.util.Collections.singleton(Instrument.EURUSD), true);
engine.submitOrder("orderValid" , Instrument.EURUSD, OrderCommand.BUY, 0.001);
engine.submitOrder("orderInvalid", Instrument.EURUSD, OrderCommand.BUY, 10.000);
}
public void onMessage(IMessage message) throws JFException {
switch(message.getType()){
case ORDER_SUBMIT_OK :
print("オーダー受信: " + message.getOrder());
break;
case ORDER_SUBMIT_REJECTED :
print("オーダー受信拒否: " + message.getOrder());
break;
case ORDER_FILL_OK :
print("オーダー約定: " + message.getOrder());
break;
case ORDER_FILL_REJECTED :
print("オーダー約定拒否: " + message.getOrder());
break;
default:
break;
}
print("<html><font color=\"gray\">"+message+"</font>");
}
注意:2つめのオーダーは無効な取引量で送信します。
サンプルソースコード:
TestOnMessage.java
約定後にストップロスを更新(onMessage)
ストップロス2pips設定で成行オーダーを送信し、約定されたら直ぐにストップロス価格をオープン価格から2pips下に正確に調整します。
public void onStart(IContext context) throws JFException {
this.engine = context.getEngine();
this.console = context.getConsole();
context.setSubscribedInstruments(java.util.Collections.singleton(Instrument.EURUSD), true);
double price = context.getHistory().getLastTick(Instrument.EURUSD).getBid();
order = engine.submitOrder("orderValid", Instrument.EURUSD, OrderCommand.BUY, 0.001, 0, 20,
price - 0.0002, price + 0.0002);
}
public void onMessage(IMessage message) throws JFException {
if(message.getOrder() == order && message.getType() == IMessage.Type.ORDER_FILL_OK){
order.setStopLossPrice(order.getOpenPrice() - 0.0005);
}
print("<html><font color=\"gray\">"+message+"</font>");
}
サンプルソースコード:
TestOnMessageSL.java
キューでストップロス更新(onMessage)
前回の例を変更して、ストップロス更新をonMessageの代わりにonTickメソッドで行われるようにします。
ユーザーが全てのオーダーを一か所(例えばonTickメソッド内)で処理したい場合に、便利なやり方です。
private Queue<IOrder> justFilledOrders = new ConcurrentLinkedQueue<IOrder>();
public void onMessage(IMessage message) throws JFException {
if(message.getOrder() == order && message.getType() == IMessage.Type.ORDER_FILL_OK){
justFilledOrders.add(message.getOrder());
}
print("<html><font color=\"gray\">"+message+"</font>");
}
public void onTick(Instrument instrument, ITick tick) throws JFException {
while(!justFilledOrders.isEmpty()){
IOrder filledOrder = justFilledOrders.poll();
if(filledOrder == order){
order.setStopLossPrice(order.getOpenPrice() - 0.0005);
}
}
}
サンプルソースコード:
TestOnMessageSLQueue.java
オーダー再送信(onMessage)
以下例は、ストラテジー起動時に約定拒否されるような取引数量で成行オーダーを送信し、
引数量を増やして再オーダーしてオーダー約定されるか、またはmaxOrderResubmitCountで指定した回数分再オーダーするまで継続します。
@Configurable("再オーダー送信回数上限")
public int maxOrderResubmitCount = 5;
private Map<IOrder,Integer> resubmitAttempts = new HashMap<IOrder,Integer>();
public void onStart(IContext context) throws JFException {
this.engine = context.getEngine();
this.console = context.getConsole();
context.setSubscribedInstruments(java.util.Collections.singleton(Instrument.EURUSD), true);
IOrder order = engine.submitOrder("order", Instrument.EURUSD, OrderCommand.BUY, - 0.002);
resubmitAttempts.put(order, 0);
}
public void onMessage(IMessage message) throws JFException {
IOrder order = message.getOrder();
if(message.getType() == IMessage.Type.ORDER_SUBMIT_REJECTED){
console.getOut().println(message);
Integer attempts = resubmitAttempts.get(order);
if(attempts == null){
console.getWarn().println("Rejected order was not created by this strategy.");
} else if (attempts > maxOrderResubmitCount){
console.getWarn().println("約定拒否の再オーダー送信回数上限を超えました");
} else {
resubmitAttempts.remove(order);
IOrder newOrder = engine.submitOrder(order.getLabel(), order.getInstrument(),
order.getOrderCommand(), order.getAmount() + 0.001);
resubmitAttempts.put(newOrder, ++attempts);
console.getOut().println("再送信オーダー: " + newOrder +
" 残送信回数: " +(maxOrderResubmitCount - attempts + 1));
}
}
}
注意:意図的に約定拒否されるようマイナスロットでオーダーしていますが、APIのバージョンによってはマイナスロットオーダーするとストラテジーが停止します。
サンプルソースコード:
TestOnMessageResubmit.java
IOrder.waitForUpdateの使用例
IOrder.waitForUpdateはストップロス価格の更新やクローズオーダー等のように急を要する操作を行う時に役に立ちます。
待機時間を数秒以上に設定する事はお勧めできません、IOrder.waitForUpdateの実行時間が長くなるとティックがスキップされてしまうからです。
成行オーダー約定のフロー(IOrder.waitForUpdate)
以下例は、IOrder.waitForUpdateメソッドを使用して、成行オーダー送信後にIOrder.State.FILLEDまで更新される事を確認します。
public void onStart(IContext context) throws JFException {
this.engine = context.getEngine();
this.console = context.getConsole();
context.setSubscribedInstruments(java.util.Collections.singleton(Instrument.EURUSD), true);
IOrder order = engine.submitOrder("orderValid", Instrument.EURUSD, OrderCommand.BUY, 0.001);
print(String.format("オーダー送信後: オーダー状態=%s, オープン価格=%.5f, オーダー作成時間=%s",
order.getState(), order.getOpenPrice(), DateUtils.format(order.getCreationTime())));
order.waitForUpdate(2000);
print(String.format("オーダー状態更新後(OPENED): オーダー状態=%s, オープン価格=%.5f, オーダー作成時間=%s",
order.getState(), order.getOpenPrice(), DateUtils.format(order.getCreationTime())));
order.waitForUpdate(2000);
print(String.format("オーダー状態更新後(FILLED): オーダー状態=%s, オープン価格=%.5f, 約定時間=%s",
order.getState(),order.getOpenPrice(), DateUtils.format(order.getFillTime())));
}
サンプルソースコード:
TestWaitForUpdate.java
オーダー状態がFILLEDになるまで待つ方法もあります(APIのバージョンによっては使用出来ません)。
order.waitForUpdate(2000, IOrder.State.FILLED);
ストップロス更新(IOrder.waitForUpdate)
成行オーダーが約定されるまで待ち、オープン価格に応じてストップロス価格を更新します。
public void onStart(IContext context) throws JFException {
this.engine = context.getEngine();
this.console = context.getConsole();
context.setSubscribedInstruments(java.util.Collections.singleton(Instrument.EURUSD), true);
double price = context.getHistory().getLastTick(Instrument.EURUSD).getBid();
IOrder order = engine.submitOrder("orderValid", Instrument.EURUSD, OrderCommand.BUY, 0.001, 0, 20,
price - 0.0005, price + 0.0005);
order.waitForUpdate(2000);
order.waitForUpdate(2000);
print(String.format("約定後: state=%s, オープン価格=%.5f, 約定時間=%s, ストップロス=%.5f",
order.getState(),order.getOpenPrice(), DateUtils.format(order.getFillTime()), order.getStopLossPrice()));
order.setStopLossPrice(order.getOpenPrice() - 0.0005);
order.waitForUpdate(2000);
print(String.format("ストップロス更新後: state=%s, オープン価格=%.5f, 約定時間=%s, ストップロス=%.5f",
order.getState(),order.getOpenPrice(), DateUtils.format(order.getFillTime()), order.getStopLossPrice()));
}
サンプルソースコード:
TestWaitForUpdateSL.java
オーダー再送信(IOrder.waitForUpdate)
以下例は、何らかの理由で約定拒否された場合の対処方法の例です。
単純に、約定拒否される取引数量でオーダー送信し、約定拒否されたらオーダー再送信します。
public void onStart(IContext context) throws JFException {
this.engine = context.getEngine();
this.console = context.getConsole();
context.setSubscribedInstruments(java.util.Collections.singleton(Instrument.EURUSD), true);
IOrder order = engine.submitOrder("order", Instrument.EURUSD, OrderCommand.BUY, -0.001);
IMessage message = order.waitForUpdate(2000, IOrder.State.OPENED);
if(message.getType() == IMessage.Type.ORDER_SUBMIT_REJECTED){
order = engine.submitOrder("order", Instrument.EURUSD, OrderCommand.BUY, 0.001);
message = order.waitForUpdate(2000, IOrder.State.OPENED);
}
print(String.format("更新後: オーダー状態=%s, メッセージ=%s", order.getState(), message));
}
IMessage.Typeをチェックする代わりに、IOrder.waitForUpdate実行後にIOrder.Stateする事も出来ます。
注意:意図的に約定拒否されるようマイナスロットでオーダーしていますが、APIのバージョンによってはマイナスロットオーダーするとストラテジーが停止します。
サンプルソースコード:
TestWaitForUpdateResubmit.java