English Русский 中文 Español Deutsch Português
トレードの一時停止について

トレードの一時停止について

MetaTrader 4 | 24 2月 2016, 13:32
1 606 0
Andrey Khatimlianskii
Andrey Khatimlianskii

1. 義務か、それとも善意か?

メタトレーダー3クライアントターミナルでは、10秒以下のインターバルで2つのトレードをおこなうことは不可能でした。MT4を開発するにあたって、メタクウォーツ・ソフトウェア社は、この制限を除外したいというトレーダーの要望について検討しました。事実、多くのトレードを逐次おこなうことを容認できる状況はあります(複数ポジションのストップロスを移動させたり、指値注文を中止したりする状況です)。しかし、そうすることでトレーダーは間違ったやり方をおこないました。休みなく次から次へとポジションをオープンすることができる”殺人”エキスパートを作成するようになったのです。この結果、口座凍結や、少なくともブローカーの不親切な対応をトレーダーは被ることになりました。

本稿はこのようなエキスパートアドバイザー作成者向けではありません。トレードを、自分自身とブローカーにとってより快適におこなえるようにしたいと考えるトレーダー向けのものなのです。


2. 1つのエキスパート稼働と複数エキスパート稼働のちがいとは?

1つのターミナルのみを稼働し、1つのエキスパート・アドバイザーのみを作動させている場合、トレード中に一時停止をおこなうこと自体は簡単です。グローバル変数(グローバルレベルで宣言した変数です。ターミナルのグローバル変数と混同しないでください。)を生成し、そこに最後のオペレーション時間を格納するだけです。もちろん、各トレードオペレーションを作動させる前に、最後にトレードを試みてから十分な時間が経過したかどうかチェックしなければいけません。

この機能のコードは以下のようになります。

datetime LastTradeTime = 0;
int start()
 {
  // マーケットへエントリーすべきかどうかチェック
  ...
  // ストップロスとテイクプロフィットレベル、ロットサイズを計算
  ...
  // 最期のトレードから経過した時間が十分かチェック
  if(LocalTime() - LastTradeTime < 10)
   {
    Comment("最期のトレードから10秒経過していません!",
            " エキスパートアドバイザーはトレードできません!"); 
    return(-1);
   }
 
  // ポジションをオープン
  if(OrderSend(...) < 0)
   {
    Alert( "エラー オープンポジション # ", GetLastError() );
    return(-2);
   }
    
  // 最期のトレード時刻を記憶
  LastTradeTime = LocalTime();
 
  return(0);
 }

この例は1つのターミナル上で作動する1つのエキスパートアドバイザーに適しています。さらにもう1つかそれ以上のエキスパートアドバイザーを同時に作動させる場合、それらは10秒間一時停止することができません。ほかのエキスパートアドバイザーがトレードをおこなっていたことに気づくことができないのです。どんなエキスパートアドバイザーも固有のローカルLastTradeTime変数をもちます。この問題の解決方法は明らかです、グローバル変数を作成し、それをトレード時刻に格納するのです。ここでいうターミナルのグローバル変数は、全エキスパートアドバイザーがアクセスすることができます。


3. _PauseBeforeTrade()関数

一時停止を実現するコードは全てのエキスパートアドバイザーに共通なものなので、より合理的に関数として組み込むことができるでしょう。最大限の汎用性と最小限のエキスパートアドバイザーコードを提供します。

コードを書く前に、私たちのタスクを正確に定義させてください。タスクとは、私たちの時間と労力を減らすことだと思います。そうであるならば、関数がすべきことは次のとおりです。

  • グローバル変数が生成されているかどうかチェックし、なければ作成します。エキスパートアドバイザーのinit()関数でおこなうことがより論理的で作業効率が良いかもしれません。しかしその場合、ユーザーがその部分を削除し、その時稼働している全エキスパートアドバイザーが、トレード中の一時停止をおこなう機能を中止してしまう可能性があります。したがって、あらたに作成した関数内に入れることにします。
  • グローバル変数に現在時刻を記憶させます。ほかのエキスパートアドバイザーが一時停止機能を維持するためにおこないます。
  • 最後のトレードから経過した時間が十分かチェック。汎用性を考慮すれば、一時停止の必要期間を設定する外部変数を追加することも必要です。その値は各エキスパートアドバイザーごと個別に変更できます。
  • ディスプレイ・インフォメーション。プロセスと稼働中に発生した全エラーについて表示します。
  • パフォーマンス結果に応じてそれぞれ値を返します。

