2011/06/04

MT4用EA開発時代 - ERR_TRADE_TIMEOUT/142/143発生時の処理方式検討



さて前回は、課題を残しつつも、デモ口座上のMT4用EAからの警告メールで気付いたバグ修正をしたところまで書きました。今回は、前回の記事課題として挙げていた「ERR_TRADE_TIMEOUT」発生時の処理について書いてみたいと思います。


そもそも何が発端かというと、デモ口座上でMT4用EAを動かしていたときに、ERR_TRADE_TIMEOUT」が発生。その後当初設計通りにリトライをして無事成功。でもちょっと気になって、ヘルプを改めて見てみると。

愛用している有志の日本語ヘルプより。
●「ERR_TRADE_TIMEOUT」エラー
 トレードがタイムアウトに達しました。再試行する前に(少なくとも1分間)トレード操作
  が成功していないか確認する必要があります。(新しいポジションが開かれていないか、または、
  既存の注文がない変更されてないか、削除されてないか、既存のポジションが閉じられていない
  か、など)

つまり、「このエラーを検出した場合は、処理に成功している可能性もあるので、リトライする前に成功したか、失敗だったかを確認してからリトライしろ」と。









面倒やっちゅうねん!!




しかも、上記「ERR_TRADE_TIMEOUT」と同様に制御しないといけないエラーコードが後2つあった。

 オーダーは待ち行列に入れられました。これは、クライアント ターミナルとトレード サーバー
  とのエラーではなく、相互作用コードです。このコードは、切断と再接続のトレード操作の実行中
  に稀に発生します。このコードはエラー 128 と同じ方法で処理する必要があります。
 オーダーは実行のためにディーラーに受け入れられました。これは、クライアント ターミナル
  とトレード サーバーとのエラーではなく、相互作用コードです。コード 142 と同じ理由で表示
  されることあります。このコードはエラー 128 と同じ方法で処理する必要があります。


これらのエラーコードの説明から、MT4でトレードしたときの経路と、発生している事象を想像で書いてみると。

【トレード時経路の想像図】
---------------------------------
1.通常動作
  つまるところ、MT4が直接通信している先として、「トレードサーバ」があって、
  さらにその先に「ディーラ」のシステムがある。
  オーダすると、「MT4クライアント」→「トレードサーバ」→「ディーラ」の順に
  リクエストが送信され、レスポンス受信時はその逆から。通常は、トレードサーバ内の
  「待ち行列」は使われない。以下の図はOrderSend時の例。
2.ERR_TRADE_TIMEOUT発生時 - リトライしてもいいとき
  リトライしてもいい時の例を考えて見た。以下の図では「①OrderSend送信」時にパケットロスト
  等の通信障害により、「トレードサーバ」にリクエストが到着しなかった場合。
  当然処理されないので、リトライが必要。
3.ERR_TRADE_TIMEOUT発生時 - リトライしてはいけない時
  リトライしてはいけない例を考えて見た。以下の図では、「トレードサーバ」では、OrderSend自体
  の処理に成功しているけど、結果応答の際に通信障害等の理由で、MT4クライアント
  が、レスポンスを受信できなかった場合。
  このケースでは、おそらく「トレードサーバ」⇔「ディーラ」間で障害が発生したんじゃ
  ないかなぁと。この場合、「トレードサーバ」にはOrderSendした情報が到着していて、
  「ディーラー」とは通信できない状態なので、一旦「トレードサーバ」内の「待ち行列」
  に蓄積。でも実際には「ディーラー」にオーダが通っていないので、エラーを返却。
  その後「ディーラ」との通信が復旧したら、「待ち行列」に溜まっていた情報を一気に
  「ディーラ」に送信するという仕組みに見える。
  でも、復旧までの時間によっては、「待ち行列」に溜まっていた情報を「ディーラー」
  に送信したとしても、全て成功になるとは限らない(価格が市場と不一致だったり)。
  だから、実は成功するケースがあったりなかったり。  
  このケースでは、おそらく「トレードサーバ」⇔「ディーラ」間の通信は正常にも
  関わらず、「ディーラー」側で何かしらの障害で処理ができずに、処理を保留させて
  エラー応答した状態なのではないかと。
  もっと想像を膨らますと、「ディーラー」側にも「トレードサーバ」の「待ち行列」と
  同等の仕組みがあって、そこに蓄積。その後は上記「4.」と同じ様な原理で、リトライ
  していいケースと、リトライしてはいけないケースが出てくる。
