English Русский 中文 Español Deutsch Português
Sleep か否か?

Sleep か否か?

MetaTrader 4 | 22 4月 2016, 11:08
6 545 0
Sergey Gridnev
Sergey Gridnev

はじめに

動作中、Expert Advisor は処理の間にポーズを設ける状態を産み出すことがあります。これはトレードサーバーに対して繰り返し出されるリクエストの間で一定の間隔を維持する要件を満たすための必要性から(実行エラー)の場合)、また特定のイベントを待って(たとえば、サーバーへの再接続、無料のトレードコンテキスト、など)生じます。



Sleep() か否か?

ポーズの実装には、MQL4 には Sleep() と呼ばれる関数があり、それはパラメータとしてミリ秒の単位であらわされる時間間隔の値を取ります。Sleep() 関数はプログラムコードの実行を停止し、所定の時間間隔が経過したあとに続行を許可します。

私見ですが、この関数の使用には2つの欠点があります。第1に、マシンタイムが非現実的に使用されることです。あるタイプのアクションにおけるポーズ中、プログラムは前のアクションによって別のタイプのアクションを行うことが可能です(たとえば、トレード中のポーズの間、プログラムは計算をし、ティックの到着をモニターする、など)。第2に、こちらの方が重要ですが、関数Sleep() はカスタムインディケータから呼び出すことができません(ドキュメンテーションを参照ください)。ただし、プログラミングにはプログラム言語を使用する必要があります。

MQL4 プログラムに10秒のポーズを実装する方法を考察します。以下が関数 Sleep() を使った例です。

if ( /* condition that requires to hold a pause */ )
   Sleep(10000); // sleep for 10 seconds

// program block to be executed upon the expiry of the pause
// ...

代替として、追加の変数を用いたプログラムコードを考えます。アクションを行う前にポーズが必要な場合、そのポーズはその終わりのローカル時間値で初期化されます。ローカル時間値をこの変数値と比較することで、ポーズの終了を確率することができます。この代替法の利点を享受するため、ポーズの終了を待つ周期にポーズを設定する必要のないプログラムコードの周期的実行を作成します。サイクルの使用(たとえばwhile)および、ティックの到来周期に応じたサイクルに呼び出される関数 start() の対応する実行の両方でこれを行うことができます。前者の代替はカスタムスクリプトに適しており、後者は EA やインディケータに適しています。以下がその両方です。

代替1(サイクル 'while' の使用)

int _time_waiting=0;
// ...
if ( ... ) // condition that requires to hold a pause
   _time_waiting = TimeLocal() + 10; // the pause ends in 10 seconds after the current local time
while ( TimeLocal() < _time_waiting )
{ // program block to be executed cyclically while waiting for the end of the pause
   // ...
}
// program block to be executed after the end of the pause
// ...

代替2(関数 start() 呼び出しのサイクル使用)

static int _time_waiting=0;
// ...
if ( ... ) // condition that requires to hold a pause
   _time_waiting = TimeLocal() + 10; // the pause ends in 10 seconds after the current local time
if ( TimeLocal() >= _time_waiting )
{ // program block to be executed after the end of the pause
   // ...
}
// program block to be executed at every tick, not related to waiting for the end of the pause
// ...

上記代替間の主な違いは、代替1はポーズの期間停止されたプログラムブロックの実行を保証し、一方代替2はそれを保証しないことです。これはティックフローが再開せずなんらかの理由で中断されることに関連しています。また、代替2では、変数 _time_waiting iが 静的に記述されます。それは start() 関数呼び出しの間、その値を保持しますが、代替1ではそれは不要です。

代替でのコードの冗長性は、 Sleep() 関数呼び出しでポーズの実装を行うことと比較して、ダウンタイムを排除することへの報いです。ただし、プログラムコードで変数 _time_waiting を繰り返し使用することでダウンタイムが問題を起こすことはありません。というのも、先行するポーズが時間切れになるまで新規の値では初期化することができないためです。この問題は少なくとも2とおりの方法で解決できます。その一つを選ぶか、別の方法を選ぶかはプログラマーのスタイルと好みによります。

1) 対応するポーズの期限を格納する条件グループに対して独自の変数を使う。

2) 配列として _time_waiting を記述する。

