2011/08/27

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



さて前回は、EA無言停止対策とデモ口座期限切れ対策の考慮漏れについて検討している様子を書きました。今回は、MT4用EA共通部品「ahfw」MT4障害監視システム「ahfw_hc」のバージョンアップリリースリリースにいたるまでの諸々について書いてみたいと思います。

【本日のお題】
-----------------------
1.テスト状況について
2.OrderDelete()後のOrderSelect(SELECT_BY_TICKET)について
  多分MT4をバグ発見
3.MT4用EA共通部品「ahfw」バージョンアップリリースのお知らせ
4.MT4障害監視システム「ahfw_hc」バージョンアップリリースのお知らせ
-----------------------


【テスト状況について】
-----------------------------
さて、以前のテスト計画で決めてた8月末期限ですることを振り返ると、以下の様な感じ。

●8月の作業予定
 1.設計書レビュー
 2.プログラムソースレビュー
 3.PC切替テスト
 4.トレード用PCクリアインストール
 5.最新Buildへのバージョンアップ

以前の記事では、上記「1.」のネタである設計書を書いただけで発見したバグを修正した
話で、上記「1.」自体はまだだった状態。

今回無事に、「3.PC切替テスト」まで完了しました~



わ~い!! パチパチパチ!!!


と、一人で盛り上がってますが、前述の「1.設計書レビュー」と「2.プログラムソースレビュー」
見つかるわ見つかるわ、たくさんのバグたち。

●見つかったバグ
 1.設計バグ
   ・CalcLotSize()でエラー検出時にゼロを返却しているが-1を返却すべき
   ・GetMaxLotSize()でエラー発生時にゼロを返却しているが-1を返却すべき
 2.重要度-大
   ・EAOnPendingOrderOpened()の呼び出し方が設計書と著しく異なる
   ・EAOnClose()呼出し時に、vCmd , vCondが正しく渡せない事がある
   ・オーダ保有状態取得時(OrderSelectループ)に、決済済みオーダと未決済オーダの両方
    の情報がある場合、保有オーダ無しと判断されるケースがある
   ・大域変数設計変更によるデータ変換処理失敗時に、保有中オーダがあると不正なオーダ
    操作となる可能性がある
   ・決済処理で、発注タイムアウト後の不要オーダ削除失敗後の場合、成功した発注オーダ
    が決済されてしまう
   ・start()処理が一回動いた後、deinit()/init()で処理に時間がかかった場合、スレッド停止障害
    と誤検知される
    可能性がある
   ・トレードログ出力で、不要な判定文があり不要なトレードログが出力される可能性がある
   ・AdjustTpPrice()関数で、ストップレベルによる価格補正が間違い
   ・CheckLotSize()関数で、指定ロットが最大ロット数を超過しており、通知が必要な引数を
    指定されていた場合にメール通知されない
 3.重要度-中
   ・AdjustLotSize()関数ででエラー検出時に引数のロット数変数に-1を設定していない
   ・AdjustSlPrice()/AdjustTpPrice()関数で、現在の価格が凍結レベル内の時、引数の価格変数に
    現在値を設定していない
   ・決済処理でリトライすると、トレードログの決済時のスリッページ出力がずれてしまう
   ・決済処理で凍結レベルが設定されている場合、SL/TP価格が凍結レベル範囲内かどうかの
    判定がおかしい
   ・チャート変更時/EAリコンパイル時に、FWinit()が2回呼び出されてしまう
   ・deinit()での大域変数クリア処理で、FW_StopAllが抜けている
   ・deinit()で呼び出し理由がチャート変更の場合は大域変数削除されていなかった
   ・init()時のアカウント切れの際の大域変数クリアで、FW_StopAllが抜けている
   ・init()時のセマフォキー生成処理でエラー発生時にリトライされているが、リトライは不要
   ・start()関数排他制御処理内のエラー処理が誤まっており、start()でフリーズする可能性がある
   ・SL/TP変更処理時、リトライループ中に決済されていた場合、情報レベルの運用ログが
    出力されない
   ・メール通知や運用ログ出力時のチケット番号が不定になる
   ・決済後処理で、FWOrderCondが不正な値になる
   ・AdjustLotSize()で、ロット数有効桁数を計算するタイミングが、ゼロ除算チェック前
   ・AdjustSlPrice()/AdjustTpPrice()で、エラー検出時に引数の価格変数に-1を設定していない
   ・大域変数の削除漏れ
   ・メールサブジェクトにEA名とMAGICが付与されていない
 4.軽微なバグ/改善
   ・オーダ保有していなかった場合に、コールバック関数に渡すvCmdが不定になる
   ・初期状態ではオーダ保有状態は不明とすべき。デフォルトでORDER_NONEになっている。
   ・テスター開始時の大域変数削除処理で、FW_StopAllが抜けている
   ・EAGetModifyPrice()でSLもTPもゼロ以下の場合に、同じバーで何回もEAGetModifyPrice()が
    呼びだされる
   ・発注前のSL価格と発注価格の上下関係チェックで、補正前の価格が使用されている
   ・発注前のTP価格と発注価格の上下関係チェックで、補正前の価格が使用されている
   ・2バー前の時のEAToOrder()に渡す参照変数をゼロで初期化していない
   ・OnError()関数で引数のvLevelが不正な場合にログ出力されない。
   ・OnError()関数のコメントで説明不足の箇所あり
   ・不要なコメント行やロジックがある
   ・大域変数のセットで、エラー判定は戻り値がゼロで判定すべき
   ・その他軽微な改善

