決済したいポジションを取り出す
今回はcBotからポジションを決済する方法をざっとまとめてみます。
まずは決済したいポジションを取り出すところから。
エントリー後に自分のエントリーしたPositionオブジェクトの参照をフィールドに持っておいてもいいのですが、この場合、適切に処理しないとストップロスなどでサーバー側で決済されたポジションの参照をいつまでも持ってしまう可能性があります。
もともと保有中の全ポジションはRobot本体クラスのPositionsプロパティからアクセス可能ですので、基本的に決済したいポジションはその都度ここから取り出す方がわかりやすいと思います。
いくつか取り出し方の例を挙げてみましょう。
Find系メソッドで取り出す
自分がエントリーしたポジション全部
エントリー時にラベルを指定しておく必要はありますが、こんな感じで取り出せます。
var myPositions = Positions.FindAll("MyLabel", SymbolName);
SymbolNameを指定するのを忘れないようにしてください。ラベルだけの指定では、他通貨ペアで同じcBotが動いてたらそっちのポジションも持ってきてしまいます。
注意点としては同じ通貨ペア名の別チャートで同じcBotを動かしていた場合は、そっちでエントリーしたポジションも拾ってしまいますが、まぁレアケースでしょう。
そのような使い方をするcBotを作成する場合はパラメータで別のラベルを指定するなど工夫をしておいてください。
自分がエントリーした売りポジション一つだけ
Find系メソッドはTradeTypeを指定することで買いだけ、売りだけ、といった取り出し方も可能です。
例として一番古い売りポジションはこんな感じで取り出せます。
var sellPosition = Positions.Find("MyLabel", SymbolName, TradeType.Sell);
もちろんFindAllでも同じように指定して、売り全部を取り出すことも可能です。Findメソッドの場合、対象ポジションがなければnullが返ります。
LINQで取り出す
PositionsのFindAllやFindメソッドでは、ラベル、通貨ペア名、売りか買いかだけしか指定できませんが、Linqを使えば様々な条件を指定してポジションを取り出すことが可能です。
以下のサンプルコードは上記のFindAllで取り出した自分のポジションmyPositionsの中から取り出す例ですが、Positionsプロパティから直に取り出すことも可能です。
利益の出てるポジションだけ
var profitPositions = myPositions.Where(position => position.NetProfit > 0);
Whereを使って条件に合うポジションだけ取り出します。Whereの引数(ラムダ式部分)を変えればどんな条件でも指定できます。
一番獲得Pipsの多いポジション一つだけ
var bestPosition = myPositions.OrderBy(position => position.Pips).LastOrDefault();
Pipsで昇順に並び替えて、一番最後の要素だけ取り出しています。
なお、いずれもmyPositionsが空っぽの場合はnullが返りますのでnullチェックはお忘れなく。
取り出したポジションを全部決済する
では、続いて取り出したポジションを決済してみましょう。下記サンプルではclosePositionsという名前の変数に取り出した決済したいポジションが全部入ってると思ってください。
単純な全決済
一つずつ順番に決済するならこう。Positionsをたどって対象ポジションだけを決済します。cBotで自分がエントリーしたポジションを順番に決済するならこんな感じになります。
foreach(var position in closePositions) {
position.Close();
}
ちなみにposition.Close()とClosePosition(position)は全く同じです。これらのメソッドは注文結果が返って来てから、次の注文を送ります。
決済対象が一つだけの時はいいのですが、複数ポジション決済時はこれだとタイムロスが生まれるため、お勧めしません。非同期注文を使いましょう。
foreach(var position in closePositions) {
ClosePositionAsync(position);
}
基本こっちでいいと思います。
決済処理としてはこれだけなのですが、万が一決済に失敗した時はポジションが残ってしまいます。次に示す決済失敗した時の処理も入れておくことをお勧めします。
決済失敗したらリトライ
成功したかどうか確認して、失敗してたら1回リトライするならこう。
var res = position.Close();
if (!res.IsSuccessful) position.Close();
非同期でやるなら次の通り。
ClosePositionAsync(position, args => {
if (!args.IsSuccessful) ClosePositionAsync(args.Position);
});
まぁ失敗したとこに1回リトライしたくらいで決済できるかどうかはわかりませんが。
絶対に決済させたい
1回リトライなんてぬるい。死んでも絶対に決済させたいんじゃ!という方はもしかしたらこんなコードを書きたくなってしまうかもしれません。
//非推奨!
TradeResult res;
do {
res = ClosePosition(position);
} while (!res.IsSuccessful);
これはお勧めできません。決済できない状況では本当に死にます。プログラムが。(もし死ななかったらフリーズします。)
そもそもサーバーが応答しないときなんて、コンマ秒単位で何回命令送りつけたところでたいてい結果は同じです。
死んでも決済させたいのはわかりますが、死んだら決済できないのであまりお勧めできません。
やるなら非同期でやりましょう。こんなメソッドを用意してあげます。
//--------------------------
// 絶対クローズしたい!
private void AbsolutelyClose(Position position) {
ClosePositionAsync(position, args => {
if(!args.IsSuccessful) AbsolutelyClose(args.Position);
});
}
失敗の結果を受け取ったら、再度決済注文を送る、というのをひたすら繰り返します。
使い方はforeachの中でClosePositionAsyncの代わりにこのAbsolutelyCloseを呼んであげるだけです。必要に応じてエラー内容まで確認してから再起呼び出ししてください。
やってること自体は同期のときと同じなんですが、非同期で動くためプログラムをフリーズさせることはありません。(多分。実際そんな状況になったことないので確認できてませんが。)
決済は重要
言うまでもなく、決済注文は自動売買において特に重要ともいえる部分でしょう。エントリーできないならまだしも、「決済できずに損切りできませんでした」ではシャレになりません。
決済失敗した場合には仮に延々と注文送り続けるにしても、本来は何度か失敗したら通知でも送って人の手が入るようにする方が望ましい気がします。
また、万が一「決済できない」ということを見越して、エントリー時は仮でもいいので必ずストップロスを設定しておくことも重要ですね。
確実な決済処理でよりよいcBotライフを!