-------------------------------------






「想像」というより

「妄想」かもしれませんが。



ともかく、ここで問題になるのは、結局オーダが成功したのか失敗したのかが明確に判らない点。
ヘルプ上では「最低1分(at least, in 1-minute time)」とかいてあるが、この「最低」という箇所。
つまり、最大どれだけ待てば正しい事が判るのかが不明という事。

でも、実際問題リトライした結果、両方オーダが通ってしまって問題になるケースを考えて見ると、実はOrderSend()だけじゃないかなぁと思った。

OrderModify()の場合は、2回目で「ERR_NO_ERROR」が返却されるだけだし、OrderClose()の場合だと、「ERR_INVALID_TRADE_PARAMETERS」が返ってくるんだと思う。
なので、それらのエラーコードのハンドリングさえ間違えなければ問題にならない。

でも、OrderSend()の場合は、本来1つだけのポジションを持ちたかったのに、最悪リトライの回数分だけ同じポジションを持ってしまうという、非常に困った事に。(オーバ・リスクにもなるし)

しかも最大何分待てばいいのかもわからん。
保留中オーダの有無を確認するAPIも無い。










一体どうせよと?


といっても、話が進まないので、幾つか案を考えて見た

【リトライ方式案】
---------------------------------
案1.142/143は発生しない事を期待する
   142/143はまだ発生してない。でも、「ERR_TRADE_TIMEOUT」は実際発生している。
   けど、「ERR_TRADE_TIMEOUT」が発生しているという事は、「トレードサーバ」
   がまともに動いていないんだから、十中八九待っててもオーダは成功しない。
   だから、成功するまでリトライを繰り返す。

   そんな勇気はありません。。

案2.一定時間待つ
   最低1分という事なので、多めに見て2倍の2分まってから、OrderSelect
   ループで保有中ポジションを確認して、ポジション無しであれば、リトライする。
   ロジックは簡単だけど、確実じゃないのも事実。

案3.リトライせずに、次のティックデータ受信まで待つ
   ティックデータ受信できているという事は、「トレードサーバ」と「ディーラ」が正常
   という明かしと考えると、一番楽だし確実。
   でもそんな事はどこにも書いてないし、実際1つのMT4ではHTTPSのセッションが
   2本張ってある。今の想像では、2本のうち1本はオーダ送信用、もう一本はティックデータ
   受信用。これが事実であれば、この対処は不適切な可能性が高くなる。

案4.リトライ成功後不要なオーダを削除する
   とりあえず成功するまで通常のリトライ(500msec×240回)して、成功したらOrderSelect
   のループで、保有中ポジションを確認して、不要なオーダは削除する。
   成功したという事は、「トレードサーバ」も「ディーラ」も正常に戻ったはずで、
   「待ち行列」に溜まっていたデータも処理された後「だと思う」から。
   仮説としては、「待ち行列」に溜まっていたデータから優先的に処理されるという部分。
---------------------------------


確実なのは「案4」っぽいが、面倒。。
できれば一時的にでも不用なポジションは持ちたくない。


「案2」/「案3」/「案4」をあわせた、「案5」を考えて見た。

案5.「案2」+「案3」+「案4」+α
   1分(仮)はリトライしない。リトライは1分経過後のティックデータ受信時。
   リトライ成功後、同じ通貨ペアで複数ポジションを持っていないか確認して、不要ポジション
   は削除する。