プログラムソースから設計書をおこして、その設計書を元にプログラムソースをレビューで
なんでこんなに見つかるんや?という疑問もあるかもしれないですけど、こういうもんです。。



やっただけの価値があった!!



と素直に喜んでおこう。

今は「4.トレード用PCクリアインストール」に向けた環境構築手順書までは
できて、土日をかけて実施する予定。作業が間に合わなくても、暫定予備PCに切り替えれる
様にしたので、大丈夫!!
-----------------------------

さて、次の話題。

【OrderDelete()後のOrderSelect(SELECT_BY_TICKET)について】
----------------------------------
さて、突如現るプログラミングの各論。一体なんなのかというと、あるバグを修正するのに、
今までうやむやにしていた事をはっきりさせる必要があった事が1点。

それは、OrderDelete()したチケット番号を指定してOrderSelect(SELECT_BY_TICKET)すると、
FALSEが返却され、失敗するという点。

まず、ヘルプを見てみると。

1.OrderSelectヘルプ(非公式有志)の抜粋
  The function selects an order for further processing. It returns TRUE if the function succeeds. 
   It returns FALSE if the function fails. To get the error information, one has to call the 
   GetLastError() function.
   この関数は、さらに先の処理のためにオーダーを選択します。この関数が正常終了した場合、
   戻り値は TRUE です。この関数が失敗した場合、戻り値は FALSE です。詳細なエラー情報
   を取得するには、GetLastError() を呼び出します。

   The pool parameter is ignored if the order is selected by the ticket number. The ticket number 
   is a unique order identifier. To find out from what list the order has been selected, its close
   time must be analyzed. 
   If the order close time equals to 0, the order is open or pending and taken from the terminal 
   open positions list. One can distinguish an open position from a pending order by the order 
   type. If the order close time 
   does not equal to 0, the order is a closed order or a deleted pending order and was 
   selected from the terminal history. They also differ from each other by their order types.
   もし、オーダーがチケット番号で選ばれる場合、pool パラメータは無視されます。チケット番号は、
   一意のオーダー識別子です。どのリストからオーダーが選択されたかを認識するには、その決済
   時間を調べる必要があります。もし、決済時間が 0 
   ならば、そのオーダーは保有中か待機中で、ターミナルの保有中ポジション一覧からの取得
   されています。注文タイプによって、
   保有中ポジションを保留中オーダーと区別することができます。もし、オーダーの決済時間が 0 
   でない場合、そのオーダーは、決済オーダーか、削除された保留中オーダーで、ターミナルの
   履歴から選択されています。それらは、同じく、それらの注文
   タイプによって異っています。
  
   ここのポイントは、チケット番号指定の場合は、第三引数のpoolが無視される点と、失敗した
   場合はGetLastError()にエラーコードが設定されるという点。そして、オーダーの決済時間が 
   0 でない場合、そのオーダーは、決済オーダーか、削除された保留中オーダーで、ターミナルの
   履歴から選択されています。」という記述。

   つまり、削除(OrderDelete())済みもOrderSelect()できるという事。
  
   次に、「ランタイムエラー」(Runtime errors)のヘルプを見てみると。
   
   Other functions never change the last_error variable value.
    その他の関数は、last_error 変数値を変更することはありません。
    AccountBalance, AccountCompany, ・・・OrderSelect, ・・・・WindowYOnDropped, Year

   片やエラー情報はGetLastError()で取得せよと書いてあるのに、片やエラー情報は設定され
   ないと書いてある。


   
   
   どっちやねん。
   
   