関数が最後のトレードから経過した時間が十分でないことを検出すると、必ず待機します。Sleep()関数は、IsStopped()の待機とチェックの両方に役立ちます。すなわち、”眠っている”あいだにチャートからエキスパートアドバイザーを削除した場合、それは維持されず強制的に停止されるのです。

トレーダーにはそのことが分かるように、あとどれだけ待機する時間があるのか、(”眠っている”あいだ毎秒)情報を表示します。

結果として、以下のコードが作成されなければいけません。

extern int PauseBeforeTrade = 10; // トレードの一時停止(秒)
 
/////////////////////////////////////////////////////////////////////////////////
// int _PauseBeforeTrade()
//
// 関数はグローバル変数LastTradeTimeに必要なローカル時刻を設定します。
// 作動開始時点のローカル時刻値がLastTradeTime + PauseBeforeTrade valueより小さい場合、 
// 関数は待機します。
// グローバル変数LastTradeTimeがどこにも見当たらない場合、関数が変数を生成します。
// 返り値:
//  1 - 正常に完了した場合
// -1 - エキスパートアドバイザーがユーザーによって中断された場合(チャートからエキスパートアドバイザーが削除された、 
//      ターミナルが閉じた、チャートシンボルや期間が変更された、等々。)
/////////////////////////////////////////////////////////////////////////////////
int _PauseBeforeTrade()
 {
  // テスト中に一時停止を維持する必要はありません。関数を終了させます。
  if(IsTesting()) 
    return(1); 
  int _GetLastError = 0;
  int _LastTradeTime, RealPauseBeforeTrade;
 
  //+------------------------------------------------------------------+
  //| グローバル変数が存在する¥るかどうかチェックし、なければ作成します。     |
  //+------------------------------------------------------------------+
  while(true)
   {
    // エキスパートアドバイザーがユーザーによって中断された場合、処理を停止
    if(IsStopped()) 
     { 
      Print("エキスパートアドバイザーが停止されました!"); 
      return(-1); 
     }
    // グローバル変数が存在するかどうかチェック
    // 存在すればこのループをイグジット
    if(GlobalVariableCheck("LastTradeTime")) 
      break;
    else
     // GlobalVariableCheckがFALSEを返す場合は、グローバル変数が存在しないか、 
     // チェク中にエラーが発生したことを意味します。
     {
      _GetLastError = GetLastError();
      // エラーが続く場合、情報を表示し、0.1秒待機した後に 
      // 再度チェックします。
      if(_GetLastError != 0)
       {
        Print("_PauseBeforeTrade()-GlobalVariableCheck(\"LastTradeTime\")-エラー #",
              _GetLastError );
        Sleep(100);
        continue;
       }
     }
    // エラーが発生しない場合は、グローバル変数が存在しないことを意味するので、生成します。
    // GlobalVariableSet > 0 の場合、グローバル変数の生成に成功したことを示します。 
    // 関数をイグジット
    if(GlobalVariableSet("LastTradeTime", LocalTime() ) > 0) 
      return(1);
    else
     // GlobalVariableSetの戻り値が <= 0 である場合、変数の生成中にエラーが発生した 
     // ことを意味します。
     {
      _GetLastError = GetLastError();
      // 情報を表示し、0.1秒待機した後に再度試行します。 
      if(_GetLastError != 0)
       {
        Print("_PauseBeforeTrade()-GlobalVariableSet(\"LastTradeTime\", ", 
              LocalTime(), ") - エラー #", _GetLastError );
        Sleep(100);
        continue;
       }
     }
   }
  //+--------------------------------------------------------------------------------+
  //| 関数の処理がここまで達したならば、グローバル変数が存在することを  |
  //| 意味します。                                                                   |
  //| Wait until LocalTime() becomes > LastTradeTime + PauseBeforeTrade               |
  //+--------------------------------------------------------------------------------+
  while(true)
   {
    // エキスパートアドバイザーがユーザーによって中断された場合、処理を停止
    if(IsStopped()) 
     { 
      Print("エキスパートアドバイザーが停止されました!"); 
      return(-1); 
     }
    // グローバル変数の値を取得
    _LastTradeTime = GlobalVariableGet("LastTradeTime");
    // ここでエラーが発生した場合、情報を表示し、0.1秒待機した後に 
    // 再試行
    _GetLastError = GetLastError();
    if(_GetLastError != 0)
     {
      Print("_PauseBeforeTrade()-GlobalVariableGet(\"LastTradeTime\")-エラー #", 
            _GetLastError );
      continue;
     }
    // 最期のトレードから経過した秒数をカウント
    RealPauseBeforeTrade = LocalTime() - _LastTradeTime;
    // PauseBeforeTrade秒経過していない場合、
    if(RealPauseBeforeTrade < PauseBeforeTrade)
     {
      // 情報を表示し、0.1秒待機した後に再度試行します。
      Comment("トレード一時停止。残り時間: ", 
               PauseBeforeTrade - RealPauseBeforeTrade, " 秒" );
      Sleep(1000);
      continue;
     }
    // PauseBeforeTrade秒経過した場合、ループ処理を中止
    else
      break;
   }
  //+--------------------------------------------------------------------------------+
  //| 関数の処理がここまで達したならば、グローバル変数が存在することを  |
  //|  意味し、ローカル時刻がLastTradeTime + PauseBeforeTrade を超えたということを意味します。   |
  //| ローバル変数LastTradeTimeに必要なローカル時刻を設定   |
  //+--------------------------------------------------------------------------------+
  while(true)
   {
    // エキスパートアドバイザーがユーザーによって中断された場合、処理を停止
    if(IsStopped()) 
     { 
      Print("エキスパートアドバイザーが停止されました!"); 
      return(-1);
     }

    // ローバル変数LastTradeTimeに必要なローカル時刻を設定
    // 成功した場合 - イグジット
    if(GlobalVariableSet( "LastTradeTime", LocalTime() ) > 0) 
     { 
      Comment(""); 
      return(1); 
     }
    else
    // GlobalVariableSetの戻り値が <= 0 である場合、エラーが発生したことを意味します。
     {
      _GetLastError = GetLastError();
      // 情報を表示し、0.1秒待機した後に再度試行します。
      if(_GetLastError != 0)
       {
        Print("_PauseBeforeTrade()-GlobalVariableSet(\"LastTradeTime\", ", 
              LocalTime(), " ) - エラー #", _GetLastError );
        Sleep(100);
        continue;
       }
     }
   }
 }


