2011/07/08

MT4用EA開発時代 - EA開発共通部品「ahfw」/障害監視システム「ahfw_hc」バージョンアップと、スレッド障害対応



さて前回は、MT4用EAをデモ口座で動かしていたときに、ネットワーク障害が発生した様子を書きました。今回は、既にリリース済みの、EA開発共通部品「ahfw」と、障害監視システム「ahfw_hc」バージョンアップのお知らせと、その背景であるスレッド障害対応に関する内容です。

まずは、バージョンアップのお知らせ。

【バージョンアップ版の主な変更点】
-----------------------
1.MT4用EA開発共通部品「ahfw」ベータ版(Ver1.03)
  障害監視システム「ahfw_hc」のスレッド障害に対応。
  オーダ操作エラー検出時の制御を一部変更
  バグフィックス、その他諸々。

  ●ダウンロード用HP
          
2.MT4障害監視システム「ahfw_hc」ベータ版(Ver1.01)
  前回公開してなかった「プロセス障害監視アプリケーション」のソース公開
  上記ソースのコンパイル方法についてドキュメントに追記
  スレッド障害タイムアウトデフォルト値の変更
  バグフィックス、その他諸々。

  ●ダウンロード用HP
-----------------------


さて、ここからが本題。


本当はもっと早くMT4用EA開発共通部品「ahfw」のスレッド障害対応版をリリースする予定やったけど、甘かった。

スレッド障害検出方式は、start()関数が開始されてから、プロパティで設定した一定時間を経過してもstart()関数が終ってなければ「スレッド障害」と判断してMT4を自動再起動。そしてこのタイムアウト値を、2分30秒にしていた。

そしてデモ口座で評価開始。





翌朝、「スレッド障害を検出したからMT4を再起動した」
という旨の警告メールが。。。


【スレッド障害検出になった原因】
------------------------------
ログを見てみると、OrderSend()でタイムアウトが発生していて、タイムアウト検出まで3分34秒かかっていた。つまり、「スレッド障害」タイムアウト値の2分30秒以上に時間がかかったので、「スレッド障害」と誤検出。

しかも、前回リリースしていたMT4障害監視システム「ahfw_hc」で「スレッド障害」検出時は、監視用ファイル更新を停止させる事で、一定時間監視用ファイルの更新がされなかったらMT4を強制終了して、MT4を再起動するという仕様。ちなみにこのタイムアウト値のデフォルト値は1分。

MT4が起動されEAが再開した頃には、オーダ操作をしたかったバーから次のバーに時間が進んでしまっていたので、これでも警告メールが発生。

5分足で動作させてたEAで、スレッド異常検出まで2分30秒、MT4停止まで+1分、MT4再起動まで+約30秒。合計すると約4分。

1つのMT4で5分足EAを10通貨ペア同時実行させてたので、そもそもの該当オーダ操作がが遅れて開始されていて、結果オーダ操作再開可能になったタイミングでは次のバーに進んでしまったという結果に。
-------------------------------


MT4が再起動されてしまった事も不本意だし、5分足とはいえ、オーダ操作したかったバーで操作できなかった事も不本意。


【最初の対処】
--------------------------------
まず、検出してからMT4EA開始まで5分以内に収めたいと思った。
でも今の方式だと、オーダ操作タイムアウト検出に3分34秒、MT4停止まで1分、MT4再起動まで30秒。誤検知せずに5分以内に収めようとしても、全部足すと5分以上かかるので、そもそもの方式を変えないといけない。

1.MT4停止までの「1分」を時間短縮する
  スレッド障害検出してからMT4停止まで1分余計にかかっていて、この余計な1分に特に
  意味は無い。

  なので、スレッド障害検出後の再起動方式を見直す事にした。

  つまり、監視用EAから「MT4を再起動してくれ」というファイルを出力して、MT4の
  プロセスを監視しているプロセスがそのファイルを検出したら直ぐに、MT4を再起動する様に
  修正。

2.スレッド障害検出までのタイムアウト値の2分30秒の見直し
  今回オーダ捜査タイムアウト検出に3分34秒かかっているので、少なくともスレッド障害
  タイムアウト値は、3分34秒以上じゃないといけない。
  そしてMT4の再起動に約30秒かかっているので、5分以内に収め様とすると、スレッド障害
  タイムアウト値は、
4分30秒未満じゃないといけない。
  しかも、EA再開から一定の処理時間を確保しないといけないので、それも考慮する
  必要がある。なので、ざっくりとスレッド障害検出タイムアウト値を4分にしてみた。
--------------------------------


さて、この対処を行ってMT4EAデモ環境にリリースして、翌朝。






スレッド障害検出した旨の警告メールが。。。
 

 
しかも、MT4は再起動されているが、
EAの動作が停止している。








これじゃ、障害監視システムじゃなくて、

障害発生システムやん。。