2.実際に簡単なプログラムで試して見る
  試して見たのは以下のプログラム。

  ●プログラムソース  
int start()
  {
//----
    int ticket = 0;
    Print("Start!!");
    while( TRUE )
    {
        ticket = OrderSend(Symbol(),OP_BUYSTOP,MarketInfo(Symbol(),MODE_MINLOT),Ask+30*Point,10,Bid-(MarketInfo(Symbol(),MODE_STOPLEVEL)+1)*Point,0,NULL,11111111);
        if( ticket <= 0 )
        {
            int errno = GetLastError();
            Print("OrderFailed:errno("+errno+") " + ErrorDescription(errno)+")");
            return(0);
        }
        else
        {
            Print("OrderSuccess("+ticket+")");
            break;
        }
    }
    while(TRUE)
    {
        if( OrderDelete(ticket) )
        {
            if( OrderSelect(ticket , SELECT_BY_TICKET ) )
            {
                Print("OrderSelect was succeed : OrderType("+OrderType()+")");
            }
            else
            {
                int err = GetLastError();
                Print("OrderSelect was failed. : OrderTicket("+OrderTicket()+") OrderType("+OrderType()+") errno("+err+"):"+ErrorDescription(err)+")");
            }
            return;
        }
        continue;
    }
   
//----
   return(0);
  }