私はタイマー関数を実装するちいさなライブラリを作成するのが妥当であると思います。このタイマーは十分な量のカウンター変数と、初期化、制御抽出物の追加、削除の関数を持つ必要があります。また、カウントダウン(待ちタイマー)とアップカウンター(ストップウォッチ)を実装することも可能です。そのようなライブラリを実装する方法例は本稿に添付があります(ファイル ExLib_Timer.mq4 とソーステキストを持ちます)。以下が実装される関数のプロトタイプを持つヘッダファイルです。

#import "ExLib_Timer.ex4"
//
// Note.
// The counter identifier <CounterID> can take any values except "0",
// since "0" is reserved for the designation of nonactivated counters
// in a general array.
//
void timer_Init();
// start initialization
//
int timer_NumberTotal();
// request for the total amount of timer counters
// Return:
//    total amount of counters
//
int timer_NumberUsed();
// request for the amount of activated counters
// Return:
//    amount of activated counters
//
int timer_NumberFree();
// request for the amount of nonactivated (free) counters
// Return:
//    amount of free counters
//
void timer_ResetAll();
// resetting (zeroing) all counters
//
bool timer_Reset(int CounterID);
// resetting (zeroing) a counter by ID
// Parameters:
//    <CounterID> - counter identifier
// Return:
//    true  - resetted
//    false - no counter with such ID is found
//
void timer_DeleteAll();
// deletion of all counters
//
bool timer_Delete(int CounterID);
// deleting a counter by ID
// Parameters:
//    <CounterID> - counter identifier
// Return:
//    true  - deleted
//    false - no counter with such ID is found
//

bool timer_StartWaitable(int CounterID, int timeslice);
// starting a counter of the "wait timer" type
// Parameters:
//    <CounterID> - counter identifier (if it is not available, a counter is added)
//    <timeslice> - wait period (seconds)
// Return:
//    true  - started
//    false - no free counters
//
bool timer_StartStopwatch(int CounterID);
// starting a counter of the "stopwatch" type
// Parameters:
//    <CounterID> - counter identifier (if it is not available, a counter is added)
// Return:
//    true  - started
//    false - no free counters
//
int timer_GetCounter(int CounterID);
// request for the indications of the counter with ID <CounterID>
// Return:
//    for a counter of the "stopwatch" type, it is the amount of seconds elapsed since the start
//    for a counter of the "wait timer" type, it is the amount of seconds left till the end of wait period
// Note:
//    in case of no counter with ID <CounterID>, -1 is returned
//
int timer_GetType(int CounterID);
// request for a counter with ID <CounterID>
// Parameters:
//    <CounterID> - counter identifier
// Return:
//     1 - counter of the "stopwatch" type
//    -1 - counter of the "wait timer" type
//     0 - no counter with such ID is found
//
int timer_GetCounterIDbyPOS(int CounterPOS);
// request for a counter ID by its position among the activated ones
// Parameters:
//    <CounterPOS> - the number of the counter position among the activated ones
// Return:
//    counter identifier or "0" if a nonexisting position is specified
// Note:
//    <CounterPOS> can take the values ranging from 0 up to the amount of the activated counters,
//    returned by function timer_NumberUsed(). Otherwise, it returns 0
//
#import

Sleep() 関数呼び出しを使用することと、ログにエントリを作成して代替のポーズを実装することの違いは以下にテキストが提供されている短いExpert Advisor によって明らかに示されています。代替のポーズ実装は入力パラメータ Use_Sleep によって設定されます。-Sleep() を使用するなら『真』、Sleep() の使用を拒否するなら『偽』です。

#include "include/ExLib_Timer.mqh"
// --
//---- input parameters
extern bool Use_Sleep = false;
// =true  - using function "Sleep()"
// =false - using a timer
//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
{
   timer_Init();
   if ( Use_Sleep )
      Print("init(). * Using function Sleep() *");
   else
      Print("init(). * Using a timer *");
   timer_StartWaitable(101, 10);
   timer_StartStopwatch(102);
   return(0);
}
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
{
   timer_DeleteAll();
   Comment("");
   return(0);
}
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
{
   Comment("Waitable:",timer_GetCounter(101)," / Stopwatch:",timer_GetCounter(102));
   Print("start() - initial block -");
// --
   if ( Use_Sleep )
   {
      //  - using function "Sleep()" -
      Sleep( 10000 ); // sleep for 10 seconds
      // program block to be executed after the pause ends
      Print("start() - block to be executed after the pause -");
   }
   else
   {
      //  - using a timer -
      if ( timer_GetType(101) == 0 )
         timer_StartWaitable(101, 10); // sleep for 10 seconds
         
      if ( timer_GetCounter(101) == 0 )
      {
         // program block to be executed after the pause ends
         timer_Delete(101);
         Print("start() - block to be executed after the pause -");
      }
   }
// --
   Print("start() - end block -");
   return(0);
}

