English Русский 中文 Español Deutsch Português
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法

エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法

MetaTrader 4 | 6 2月 2012, 13:29
2 537 0
Andrey Khatimlianskii
Andrey Khatimlianskii

1. MT4 のクライアントターミナルの「トレードコンテキスト」とは何でしょう。

メタエディタのリファレンスから参照します。

    EAやスクリプトでトレードするには、プログラム上で1つのスレッドだけを稼働させます。 (EAおよびスクリプトから自動化されたトレードのコンテキスト)こういう訳で、EAによってコンテキストが占有されている場合、他のEAやスクリプトを呼び出すことはできません。この場合、146エラーとなります。(ERR_TRADE_CONTEXT_BUSY) 。

一度に 1つのEAだけ (スクリプト) トレードが可能と言って良いでしょう。トレードを開始しようとすると、他のすべてのEAは、エラー146 によってストップされます。この記事では、この問題の解決策を見出します。


2. IsTradeAllowed()

トレード状況がビジー状態かどうかを調べる最も簡単なメソッドは、IsTradeAllowed() という名前の関数を使用することです。

メタエディタのリファレンスから参照します。

    "bool IsTradeAllowed()

    EAに対してトレードが許可されいる場合true、それ以外の場合は false を返します。

つまり、IsTradeAllowed() 関数がTRUEを返す場合にのみトレードできます。

トレード操作の前にチェックを行う必要があります。

関数の間違った使用法の例:

int start()
  {
    //トレード状況がフリーかどうかを確認してください。
    if(!IsTradeAllowed())
      {
        //sTradeAllowed() 関数がFALSE が返された場合
        Print(「トレード状況がビジー状態!EAはポジションを持たない!");
        //エキスパートの操作を終了します。このようになり、次のティック時リスタートします。 
        //
        return(-1);
      }
    else
      {
        //IsTradeAllowed() 関数が true の場合、その旨がユーザーに通知されます 
        //続き
        Print(「トレード状況クリアです!稼働中...");
      }
    //エントリーするかどうかを確認してください。
    ...
    //ストップロスとテイクプロフィトのサイズを計算します。
    ...
    //ポジションを開く
    if(OrderSend(...) < 0) 
        Alert("Error opening position # ", GetLastError());
    return(0);
  }

この例では、コンテキストのトレード状態は start() 関数の非常に初めにチェックされます。これは間違った考えです。つまり、トレードコンテキストは他のEAによって占有されている可能性があります。このような場合は、ポジションは開きません。

適切な使用例:

int start()
  {
    //エントリーするかどうかを確認してください。
    ...
    //ストップロスとテイクプロフィト レベルだけでなく、ロットサイズを計算します。
    ...
    //トレード状況がフリーかどうかを確認
    if(!IsTradeAllowed())
      {
        Print(「トレード状況がビジー状態!EAはポジションを持たない!");
        return(-1);
      }
    else
        Print(「トレード状況クリアです!ポジションを開く...");
    //チェックが成功した場合は、ポジションを開く
    if(OrderSend(...) < 0) 
        Alert("Error opening position # ", GetLastError());
    return(0);
  }

ポジションを開く前に、トレードコンテキスト状態はここですぐに確認されます。 このわずかな間に他のEAが邪魔をする可能性は極めて少ないです。しかし、まだ考慮すべきものが存在します。

このメソッドは、2 つの重要な欠点があります。

  • EAの状態を同時チェックし、同時にトレードしようとする可能性があります。
  • チェックが失敗した場合、EAは次のティックでトレードをもう一度します。このような遅延は非常に望ましくありません。

2番目の問題は比較的シンプルに解決できます。トレードのコンテキストをエントリーされるまで待機するだけです。EAは、他のEAがを完了した後すぐにトレードを開始します。

おそらく次のようになります。

int start()
  {
    //エントリーするかどうかを確認してください。
    ...
    //ストップロスとテイクプロフィト レベルとロットサイズを計算します。
    ...
    //トレード状況がフリーかどうかを確認してください。
    if(!IsTradeAllowed())
      {
        Print(「トレード状況がビジー状態!フリーになるまで、待機);
        //無限ループ
        while(true)
          {
            //EAがユーザーによってストップされた場合、停止します。
            if(IsStopped()) 
              { 
                Print(「EAはユーザーによってストップされました!」); 
                return(-1); 
              }
            //トレード状況はフリーとなっている場合、ループを終了し、トレードを開始
            if(IsTradeAllowed())
              {
                Print("Trade context has become free!");
                break;
              }
            //ループ脱出条件を満たしてない場合""0.1 秒待ってください。
            //再起動の確認
            Sleep(100);
          }
      }
    else
        Print(「トレード状況クリアです!ポジションを持つ...");
    //ポジションを開くしようとしました。
    if(OrderSend(...) < 0) 
        Alert("Error opening position # ", GetLastError());
    return(0);
  }
再び問題点があります。
  •  IsTradeAllowed() 関数は、コンテキスト状態に対してだけでなく、EAのトレード有効化/無効化にも有効なので、EAが無限ループの中にい続け、チャートから手動で外さない限り、終わらない可能性があります。
  •  EAがトレードコンテキストがフリーになるまで待機した場合、わずか数秒ですが、レートが変化します。また、前のレートでトレードすることはできません。データはリフレッシュされるべきであり、トレードの前に再計算する必要があります。

修正後のコードは次のようになります。

//EAが、トレードまで待機する時間 (単位は秒)  
//コンテキストが (ビジー状態) の場合はフリー
int MaxWaiting_sec = 30;
int start()
  {
    //エントリーするかどうかを確認してください。
    ...
    //ストップロスとテイクプロフィト レベルとロットのサイズを計算します。
    ...
    //トレード状況がフリーかどうかを確認してください。
    if(!IsTradeAllowed())
      {
        int StartWaitingTime = GetTickCount();
        Print(「トレード状況がビジー状態!フリーになるまで、待機);
        //無限ループ
        while(true)
          {
            //EAがユーザーによって終了した場合は、操作をストップします。
            if(IsStopped()) 
              { 
                Print(「EAはユーザーによってストップされました!」); 
                return(-1); 
               }
            //変数で指定された時間より長く待っていた場合 
            //MaxWaiting_sec、ストップ操作
            if(GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000) 
              {
                Print("The standby limit (" + MaxWaiting_sec + " sec) exceeded!");
                return(-2);
              }
            //トレード状況がフリーになっている場合
            if(IsTradeAllowed())
              {
                Print(「トレードコンテキストがフリー!」);
                //相場情報を更新します。
                RefreshRates();
                //ストップロスとテイクプロフィト レベルを再計算します。
                ...
                //ループから離れ、トレードを開始                
                break;
              }
            //ループ脱出条件を満たしてない場合、0.1秒待つ 
            //チェックを再開します
            Sleep(100);
          }
      }
    else
        Print(「トレード状況クリアです!ポジションを持つ...");

    //ポジションを開くしようとしました。
    if(OrderSend(...) < 0) 
        Alert("Error opening position # ", GetLastError());
 
    return(0);
  }
上記の例では、次のものを追加しました。
  • RefreshRates() とそれに伴うストップロスとテイクプロフィトの再計算、相場情報の更新です。
  • 最大待機時間はMaxWaiting_secで、これを超えた場合EAはストップします。

これは、すでにEAで上記のコードで使用できます。

最後の仕上げです。別の関数をチェックします。EAとその使用法の統合を簡素化させます。

/////////////////////////////////////////////////////////////////////////////////
//nt _IsTradeAllowed (int MaxWaiting_sec = 30)
//
//トレードのコンテキスト状態を調べます。リターン コード:
//1-トレードコンテクストがフリーの場合、トレードを許可
//0 - トレードコンテクストがビジーで、その後フリーにフリー時にのみトレードを許可します。 
//マーケット情報が更新されています。
//-1 - トレード状況がビジー状態か、ユーザーによって中断された (EAはから削除されました 
//グラフ、ターミナルはシャット ダウン、チャートの期間/シンボルを変更したなど。)
//-2 - トレード状況がビジー状態、最大待機時間制限に達すると (MaxWaiting_sec)。 
//EAは、(チェック ボックス許可ライブトレード」トレードすることはできません。 
//エキスパート設定で)。
//
//MaxWaiting_sec - 関数が待機する時間 (単位は秒) 
//トレード状況がフリー (忙しい場合)。既定では、30。
/////////////////////////////////////////////////////////////////////////////////
int _IsTradeAllowed(int MaxWaiting_sec = 30)
  {
    //トレード状況がフリーかどうかを確認してください。
    if(!IsTradeAllowed())
      {
        int StartWaitingTime = GetTickCount();
        Print(「トレード状況がビジー状態!フリーになるまで、待機);
        //無限ループ
        while(true)
          {
            //EAがユーザーによって終了した場合は、操作をストップします。
            if(IsStopped()) 
              { 
                Print(「EAはユーザーによって終了されました!」); 
                return(-1); 
              }
            //待機時間が指定した時間を超える場合、 
            //MaxWaiting_sec 変数、ストップ操作
            if(GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000)
              {
                Print("The waiting limit exceeded (" + MaxWaiting_sec + " seconds)!");
                return(-2);
              }
            //トレード状況がフリーになっている場合
            if(IsTradeAllowed())
              {
                Print("Trade context has become free!");
                return(0);
              }
              //ループ脱出条件を満たしてない場合、0.1秒待つ 
              //チェックを再開します
              Sleep(100);
          }
      }
    else
      {
        Print(「トレードコンテキストがフリー!」);
        return(1);
      }
  }

この関数を使用したEAのテンプレート:

int start()
  {
    //エントリーするかどうかを確認してください。
    ...
    //ストップロスとテイクプロフィト レベルとロットサイズを計算します。
    ...
    //トレード状況がフリーかどうかを確認してください。
    int TradeAllow = _IsTradeAllowed();
    if(TradeAllow < 0) 
      { 
        return(-1); 
      }
    if(TradeAllow == 0)
      {
        RefreshRates();
        //ストップロスとテイクプロフィト レベルを再計算します。
        ...
      }
    //ポジションを開く
    if(OrderSend(...) < 0) 
        Alert("Error opening position # ", GetLastError());
    return(0);
  }

結論です。

IsTradeAllowed() 関数は、使いやすいですし、同時に 2 つまたは 3 つのEAのコンテキストのトレードへのアクセスに最適です。多くのEAが同時に働くときエラー 146 から保証しません。「許可するライブトレード」が無効な場合、EAの「ハングアップ」も原因になります。

よって、グローバル変数の「セマフォ」の代替ソリューションを検討します。

 

3. クライアント ターミナルのグローバル変数

まず、定義です:

クライアント ターミナルのグローバル変数は、すべてのEA、スクリプトおよび評価インジケーターにアクセスできる変数です。(アクセスを分散する) 他のEA、EAによって作成されたグローバル変数を作成することを意味します。

グローバル変数を操作する MQL 4 で提供される関数があります。

  • GlobalVariableCheck() - グローバル変数が存在するかどうかを確認する
  • GlobalVariableDel() - グローバル変数を削除する
  • GlobalVariableGet() - グローバル変数の値を取得する
  • GlobalVariableSet() - 作成またはグローバル変数を変更する
  • GlobalVariableSetOnCondition() - ユーザーが指定したグローバル変数の値を変更。前の値は新しい値が設定され、GlobalVariableSet() とは異なります。セマフォを作成する関数は、この関数です。
  • GlobalVariablesDeleteAll() - すべてのグローバル変数を削除する (誰がこれを必要としているのでしょう:)

なぜ、GlobalVariableSetOnCondition()を使い、GlobalVariableGet() と GlobalVariableSet()を使わないのでしょうか。同じ理由で、 2 つの関数の使用の間には隙間があります。セマフォの切り替えに別のEAを置くことができます。しかし、これは我々が今必要なものではありません。

 

4. セマフォの基本的な概念

トレードEAは、セマフォの状態を確認する必要があります。セマフォが「レッドライト」を示している場合 (グローバル変数 = 1)、待機する必要があるので、別のEAがトレードされていることを意味します。「グリーン ライト」が表示されている場合 (グローバル変数 = 0)、すぐにトレードを開始できます。

したがって、2つの関数を作成します。:「レッドライト」、「グリーン ライト」このタスクは簡単です。しかし 早急に結論付けてはいけません。各関数によって実行されるアクションのシーケンスを定式化しましょう (TradeIsBusy()、TradeIsNotBusy())。

 

5. TradeIsBusy()

前に言われているようにこの関数の主なタスクは「グリーン ライト」が表示されるまで待ち、「レッドライト」にスイッチすることです。また、グローバル変数が存在するかどうかをチェックする必要があります。このチェックにより合理的 (でより効率的) となります。init()から関数を実行します。しかし、ユーザーがグローバル変数を削除し、どのEAもトレードすることができない可能性が存在します。よって、 作成された関数の本体に格納されます。

このすべての情報を表示して、グローバル変数を操作するときに発生したエラーの処理をする必要があります。「ハングアップ」を忘れてはなりません。同様に、関数の操作時間を制限する必要があります。

これが最終的なものになります。

/////////////////////////////////////////////////////////////////////////////////
//nt TradeIsBusy (int MaxWaiting_sec = 30)
//
//この関数は、TradeIsBusy の値を0から 1 に置き換えます。
//TradeIsBusy = 1の場合、 TradeIsBusy が 0 になるまで待機します 
//置き換え。
//グローバル変数 TradeIsBusy が存在しない場合は作成されます。
//リターン コード:
//1-正常に完了。1の値を持つグローバル変数 TradeIsBusy が割り当てられていた
//-1 - TradeIsBusy = 1 関数の起動の瞬間、ユーザーによって中断されました
//EAがグラフから削除され、 
//または変更など)。
//-2 - TradeIsBusy = 1 関数の起動の瞬間、待っている制限を超えました
//MaxWaiting_sec)
/////////////////////////////////////////////////////////////////////////////////
int TradeIsBusy( int MaxWaiting_sec = 30 )
  {
    //テストでトレード状況を分割 - 終了する理由はありません。 
    //関数
    if(IsTesting()) 
        return(1);
    int _GetLastError = 0, StartWaitingTime = GetTickCount();
    //+------------------------------------------------------------------+
    //グローバル変数が存在するかどうかを確認し、そうでない場合は、作成 |
    //+------------------------------------------------------------------+
    while(true)
      {
        //EAがユーザーによって終了した場合は、操作をストップします。
        if(IsStopped()) 
          { 
            Print(「EAはユーザーによって終了されました!」); 
            return(-1); 
          }
        //変数で指定された待機時間を超える場合 
        //MaxWaiting_sec、ストップ操作
        if(GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000)
          {
            Print("Waiting time (" + MaxWaiting_sec + " sec) exceeded!");
            return(-2);
          }
        //グローバル変数が存在するかどうかを確認します。
        //ある場合は、ループを出て、変更のブロックへ 
        //TradeIsBusy
        if(GlobalVariableCheck( "TradeIsBusy" )) 
            break;
        else
        //GlobalVariableCheck が FALSE を返した場合、存在しないことを意味します  
        //チェック中にエラーが発生しました
          {
            _GetLastError = GetLastError();
            //エラーの場合、0.1 秒間待機 
            //再開します。
            if(_GetLastError != 0)
             {
              Print("TradeIsBusy()-GlobalVariableCheck(\"TradeIsBusy\")-Error #",
                    _GetLastError );
              Sleep(100);
              continue;
             }
          }
        //グローバル変数がないことを意味するエラーがない場合、作成
        //
        //GlobalVariableSet > 0 の場合、グローバル変数が正常に作成されたことを意味します。 
        //関数を離れる
        if(GlobalVariableSet( "TradeIsBusy", 1.0 ) > 0 ) 
            return(1);
        else
        //GlobalVariableSet の値が返された場合はエラー 
        //変数の作成時に発生。
         {
          _GetLastError = GetLastError();
          //情報を表示、0.1 秒間待機、もう一度試してください。
          if(_GetLastError != 0)
            {
              Print("TradeIsBusy()-GlobalVariableSet(\"TradeIsBusy\",0.0 )-Error #",
                    _GetLastError );
              Sleep(100);
              continue;
            }
         }
      }
    //+----------------------------------------------------------------------------------+
    //この関数がこのポイントに達した場合、グローバル変数| 
    //が存在します。                                                                |
    //TradeIsBusy = 0になるまで待機、TradeIsBusy の値を1に変更 |
    //+----------------------------------------------------------------------------------+
    while(true)
     {
     //EAがユーザーによって終了した場合は、操作をストップします。
     if(IsStopped()) 
       { 
         Print(「EAはユーザーによって終了されました!」); 
         return(-1); 
       }
     //変数で指定された待機時間を超える場合 
     //MaxWaiting_sec、ストップ操作
     if(GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000)
       {
         Print("The waiting time (" + MaxWaiting_sec + " sec) exceeded!");
         return(-2);
       }
     //TradeIsBusy の値を 0 から 1 に変更してみてください。
     //成功した場合、 1 (「正常完了」) を返す
     if(GlobalVariableSetOnCondition( "TradeIsBusy", 1.0, 0.0 )) 
         return(1);
     else
     //そうでない場合、2つの理由が考えられます。つまり、 TradeIsBusy = 1 もしくは、 

     //エラーが発生しました ( がチェックされます)
      {
      _GetLastError = GetLastError();
      //エラーがまだある場合、情報を表示し、もう一度やり直してください。
      if(_GetLastError != 0)
      {
   Print("TradeIsBusy()-GlobalVariableSetOnCondition(\"TradeIsBusy\",1.0,0.0 )-Error #",
         _GetLastError );
       continue;
      }
     }
     //エラーがない場合、TradeIsBusy = 1 (別のEAのトレード)です 
     //情報と待機.
     Comment("Wait until another expert finishes trading...");
     Sleep(1000);
     Comment("");
    }
  }

まあ、すべてはここに明らかだと思われます。

  • グローバル変数が存在するかどうかチェックし、なければ作成
  • グローバル変数の値を 0 から 1 に変更しようとしました。その値は = 0 場合にのみトリガーされます。

最大時間は MaxWaiting_sec です。この関数には、グラフからエキスパートの削除することに反論はありません。

発生したすべてのエラーに関する情報がログに表示されます。

 

6. TradeIsNotBusy()

TradeIsNotBusy 関数は、逆の問題を解決します。つまり、「グリーン ライト」が切り替わります。

長時間稼働による制限はなく、ユーザーによって終了することはできません。シンプルです。「グリーン ライト」がオフの場合、EAはトレードできません。

すべてのリターン コードにありません。正常な場合、結果で確認できます。

外観です。

/////////////////////////////////////////////////////////////////////////////////
//void TradeIsNotBusy()
//
//この関数は、グローバル変数 TradeIsBusy の値を0に設定します。 
//TradeIsBusy が存在しない場合、作成されます。
/////////////////////////////////////////////////////////////////////////////////
void TradeIsNotBusy()
  {
    int _GetLastError;
    //テストでトレード状況を分割 - 終了しても意味がありません。 
    //関数
    if(IsTesting()) 
      { 
        return(0); 
      }
    while(true)
      {
        //EAがユーザーによって終了した場合は、動作をストップします。
        if(IsStopped()) 
          { 
            Print(「EAはユーザーによって終了されました!」); 
            return(-1); 
          }
        //グローバル変数の値を0に設定しようとすると、 (またはグローバルの作成 
        //変数)
        //GlobalVariableSet は、 > 0 を返します。 
        //これは、成功しています。関数を離れます。
        if(GlobalVariableSet( "TradeIsBusy", 0.0 ) > 0) 
            return(1);
        else
        //GlobalVariableSetが < = 0 の値を返した場合、エラーが発生したことを意味します。 
        //情報を表示、待機、およびもう一度やり直してください。
         {
         _GetLastError = GetLastError();
         if(_GetLastError != 0 )
           Print("TradeIsNotBusy()-GlobalVariableSet(\"TradeIsBusy\",0.0)-Error #", 
                 _GetLastError );
         }
        Sleep(100);
      }
  }

 

7. EAの統合

トレードフローへのアクセスを分散する3つの関数があります。EAの統合をシンプルにするには、TradeContext.mq4 ファイルを作成し、#include ディレクティブ (添付ファイル) を使用して有効にします。

TradeIsBusy() と TradeIsNotBusy() 関数を使用したEAのテンプレートです。

#include<TradeContext.mq4>
 
int start()
  {
    //エントリーするかどうかを確認してください。
    ...
    //StopLoss と有効期限のレベル、およびロットサイズを計算します。
    ...
    //トレード状況が空くまで待ち、(エラーが発生した場合に使用する 
    //離れる)
    if(TradeIsBusy() < 0) 
        return(-1); 
    //相場情報を更新します。
    RefreshRates();
    //レベルの StopLoss と有効期限を再計算します。
    ...
    //ポジションを開く
    if(OrderSend(...) < 0) 
      { 
        Alert("Error opening position # ", GetLastError()); 
      }

    //フリートレード コンテキストを設定します。
    TradeIsNotBusy();

    return(0);
  }
 

TradeIsBusy() と TradeIsNotBusy() 関数は、1 つだけ問題が発生します。トレード状況がビジー状態になったあとにEAをグラフから削除すると、変数 TradeIsBusy が 1 に等しくなります。他のEAはその後トレードすることができません。

この問題はシンプルに解決することができます。トレード時、EAをグラフから削除しないでください。;)

TradeIsBusyを0にしないことでも可能です。この場合、EAの init() 関数から TradeIsNotBusy() 関数を使用できます。

そして、もちろん、変数の値は、ターミナルでF3 ボタンでいつでも裁量で変更できます。


komposter (komposterius@mail.ru)、2006.04.11

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/1412

添付されたファイル |
TradeContext.mq4 (9.3 KB)
EAのサンプル EAのサンプル
一般的なMACDを使ったEAを例として、MQL4開発の原則を紹介します。
データサイエンスと機械学習(第19回):AdaBoostでAIモデルをパワーアップ データサイエンスと機械学習(第19回):AdaBoostでAIモデルをパワーアップ
AdaBoostは、AIモデルのパフォーマンスを向上させるために設計された強力なブースティングアルゴリズムです。AdaBoostはAdaptive Boostingの略で、弱い学習機をシームレスに統合し、その集合的な予測力を強化する洗練されたアンサンブル学習技法です。
1分でできるデータ品質評価 1分でできるデータ品質評価
1分でできるデータ品質評価
データサイエンスと機械学習(第20回):アルゴリズム取引の洞察、MQL5でのLDAとPCAの対決 データサイエンスと機械学習(第20回):アルゴリズム取引の洞察、MQL5でのLDAとPCAの対決
MQL5取引環境での適用を解剖しながら、これらの強力な次元削減テクニックに隠された秘密を解き明かしていきます。線形判別分析(LDA)と主成分分析(PCA)のニュアンスを深く理解し、戦略開発と市場分析への影響を深く理解します。