4. エキスパートアドバイザーへの組み込みと使用法

関数の操作性を確認するために、トレード中の一時停止を維持したままトレードをおこなう、診断エキスパートアドバイザーを作成しました。_PauseBeforeTrade()関数を、#includeディレクティブを使用してエキスパートアドバイザーに含まれているPauseBeforeTrade.mq4ファイルにあらかじめ組み込んでおきました。

注意!このエキスパートアドバイザーは関数の操作性をチェックするためだけのものです。トレードに使用することはできません!

#include <PauseBeforeTrade.mq4>
 
int ticket = 0;
int start()
 {
  // このエキスパートアドバイザーでオープンしたポジションがない場合
  if(ticket <= 0)
   {
    // トレード中一時停止を維持します。エラーが発生した場合はイグジット
    if(_PauseBeforeTrade() < 0) 
      return(-1);
    // マーケットインフォメーションを更新
    RefreshRates();
 
    // ポジションオープンをトライ
    ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 5, 0.0, 0.0, "一時停止テスト", 123, 0, 
                       Lime);
    if(ticket < 0)
      Alert("エラー OrderSend № ", GetLastError());
   }
  // このエキスパートアドバイザーでオープンしたポジションが存在する場合
  else
   {
    // トレード中一時停止を維持します。(エラーが発生した場合はイグジット
    if(_PauseBeforeTrade() < 0)
      return(-1);
    // マーケットインフォメーションを更新
    RefreshRates();


 
    // ポジションクローズをトライ
    if (!OrderClose( ticket, 0.1, Bid, 5, Lime ))
      Alert("エラー OrderClose № ", GetLastError());
    else
      ticket = 0;
   }
  return(0);
 }