【EAの動作停止の原因と対策】
----------------------------
1.スレッド障害発生原因と対策
  ●発生原因
   今回はOrderModify()でタイムアウト1分30秒強が3連続で発生。

  ●対処方法
   そもそもサーバ起因でスレッド障害になるのはおかしいから、スレッド障害タイムアウト値を、
   オーダ操作のリトライ間隔/回数での最悪値を元に計算することにした。

   タイムアウト発生→リトライ回数(240)を元に計算してみると、とんでもない
   時間になってしまって、本来検出したいスレッド障害だったとしても検出までの
   遅延ちょっと許容できない。

   そこで、OrderSend()でタイムアウト発生時は、一旦start()関数を抜ける様にしていたが、
   OrderModify()/OrderClose()/OrderDelete()ではリトライする仕様のままにしていた。
   今回の修正で、OrderSend()と同様にstart()関数を一旦抜ける様に修正した。
   (なので次のティックデータ受信時にリトライすることになる)

   そして、上記処置前提で、1ティックでかかる時間の最悪値(全てリトライ)した
   場合の処理時間を試算してみると、7分30秒。

   5分以内に「スレッド障害」検出タイムアウトを収めるのはあきらめて、少し大目に
   丸めて、「スレッド障害」検出タイムアウト時間を10分に変更。

  ●対処方法に関する考察
   この理由だけだと、「スレッド障害」誤検出対策のためにエラー制御を変更する
   という本末転倒な状態に見えるけど、他にも理由がある。

   そもそもタイムアウト発生時は1回発生しただけでも、オーダ操作用通信スレッド
   を1つのEAで長時間占有してしまう事になる。

   start()関数を排他利用する前提で考えると、タイムアウトが発生し続けると、
   他に同居しているEAでの発注判定とか決済判定すら実行されずに、次のバーに
   進んでしまう事になる。

   売買ルールにもよるけど、本当だったら決済しないといけないタイミングなのに、
   次のバーに進んでしまう事で、決済が必要な事が判らないままになってしまう。
   これは流石に良くないので、OrderModify()/OrderClose()/OrderDelete()でも、
   タイムアウト検出時は一旦start()関数を抜ける事にした。

   この対処で、そのバーで決済できるかどうかはともかく、決済が必要という判断
   がされ、決済されるまでリトライを続ける事ができる様になる。

  ●start()排他制御は必要かどうか
   すると、そもそもstart()関数の排他制御が必要なのか?という疑問は残ってしまう。
   start()関数を排他制御している理由は、「OrderSelect問題」が発生した時の暫定
   処置。つまり、原因不明な事象に対して、有効かどうかがはっきりしていない処理。

   実際、4つのデモ環境のうち、1つのMT4だけstart()排他制御を6/30に外して1週間たった
   けど、「OrderSelect問題」は再発してないし、start()排他制御しているところでも、
   再発してない。そんな時、以下の記事を発見。


   「OrderSelect問題」とは関係なさそうな症状だし、その後改修されてるから
   関係無いといえば関係ないんだけど、オーダ操作の同時実行は、問題を誘発して
   しまうという、そもそものツクリになってる気もしてきた。

   ともかく「OrderSelect問題」は再現待ち。
   
   原因不明な以上、start()関数排他制御を使う可能性が残っているので、使った時の
   事を踏まえた設計にした方が無難。

2.今回なぜMT4再起動後に、EAの動作が停止してしまったか

  流石に直ぐにわかった。。

  ●EA動作停止の原因
   MT4用EA開発共通部品「ahfw」では、start()排他制御していて、大域変数(Global Variables)
   を使用。そして、start()開始時にロックされた状態になる。ロック中にMT4を強制終了
   させて再起動させたから、大域変数上の状態はロックされたまま。
   ロックされたままなので、start()関数が動かなかった。

  ●処置方法
   1つのMT4で複数EAを実行している場合、それぞれのinit()関数内で、排他制御用の
   大域変数を生成しているが、これがなかなか面倒。

   排他制御には、GlobalVariableSetOnCondition()という関数を使用して実装してるけど、
   この関数は、指定した大域変数が存在しなければエラーになってしまう。

   なので、init()関数内で排他制御用の大域変数を生成するんだけど、1つのMT4
   で複数EAを実行していると、init()の実行とstart()排他制御が平行して動いてしまう。
   なので、init()関数内での排他制御用大域変数生成時の初期値を無条件にゼロ
   (アンロック状態)にするわけにはいかない。

   なので、日本語で書くのは難しいけど、自分のセマフォ値(排他制御用の識別子)を
   大域変数で格納しておいて、init()関数内では、セマフォ値を全精査し、今ロック
   しているセマフォ値に一致しているものがなければ、一旦自分のセマフォ値でロックして、
   すぐにアンロックする様に修正。
   #詳細は今回リリースしたMT4用EA開発共通部品「ahfw」のFWinit()関数を見てみてください。。

   一応、200回強のスレッド障害→MT4再起動のテストを実施して無事OKでした。。
----------------------------




こんな記事書いてて、






使ってくれる人いるんだろうか。。。





そして、実は次のネタに困りながら、「FXシステムトレード初心者奮闘記」の「MT4用EA開発時代」は、どこかに向かっていくのでした。
#某VPS会社から、詳細調べるからもうちょっとだけ待ってという旨の一次回答があった!!教えてくれるっぽい!!

0 件のコメント:

コメントを投稿