なんか、いい感じかも。


さて、次は「不用なポジション」をどれにするかが不明確な点。
つまり、古いオーダと、新しいオーダのどれを削除するか。

【削除する不要オーダ選択案】
-----------------------
案1.最新のオーダのみ残して、それ以前のオーダは全て削除する
   ・ロジックは単純になりそう。最後に成功したチケット番号とか、発注バーとか
    は最新バーのデータを使えばいいので、古いオーダの情報を保管しなくてもいい。
   ・本来発注したかったタイミングからずれているから、トレードルール
    からずれるし、逆指値注文の場合、古いオーダが注文約定されてしまっている
    ケースでは、無駄な売買になってしまう。
案2.一番古いオーダのみ残して、それ以降のオーダは全て削除する
   ・ロジックが複雑になりそう。最初のOrderSend()時の情報だけを覚えておけばいい訳ではなく、
    リトライ毎の注文時情報を保管しておかなければならないので、面倒。
    #最初のOrderSend()は失敗、2回目はエラー応答だが結果成功、3回目は正常応答の様なケース
   リトライ毎に情報を覚えておいて、しかもMT4を再起動しても大丈夫な様にしようとすると、
    リトライ毎のOrderSend時の情報を大域変数に覚えておかないといけない。
    #コメントに情報付与しておく手もあるけど。
   ・本来発注したかったタイミングに近いので、トレードルールに忠実になる。
    注文約定する順序で言っても、古い方が優先されると思うので、逆指値注文の場合は、
    新しいオーダはまだ注文約定されていない可能性が高い。
-----------------------

一長一短。
プログラムの単純さでいえば「案1.」、トレードの優位性という観点に立つと「案2.」




頭の中は

天使と悪魔

の戦い


プログラムを複雑にしてバグを作りこんだら自爆してる状態やし、かといって不利なトレードもしたくない。結局、発生頻度はどの程度やねんという問題でもある。

今はデモ口座で、本来4H足を使った売買ルールのところを5分に短くして、8日間たっているが、
実際今回テーマにしているエラーコードが返却されたのは、ERR_TRADE_TIMEOUT」が1回だけ。
しかも、最初のリトライで成功している。OrderModifyだった事もあったので、不用なリトライだったとしても、問題無いケース。

つまり、発生頻度は低いから「案2.」にしたとしても、トレードの優位性がそんなに劇的に変わるとも思えない。

だとしたら、複雑にしてバグを作りこんでしまうより、シンプルな「案1.」の方がよさそう!


なので、結論を纏めると。

【処理方式の結論】
-------------------------------
ERR_TRADE_TIMEOUT」/「142」/「143エラー検出時の動作は以下の通りにする。
1.OrderModify/OrderCloseは通常のリトライ(500msec×240回)を実施
  ただし他のエラーハンドリングは以下。
  ・OrderModify → ERR_NO_ERRORは成功扱い
  ・OrderClose/OrderModify
   ポジションが決済されていれば、ERR_INVALID_TRADE_PARAMETERSは成功扱い
2.OrderSendは以下の通り
  1分(仮)はリトライしない。リトライは1分経過後のティックデータ受信時。
  リトライ成功後、リトライで成功したチケット番号以外のポジションは全て決済する。
  リトライ前のティックデータ受信時に、ポジション保有を検出した場合はリトライしない。
  この場合は、内部処理としてポジションの情報を大域変数に保存する。 
-------------------------------





  

それでも結構な修正が

必要なんですけど。。





そして、誰かがスマートな方法を教えてくれる事を願いつつ、「FXシステムトレード初心者奮闘記」の「MT4用EA開発時代」は、新たなバグが出ない限りECNへの対応に向かうのでした。
#今のところ2日間警告メール受け取っていない!!

0 件のコメント:

コメントを投稿