こうして、あるエキスパートアドバイザーはEURUSD-M1チャートで、もう1つの、全く別のものは、GBPUSD-M1チャートで稼働しました。結果を得るのに長い時間はかかりませんでした。両エキスパートアドバイザーともにトレード中に所定の10秒間一時停止を維持してトレードを開始しました。





5. 起こり得る問題について

複数のエキスパートアドバイザーを1つのグローバル変数で稼働させるとエラーが起こり得ます。これを回避するために、変数へのアクセスを区切らなければいけません。このような”区切り”の作業アルゴリズムについては、記事"エラー 146 ("Trade context busy")とその対処法"に詳しく書いてあります。それがここで使用するアルゴリズムです。

エキスパートアドバイザーの最終形は以下のようになります。

#include <PauseBeforeTrade.mq4>
#include <TradeContext.mq4>
 
int ticket = 0;
int start()
 {
  // このエキスパートアドバイザーでオープンしたポジションがない場合
  if(ticket <= 0)
   {
    // トレードがビジーでなくなるまで待機し、占有する(エラーが発生した場合、 
    // イグジット)
    if(TradeIsBusy() < 0)
      return(-1);
    // トレード中の一時停止を維持
    if(_PauseBeforeTrade() < 0)
     {
      // エラーが発生した場合、トレードを中止しイグジット
      TradeIsNotBusy();
      return(-1);
     }
    // マーケットインフォメーションを更新
    RefreshRates();
 
    // ポジションオープンをトライ
    ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 5, 0.0, 0.0, "一時停止テスト", 123, 0, 
                       Lime);
    if (ticket < 0)
      Alert("エラー OrderSend № ", GetLastError());
    // トレードを中止
    TradeIsNotBusy();
   }
  // このエキスパートアドバイザーでオープンしたポジションが存在する場合
  else
   {
    // トレードがビジーでなくなるまで待機し、占有する(エラーが発生した場合、 
    // イグジット)
    if(TradeIsBusy() < 0)
      return(-1);
    // トレード中の一時停止を維持
    if(_PauseBeforeTrade() < 0)
     {
      // エラーが発生した場合、トレードを中止しイグジット
      TradeIsNotBusy();
      return(-1);
     }
    // マーケットインフォメーションを更新
    RefreshRates();
 
    // ポジションクローズをトライ
    if(!OrderClose( ticket, 0.1, Bid, 5, Lime))
      Alert("エラー OrderClose № ", GetLastError());
    else
      ticket = 0;
 
    // トレードを中止
    TradeIsNotBusy();
   }
  return(0);
 }

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

添付されたファイル |
TradeContext.mq4 (9.27 KB)
マジックナンバー:オーダー"マジック"識別子 マジックナンバー:オーダー"マジック"識別子
本稿は複数のエキスパートアドバイザーを1つのMT4ターミナルで稼働する際に、相互干渉する問題に取り組んでいます。エキスパートアドバイザーが、”別の”ポジション(手動か他のエキスパートアドバイザーがオープンしたポジション)を修正したりクローズしたりしないように、”固有の”オーダーのみを管理するよう”教える”ようにします。本稿はMQL4のターミナルとプログラミングに関する基本スキルをお持ちのユーザー様向けに書かれています。
グラフィカルインタフェース I: コントロールのフォーム(チャプター 2) グラフィカルインタフェース I: コントロールのフォーム(チャプター 2)
本稿ではグラフィカルインタフェースの第一及び主要素である、コントロールのフォームを作成します。このフォームには複数のコントロールが任意の場所と組み合わせで添付することができます。
グラフィックエキスパートアドバイザー: オートグラフ グラフィックエキスパートアドバイザー: オートグラフ
本稿はトレードを管理する便利なインターフェースである画面表示の利用法について書いています。
グラフィカルインタフェース I: ライブラリストラクチャの準備(チャプター 1) グラフィカルインタフェース I: ライブラリストラクチャの準備(チャプター 1)
本稿は、グラフィカルインタフェースの開発に関するあと一つのシリーズの始まりです。現在、MQLアプリケーション内で高品質なグラフィカルインタフェースを迅速かつ簡単に作成できるよコードライブラリは1つもありません。ここで、グラフィカルインタフェースとは私たちになじみ深いオペレーティングシステムで使用されるグラフィカルインタフェースを意味します。