●ログの結果
15:36:27 XOrderDelete EURGBPfxf.,M5: Start!!
15:36:41 XOrderDelete EURGBPfxf.,M5: open #6430715 buy stop 0.01 EURGBPfxf. at 0.8871 sl: 0.8837 ok
15:36:41 XOrderDelete EURGBPfxf.,M5: OrderSuccess(6430715)
15:36:43 XOrderDelete EURGBPfxf.,M5: delete #6430715 buy stop 0.01 EURGBPfxf. at 0.8871 sl: 0.8837 tp: 0.0000 ok
15:36:43 stdlib EURGBPfxf.,M5: loaded successfully
15:36:43 XOrderDelete EURGBPfxf.,M5: OrderSelect was failed. : OrderTicket(0) OrderType(0) errno(0):no error)
15:36:44 XOrderDelete EURGBPfxf.,M5: Start!!
15:36:44 XOrderDelete EURGBPfxf.,M5: open #6430716 buy stop 0.01 EURGBPfxf. at 0.8871 sl: 0.8837 ok
15:36:44 XOrderDelete EURGBPfxf.,M5: OrderSuccess(6430716)
15:36:46 XOrderDelete EURGBPfxf.,M5: delete #6430716 buy stop 0.01 EURGBPfxf. at 0.8871 sl: 0.8837 tp: 0.0000 ok
15:36:46 XOrderDelete EURGBPfxf.,M5: OrderSelect was failed. : OrderTicket(0) OrderType(0) errno(0):no error)
15:36:47 XOrderDelete EURGBPfxf.,M5: Start!!
15:36:47 XOrderDelete EURGBPfxf.,M5: open #6430717 buy stop 0.01 EURGBPfxf. at 0.8872 sl: 0.8837 ok
15:36:47 XOrderDelete EURGBPfxf.,M5: OrderSuccess(6430717)
15:36:47 XOrderDelete EURGBPfxf.,M5: delete #6430717 buy stop 0.01 EURGBPfxf. at 0.8872 sl: 0.8837 tp: 0.0000 ok
15:36:47 XOrderDelete EURGBPfxf.,M5: OrderSelect was failed. : OrderTicket(0) OrderType(0) errno(0):no error)
15:36:52 XOrderDelete EURGBPfxf.,M5: Start!!
15:36:52 XOrderDelete EURGBPfxf.,M5: open #6430718 buy stop 0.01 EURGBPfxf. at 0.8871 sl: 0.8836 ok
15:36:52 XOrderDelete EURGBPfxf.,M5: OrderSuccess(6430718)
15:36:53 XOrderDelete EURGBPfxf.,M5: delete #6430718 buy stop 0.01 EURGBPfxf. at 0.8871 sl: 0.8836 tp: 0.0000 ok
15:36:53 XOrderDelete EURGBPfxf.,M5: OrderSelect was failed. : OrderTicket(0) OrderType(0) errno(0):no error)
15:36:54 XOrderDelete EURGBPfxf.,M5: Start!!
15:36:58 XOrderDelete EURGBPfxf.,M5: open #6430719 buy stop 0.01 EURGBPfxf. at 0.8871 sl: 0.8837 ok
15:36:58 XOrderDelete EURGBPfxf.,M5: OrderSuccess(6430719)
15:36:58 XOrderDelete EURGBPfxf.,M5: delete #6430719 buy stop 0.01 EURGBPfxf. at 0.8871 sl: 0.8837 tp: 0.0000 ok
15:36:58 XOrderDelete EURGBPfxf.,M5: OrderSelect was failed. : OrderTicket(0) OrderType(0) errno(0):no error)
15:37:05 XOrderDelete EURGBPfxf.,M5: Start!!
15:37:05 XOrderDelete EURGBPfxf.,M5: open #6430720 buy stop 0.01 EURGBPfxf. at 0.8871 sl: 0.8836 ok
15:37:05 XOrderDelete EURGBPfxf.,M5: OrderSuccess(6430720)
15:37:05 XOrderDelete EURGBPfxf.,M5: delete #6430720 buy stop 0.01 EURGBPfxf. at 0.8871 sl: 0.8836 tp: 0.0000 ok
15:37:05 XOrderDelete EURGBPfxf.,M5: OrderSelect was failed. : OrderTicket(0) OrderType(0) errno(0):no error)
   
   
   やっぱり、OrderSelect()全部失敗してるし、GetLastError()ではエラー情報取得できない。
   ちなみに、OrderSelect()の第三引数に、MODE_HISTORY/MODE_TRADESを指定しても結果は同じ。
  
  
  
  
  MT4のバグやんけ。。。。
  #Forex.com Japan Build229
  
  
  つまり、バグの内容は以下の2点。
  OrderDelete()後にチケット番号指定でOrderSelect()できないというバグ
  OrderSelect()失敗時のエラー詳細はGetLastErro()で取得できないというヘルプのバグ
  
  SELECT_BY_POSでMODE_HISTORYをループさせたらOrderDelete()済みのチケット番号
  でOrderSelect()できるかもしれないけど、履歴側はオーダがたくさんあるから処理時間結構
  かかりそう。
  
  なので、そういうもんだとあきらめて、別の手段で対処することにした。
----------------------------------------


さて、ついに各バージョンアップリリースのお知らせです。

【MT4用EA開発共通部品「ahfw」ベータ版バージョンアップリリース】
--------------------------------------
1.今回の修正内容
  以下の以前の記事に書いた内容
  ・今回の記事で記載した摘出バグ
  
  今回の目玉は、上記バグ修正/機能追加以外にも、ユーザーズガイドを
  大幅拡充した点。機能の詳細を書いたのでページ数も92ページまで増加。
  
  エラーコード毎のエラー制御処理の一覧だけでも役に立つかな??
  
2.ダウンロードページ
  以下のブログページからダウンロードしてください。
  #今回はベータ版1.04です。
  

--------------------------------------

【MT4障害監視システム「ahfw_hc」ベータ版バージョンアップリリース】
--------------------------------------
1.今回の修正内容
  ・ユーザーズガイド
   わかり易い様に表現を見直し
   「ahfw」を使わない場合にEAでの対応する方法について追記
2.ダウンロードページ
  以下のブログページからダウンロードしてください。
  #今回はベータ版1.02です。
  
  
--------------------------------------





今のところ予定通り!!

オチも無し!!




そして、実は気が重いPCクリアインストールを控えながら、「FXシステムトレード初心者奮闘記」の「MT4用EA開発時代」は、機能試験に進むのでした。
#暫定予備用PCがめっちゃ熱くなっててビックリした。。。

0 件のコメント:

コメントを投稿