OANDA API 解説編 第13回 決済注文 その1
前回まででオーダーについて簡単に学びましたので今度はそのポジションを決済する方法を考えてみたいと思います。
OANDA REST-v20の開発ガイド(英語版)はこちら。
決済の方法いくつか
保有しているポジションを閉じる場合でもその方法はいくつかあるのでそれについて調べてみました。
反対取引をする
まずは今あるトレードに反対のポジションを持つの新規のオーダーを出して決済させる方法です。
下の例では、ロングポジションがあるところに新規でショートの成行注文をしました。
以下のデータが戻り無事オーダーが通りポジションが相殺されなくなりました。
{ "orderCreateTransaction": { "id": "1185", "accountID": "999-999-99999999-999", "userID": 99999999, "batchID": "1185", "requestID": "60788282476870863", "time": "2020-12-16T21:27:26.320630214Z", "type": "MARKET_ORDER", "instrument": "USD_JPY", "units": "1", "timeInForce": "FOK", "positionFill": "DEFAULT", "reason": "CLIENT_ORDER" }, "orderFillTransaction": { "id": "1186", "accountID": "999-999-99999999-999", "userID": 99999999, "batchID": "1185", "requestID": "60788282476870863", "time": "2020-12-16T21:27:26.320630214Z", "type": "ORDER_FILL", "orderID": "1185", "instrument": "USD_JPY", "units": "1", "requestedUnits": "1", "price": "103.490", "pl": "-0.0002", "quotePL": "-0.016", "financing": "0.0000", "baseFinancing": "0.00000000000000", "commission": "0.0000", "accountBalance": "98891.7161", "gainQuoteHomeConversionFactor": "0.009615198836", "lossQuoteHomeConversionFactor": "0.009711834", "guaranteedExecutionFee": "0.0000", "quoteGuaranteedExecutionFee": "0", "halfSpreadCost": "0.0001", "fullVWAP": "103.490", "reason": "MARKET_ORDER", "tradesClosed": [ 👈 { "tradeID": "1184", "units": "1", "realizedPL": "-0.0002", "financing": "0.0000", "baseFinancing": "0.00000000000000", "price": "103.490", "guaranteedExecutionFee": "0.0000", "quoteGuaranteedExecutionFee": "0", "halfSpreadCost": "0.0001" } ], 以下省略
戻り値に"tradesClosed"キーが含まれていますのでポジションが相殺されているのは確認できます。
ただすべて相殺されたのかはわかりにくいですね。
利確・損切注文を使って決済する
新規のオーダーに利確・損切注文を追加しておき、そのプライスになったら決済させる方法が一番簡単だと思われますが、注文を追加する方法もいくつかあります。
新規オーダー時に同時発注
以前にも解説しましたので、詳しくは書きませんが、PUT orderの際にtakeProfit/StopLoss/TrailingStopのエリアを使ってあらかじめプライスをセットする方法。
data引数のうち、takeProfitOnFill(利確)とstopLossOnFill, traillingStopOnFill(損切)の部分のところのみ表示
名前 | 型 | 必須 | 値 | 説明 | |
---|---|---|---|---|---|
takeProfitOnFill | price | number | Y | 利確プライス | |
timeInForce | string | Y | GTC | 初期値:GTC, GTD, GFD | |
gtdTime | DateTime | UTC時間 | timeInForceがGTDの時、有効期限を指定 | ||
clientExtensions | 拡張領域 独自のid, tag, extensionをセットできる *MT4を使用の場合は使ってはいけない。 | ||||
stopLossOnFill | price | number | Y | 損切プライス | |
distance | number | 例:USD/JPYの時、1であれば±1円で損切プライスを設定 | |||
timeInForce | string | Y | GTC | 初期値:GTC, GTD, GFD | |
gtdTime | DateTime | UTC時間 | timeInForceがGTDの時、有効期限を指定する。 yyyy-mm-ddThh:mm:ssZ | ||
clientExtensions | 拡張領域 独自のid, tag, extensionをセットできる *MT4を使用の場合は使ってはいけない。 | ||||
trailingStopOnFill | distance | number | Y | 例:USD/JPYの時、1であれば±1円で損切プライスを設定 | |
timeInForce | string | Y | GTC | 初期値:GTC, GTD, GFD | |
gtdTime | DateTime | UTC時間 | timeInForceがGTDの時、有効期限を指定する。 yyyy-mm-ddThh:mm:ssZ | ||
clientExtensions | 拡張領域 独自のid, tag, extensionをセットできる *MT4を使用の場合は使ってはいけない。 |
成行注文時では注文時に約定価格がわかりませんので、ディスタンスで利確や損切のプライス設定する場合は良いですが、プライスで指定する場合は要注意です。
TAKE_PROFIT, STOP_LOSS、TRAILING_STOP_LOSSオーダーを追加
新規の指値や成行注文と同様に、すでにあるトレードIDをもとに利確・損切注文を追加で注文できます。
POST /v3/accounts/{accountID}/orders
data部分
名前 | 型 | 必須 | 値 | 説明 |
---|---|---|---|---|
type | string | Y | → | TAKE_PROFIT, STOP_LOSS, TRAILING_STOP_LOSS |
tradeID | string | Y | トレードID |
利確損切をセットしたい元のtradeID またはそのclietnTradeIDのどちらかを指定 |
clientTradeID | string | 拡張用トレードID | ||
price | number | Y | プライス |
どちらかに入力 typeが"TAKE_PROFIT"の時、price (distanceも使えないことはない) "STOP_LOSS"の時、priceかdistanceどちらか "TRAILING_STOP"の時、distanceのみ |
distance | number | ディスタンス | ||
timeInForce | string | Y | GTC | 注文の有効期限 初期値:GTC, GTD, GFD |
gtdTime | DateTime | UTC時間 | timeInForce="GTD"の時のみ指定。 | |
triggerCondition | string | Y | DEFAULT | 初期値=DEFAULT(BID or ASK), INVERSE, BID, ASK, MID |
clientExtensions | string | 拡張領域 独自のid, tag, extensionをセットできる *MT4を使用の場合は使ってはいけない。 |
data引数に以下のようにセットして実行すると
data_TP = { "order": { "type": "TAKE_PROFIT", "tradeID": "1157", "price": "104.65", } }
戻り値の例としては以下のような値を取得できます。
{ "orderCreateTransaction": { "id": "1158", "accountID": "999-999-99999999-999", "userID": 99999999, "batchID": "1158", "requestID": "24757037715201085", "time": "2020-12-10T03:20:58.753438370Z", "type": "TAKE_PROFIT_ORDER", "tradeID": "1157", "timeInForce": "GTC", "triggerCondition": "DEFAULT", "price": "104.650", "reason": "CLIENT_ORDER" }, "relatedTransactionIDs": [ "1158" ], "lastTransactionID": "1158" }
次に、Take Profit Orderがすでに元のトレードに設定されているにもかかわず、追加で発注した場合ですが、
{ "orderRejectTransaction": { "id": "1159", "accountID": "999-999-99999999-999", "userID": 99999999, "batchID": "1159", "requestID": "78800234582098036", "time": "2020-12-10T03:26:17.678158357Z", "type": "TAKE_PROFIT_ORDER_REJECT", "rejectReason": "TAKE_PROFIT_ORDER_ALREADY_EXISTS", "tradeID": "1157", "timeInForce": "GTC", "triggerCondition": "DEFAULT", "price": "104.6", "reason": "CLIENT_ORDER" }, "relatedTransactionIDs": [ "1159" ], "lastTransactionID": "1159", "errorMessage": "A Take Profit Order for the specified Trade already exists", "errorCode": "TAKE_PROFIT_ORDER_ALREADY_EXISTS" }
"orderRejectTransaction"が戻ってきてしまい、上書きや変更ではなくてエラーになってしますので注意が必要です。
→ "errorMessage": "A Take Profit Order for the specified Trade already exists" (既に存在している。)
この事から、追加用としてのみ使用可能のようです。
ちなみに存在しない番号を与えた場合は
"rejectReason": "TRADE_DOESNT_EXIST", "errorMessage": "The Trade specified does not exist", "errorCode": "TRADE_DOESNT_EXIST"
"番号が存在しません。"というエラーが返ってくるのが確認できました。
特定のトレードを変更
トレードIDをもとにトレードを変更する方法
PUT /v3/accounts/{accountID}/trades/{tradeSpecifier}/orders
約定が確定した後にそのトレードIDをもとにトレード内容(利確・損切情報)の追加・変更ができます。
URL path引数
名前 | 型 | 必須 | 値 | 説明 |
---|---|---|---|---|
tradeSpecifier | string | Y | tradeIDまたはclientTradeID | 自分でつけたclientTradeIDの時は前に@をつける |
data引数
名前 | 型 | 必須 | 値 | 説明 | |
---|---|---|---|---|---|
takeProfit | price | number | Y | 利確プライス | |
timeInForce | string | Y | GTC | 初期値:GTC, GTD, GFD | |
gtdTime | DateTime | UTC時間 | timeInForceがGTDの時、有効期限を指定 | ||
clientExtensions | 拡張領域 独自のid, tag, extensionをセットできる *MT4を使用の場合は使ってはいけない。 | ||||
stopLoss | price | number | Y | 損切プライス | |
distance | number | ディスタンス | 例:USD/JPYの時、1であれば±1円で損切プライスを設定 | ||
timeInForce | string | Y | GTC | 初期値:GTC, GTD, GFD | |
gtdTime | DateTime | UTC時間 | timeInForceがGTDの時、有効期限を指定する。 yyyy-mm-ddThh:mm:ssZ | ||
clientExtensions | 拡張領域 独自のid, tag, extensionをセットできる *MT4を使用の場合は使ってはいけない。 | ||||
trailingStop | distance | number | Y | ディスタンス | 例:USD/JPYの時、1であれば±1円で損切プライスを設定 |
timeInForce | string | Y | GTC | 初期値:GTC, GTD, GFD | |
gtdTime | DateTime | UTC時間 | timeInForceがGTDの時、有効期限を指定する。 yyyy-mm-ddThh:mm:ssZ | ||
clientExtensions | 拡張領域 独自のid, tag, extensionをセットできる *MT4を使用の場合は使ってはいけない。 |
data引数に以下の値をセットして実行すると
data_ModifyTrade = { "takeProfit": { "price": "105.00" } }
以下のような値が戻ります。
{ "takeProfitOrderTransaction": { "id": "1173", "accountID": "999-999-99999999-999", "userID": 99999999, "batchID": "1173", "requestID": "60788256587173561", "time": "2020-12-16T19:44:33.582455242Z", "type": "TAKE_PROFIT_ORDER", "tradeID": "1172", "timeInForce": "GTC", "triggerCondition": "DEFAULT", "price": "105.000", "reason": "CLIENT_ORDER" }, "1173" ], "lastTransactionID": "1173" }
更にこのエンドポイントでは、追加だけでなく取消・変更もできるようですので、両方試してみました。 まずは変更ですが、追加と同じように新しい値をセットするだけです。
data_ModifyTrade = { "takeProfit": { "price": "104.00" } }
戻ってくる値は、現在セットされているTPのオーダーがキャンセルされ新しいプライスを持っているオーダーが作成される形になります。
{ "takeProfitOrderCancelTransaction": { 👈いままでセットされていた値を取消 "id": "1174", "accountID": "999-999-99999999-999", "userID": 99999999, "batchID": "1174", "requestID": "60788261946521791", "time": "2020-12-16T20:05:51.068767834Z", "type": "ORDER_CANCEL", "orderID": "1173", "replacedByOrderID": "1175", "reason": "CLIENT_REQUEST_REPLACED" }, "takeProfitOrderTransaction": { 👈新しい値を追加する "id": "1175", "accountID": "999-999-99999999-999", "userID": 99999999, "batchID": "1174", "requestID": "60788261946521791", "time": "2020-12-16T20:05:51.068767834Z", "type": "TAKE_PROFIT_ORDER", "tradeID": "1172", "timeInForce": "GTC", "triggerCondition": "DEFAULT", "price": "104.000", "reason": "REPLACEMENT", "replacesOrderID": "1173", "cancellingTransactionID": "1174" }, "relatedTransactionIDs": [ "1174", "1175" ], "lastTransactionID": "1175" }
変更する事はあっても取消をする事はめったにないと思われますが、NULL値を設定することにより取消可能とありますので念のため取消の場合も試してみました。
詳細の事例がなかったのでいくつか試したところ以下のように”None”を指定することにより、無事キャンセルされました。
data_ModifyTrade = { "takeProfit": None }
{ "takeProfitOrderCancelTransaction": { "id": "1180", "accountID": "999-999-99999999-999", "userID": 99999999, "batchID": "1180", "requestID": "60788271380787412", "time": "2020-12-16T20:43:20.907405756Z", "type": "ORDER_CANCEL", "orderID": "1179", "reason": "CLIENT_REQUEST" "relatedTransactionIDs": [ "1180" ], "lastTransactionID": "1180" }
特定のオープントレードをクローズする
今までは利確・損切注文による決済でしたが、次はトレードそのものをクローズする方法についてです。
先ずはトレードIDをもとにその取引を決済する方法
PUT /v3/accounts/{accountID}/trades/{tradeSpecifier}/close
現在あるオープントレードの内、そのトレードIDをもとにすべて(または一部)を決済できます。
data引数
名前 | 型 | 必須 | 値 | 説明 |
---|---|---|---|---|
units | string | Y | ALLまたは数値 | ALLはすべてクローズ。マイナスは認めない。ポジションを超えてはいけない。 |
全額決済させたい場合は、以下のように”ALL"をdata引数の"units"にセットしてオーダーすると
data_Close = { "units": "ALL" }
反対取引でクローズした場合と同じようなフォーマットの戻り値が返って来ます。
"reason"は"MARKET_ORDER"ではなく、"MARKET_ORDER_TRADE_CLOSE"ですね。
{ "orderCreateTransaction": { "id": "1181", "accountID": "999-999-99999999-999", "userID": 99999999, "batchID": "1181", "requestID": "60788280420502788", "time": "2020-12-16T21:19:15.828235399Z", "type": "MARKET_ORDER", "instrument": "USD_JPY", "units": "-1", "timeInForce": "FOK", "positionFill": "REDUCE_ONLY", "reason": "TRADE_CLOSE", "tradeClose": { "units": "ALL", "tradeID": "1172" } }, "orderFillTransaction": { "id": "1182", "accountID": "999-999-99999999-999", "userID": 99999999, "batchID": "1181", "requestID": "60788280420502788", "time": "2020-12-16T21:19:15.828235399Z", "type": "ORDER_FILL", "orderID": "1181", "instrument": "USD_JPY", "units": "-1", "requestedUnits": "-1", "price": "103.482", "pl": "-0.0016", "quotePL": "-0.167", "financing": "0.0000", "baseFinancing": "0.00000000000000", "commission": "0.0000", "accountBalance": "98891.7163", "gainQuoteHomeConversionFactor": "0.00961454845", "lossQuoteHomeConversionFactor": "0.009711177078", "guaranteedExecutionFee": "0.0000", "quoteGuaranteedExecutionFee": "0", "halfSpreadCost": "0.0001", "fullVWAP": "103.482", "reason": "MARKET_ORDER_TRADE_CLOSE", 👈 "tradesClosed": [ 👈 { "tradeID": "1172", "units": "-1", "realizedPL": "-0.0016", "financing": "0.0000", "baseFinancing": "0.00000000000000", "price": "103.482", "guaranteedExecutionFee": "0.0000", "quoteGuaranteedExecutionFee": "0", "halfSpreadCost": "0.0001" } ], 以下省略
特定の銘柄のポジションをクローズする
PUT /v3/accounts/{accountID}/positions/{instrument}/close
銘柄(通貨組)毎にそのポジションをすべて(または一部)決済する方法
data引数
名前 | 型 | 必須 | 値 | 説明 |
---|---|---|---|---|
longUnits | string | ALLまたは数値 |
買ポジション決済用 ALLはすべてクローズ。マイナスは認めない。ポジションを超えてはいけない。 |
|
longClientExtensions | string | 拡張領域 独自のid, tag, extensionをセットできる *MT4を使用の場合は使ってはいけない。 | shortUnits | string | ALLまたは数値 |
売ポジション決済用 ALLはすべてクローズ。マイナスは認めない。ポジションを超えてはいけない。 |
shortClientExtensions | string | 拡張領域 独自のid, tag, extensionをセットできる *MT4を使用の場合は使ってはいけない。 |
ロングでポジションを保有しているときに、data引数に以下のようにセットして実行すると
data_PositionClose = { "longUnits": "ALL" }
Keyの名前は一部違いますが、トレードをクローズさせた時と同じような値が返されます。
("longOrderCreateTransaction", "longOrderFillTransaction")
{ "longOrderCreateTransaction": { "id": "1190", "accountID": "999-999-99999999-999", "userID": 99999999, "batchID": "1190", "requestID": "78802699802302719", "time": "2020-12-16T22:42:12.064244756Z", "type": "MARKET_ORDER", "instrument": "USD_JPY", "units": "-1", "timeInForce": "FOK", "positionFill": "REDUCE_ONLY", "reason": "POSITION_CLOSEOUT", "longPositionCloseout": { "instrument": "USD_JPY", "units": "ALL" } }, "longOrderFillTransaction": { "id": "1191", "accountID": "999-999-99999999-999", "userID": 99999999, "batchID": "1190", "requestID": "78802699802302719", "time": "2020-12-16T22:42:12.064244756Z", "type": "ORDER_FILL", "orderID": "1190", "instrument": "USD_JPY", "units": "-1", "requestedUnits": "-1", "price": "103.416", "pl": "-0.0003", "quotePL": "-0.034", "financing": "0.0000", "baseFinancing": "0.00000000000000", "commission": "0.0000", "accountBalance": "98891.7158", "gainQuoteHomeConversionFactor": "0.009619847086", "lossQuoteHomeConversionFactor": "0.009716528966", "guaranteedExecutionFee": "0.0000", "quoteGuaranteedExecutionFee": "0", "halfSpreadCost": "0.0002", "fullVWAP": "103.416", "reason": "MARKET_ORDER_POSITION_CLOSEOUT", "tradesClosed": [ { "tradeID": "1188", "units": "-1", "realizedPL": "-0.0003", "financing": "0.0000", "baseFinancing": "0.00000000000000", "price": "103.416", "guaranteedExecutionFee": "0.0000", "quoteGuaranteedExecutionFee": "0", "halfSpreadCost": "0.0002" } ], "fullPrice": { "closeoutBid": "103.416", "closeoutAsk": "103.448", "timestamp": "2020-12-16T22:42:01.918227455Z", "bids": [ { "price": "103.416", "liquidity": "10000000" } ], "asks": [ { "price": "103.448", "liquidity": "10000000" } ] }, "homeConversionFactors": { "gainQuoteHome": { "factor": "0.00961984708610" }, "lossQuoteHome": { "factor": "0.00971652896636" }, "gainBaseHome": { "factor": "1" }, "lossBaseHome": { "factor": "1" } } }, "relatedTransactionIDs": [ "1190", "1191" ], "lastTransactionID": "1191" }
当然ですが、ロング保有の時に"shortUnits"を使ったり、"longUnits"と"shortUnits"を両方指定したり、ポジションがない時に呼び出した場合は"CLOSEOUT_POSITION_DOESNT_EXIST"エラーになりました。
まとめ
今回は決済の仕方についてあれこれ試してみました。
利確・損切注文でクローズさせる場合は、トレードIDからそのトレードを変更(PUT /v3/accounts/{accountID}/trades/{tradeSpecifier}/orders)する方法が一番汎用性がありそうです。
ポジションをオープンする際に同時発注する場合は、スリッページを考慮したプライスが必要ですし、追加でオーダーする場合はすでにセットされているかどうか必ず確認が必要になります。
新規発注時に広めに利確・損切を同時注文してその結果をみて再度適切な価格を設定しなおすという方法も考えられます。
トレードやポジションそのものでクローズする場合は、自分で独自のIDを予めつけておけば、tradeIDを覚えておかなくても決済のタイミングでそのIDを呼び出しクローズが出来ます。
また、もしその口座で複数のシストレや裁量取引が混ざらず、その銘柄をすべてクローズしても問題ないシステムであれば、その銘柄のポジションを丸ごと”ALL"で決済させれば、同様にトレードIDを記録しておく必要がないのでシンプルな仕組みになりそうです。
決済させるだけでもいろいろなシナリオが考えられますね。