上記の EA 以外に、本稿にはもう2つデモ EA が添付されています。
Ex_Timer_OrderLimits_TrailByTime.mq4 は時間によってトレーリングSL および TP を実装する方法を示しています。
そして、Ex_Timer_OrderSend_wMinTimeLimit.mq4 は現行チャートに設定されている周期内で1度以上はオーダーを出さない方法を示しています。



マイナーなデメリット、『スプーン一杯のタール』?

残念ながら、提供されているライブラリは完全に Sleep() と置換可能ではありません。読者のみなさんの注意を引くべきマイナス面が2つあります。

まず、Sleep() を使用することでプログラム実行がポーズ後即継続されることです。タイマー使用の場合は、ポーズ後最初のティックが到来してから実行が継続されます。これは遅れたブロックがかなり遅く実行される、または前述のようにまったく実行されないことにつながります(たとえば、リンクチャンネル内の重大なエラー)。

次に、関数 Sleep() により 0.1 秒の間隔でポーズが設定できることです。タイマーの場合は1秒間隔のみです。



おわりに

よくあことですが、上記の Sleep() を使用することによる代替ポーズ実装のメリットは、絶対確実なものではありません。いくつか細かい点があります。何事にも強みと弱みがあるものです。その重要性は追求する目的によって決まります。Sleep() を使うか否か、 何を犠牲にし何を採るか、すべては一般的には決められ得ませんが、プロジェクトの枠組みの中で実現するものです。

添付資料

関数ライブラリ

  • ExLib_Timer.mqh -ヘッダファイル
  • ExLib_Timer.mq4 -ソーステキスト

用例

  • Ex_Timer_SleepOrNot.mq4 -ポーズ実装の代替法間の違いを明確に示す Expert Advisor
  • Ex_Timer_OrderLimits_TrailByTime.mq4-時間によるトレーリング SL および TP を説明する EA
  • Ex_Timer_OrderSend_wMinTimeLimit.mq4 -現行チャートに設定されている周期内で1度以上はオーダーを出さないことを説明するEA

重要事項!"Ex_Timer_OrderLimits_TrailByTime" および "Ex_Timer_OrderSend_wMinTimeLimit" の EA はデモアカウントでのみ使用するためのものです。


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

MetaTrader 4 クライアントターミナルのプログラムフォルダ MetaTrader 4 クライアントターミナルのプログラムフォルダ
本稿では、MetaTrader 4 クライアントターミナルのプログラムフォルダの内容を説明します。本稿は、クライアントターミナル操作の詳細をすでに理解し始めた方に有用です。
怠惰は進歩への誘因である半自動マーキングテンプレート 怠惰は進歩への誘因である半自動マーキングテンプレート
チャート作業例があまたある中で、マニュアルでテンプレートをマーキングする方法があります。トレンドライン、チャンネル、サポート/レジスタンスレベルなど、はチャートに示されます。確かにこの類の作業をする特殊なプログラムがいくつかあります。どの方法を使うかは各自が決めることです。本稿では、検討材料として、マニュアルのマーキング方法に続いて繰り返されるルーチンアクションのエレメントをいくつか自動化する方法を提供します。
MQL4 でダブルスの作業をする MQL4 でダブルスの作業をする
本稿では、一般的なプログラムエラーについて考察します。それは MQL4 プログラムでdouble の数字を処理するときに発生するものです。
自動制御システム(ACS)のコンテキストでのテクニカル分析の考え方、また『逆説』 自動制御システム(ACS)のコンテキストでのテクニカル分析の考え方、また『逆説』
本稿は、テクニカル分析の代替の考え方を実証します。それは最新の自動制御理論やテクニカル分析自体の原理に基づくものです。本稿は、実用的なアプリケーションをいくつか使って理論を表現する導入記事です。