自己キャッシング指標の速度比較

Vladimir Karputov | 21 5月, 2018


はじめに

突然、MQL5指標に古典的にアクセスするのに飽きてしまったと仮定し、アクセス速度を代替オプションと比較してみます。たとえば、キャッシュ有り/無しのMQL4形式の指標アクセスと比較することができます。MQL4形式のアクセスに関するアイデアは、トレーダーのライフハック: インジケーターで作られたファストフードから導き出され、途中で改善されました。


指標ハンドルのMQL5番号付けの分析

ターミナルでは、ゼロから始まる指標ハンドルの連続番号付けが特徴とされていると仮定します。この仮定を確認するには、簡単なエキスパートアドバイザー iMACD and IndicatorRelease.mq5を作成します。これは、いくつかの指標ハンドルを作成し、すぐに印刷してOnTick()で定期的にアクセスします。

//+------------------------------------------------------------------+
//|                                   iMACD and IndicatorRelease.mq5 |
//|                              Copyright © 2018, Vladimir Karputov |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2018, Vladimir Karputov"
#property link      "http://wmua.ru/slesar/"
#property version   "1.003"
//--- 入力パラメータ
input int   count=6;   // MACD指標を数える

int    handles_array[]; // iMACD指標のハンドルを格納するための配列
//+------------------------------------------------------------------+
//| エキスパート初期化関数                                              |
//+------------------------------------------------------------------+
int OnInit()
  {
   int array_resize=ArrayResize(handles_array,count);
   if(array_resize==-1)
     {
      Print("ArrayResize error# ",GetLastError());
      return(INIT_FAILED);
     }
   if(array_resize!=count)
     {
      Print("ArrayResize != \"Count MACD indicators\"");
      return(INIT_FAILED);
     }
   ArrayInitialize(handles_array,0);
   for(int i=0;i<count;i++)
     {
      handles_array[i]=CreateHandleMACD(12+i);
      //--- ハンドルが作成されない場合 
      if(handles_array[i]==INVALID_HANDLE)
        {
         //--- 失敗について報告してエラーコードを出力する 
         PrintFormat("Failed to create handle of the iMACD indicator for the symbol %s/%s, error code %d",
                     Symbol(),
                     EnumToString(Period()),
                     GetLastError());
         //--- 指標が早期に停止された 
         return(INIT_FAILED);
        }
      Print("ChartID: ",ChartID(),": ",Symbol(),",",StringSubstr(EnumToString(Period()),7),
            ", create handle iMACD (",handles_array[i],")");
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| エキスパート初期化解除関数                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   Comment("");
   for(int i=0;i<count;i++)
     {
      Print("ChartID: ",ChartID(),": ",Symbol(),",",StringSubstr(EnumToString(Period()),7),
            ", remove handle iMACD (",handles_array[i],"): ",IndicatorRelease(handles_array[i]));
     }
  }
//+------------------------------------------------------------------+
//| エキスパートティック関数                                            |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   string text="";
   for(int i=0;i<count;i++)
     {
      double macd_main_1=iMACDGet(handles_array[i],MAIN_LINE,1);
      if(i<15)
        {
         text+="\n"+"ChartID: "+IntegerToString(ChartID())+": "+Symbol()+
               ", MACD#"+IntegerToString(i)+" "+DoubleToString(macd_main_1,Digits()+1);
         Comment(text);
        }
      else if(i==15)
        {
         text+="\n"+"only the first 15 indicators are displayed ...";
         Comment(text);
        }
     }
  }
//+------------------------------------------------------------------+
//| iMACDバッファの値を取得する                                         |
//|  バッファ番号は                                                    |
//|   0 - MAIN_LINE, 1 - SIGNAL_LINE                                 |
//+------------------------------------------------------------------+
double iMACDGet(const int handle_iMACD,const int buffer,const int index)
  {
   double MACD[1];
//--- エラーコードをリセットする 
   ResetLastError();
//--- iMACDBuffer配列の一部に、0インデックスを持つ指標バッファの値を入力する 
   if(CopyBuffer(handle_iMACD,buffer,index,1,MACD)<0)
     {
      //--- コピーが失敗したら、エラーコードを報告する 
      PrintFormat("Failed to copy data from the iMACD indicator, error code %d",GetLastError());
      //--- 結果がゼロで終了し、指標が計算されていないとみなされることを意味する 
      return(0.0);
     }
   return(MACD[0]);
  }
//+------------------------------------------------------------------+
//| MACDハンドルを作成する                                             |
//+------------------------------------------------------------------+
int CreateHandleMACD(const int fast_ema_period)
  {
//--- iMACD指標ハンドルを作成する
   return(iMACD(Symbol(),Period(),fast_ema_period,52,9,PRICE_CLOSE));
  }
//+------------------------------------------------------------------+

実験1

ソースデータ: ターミナルには、指標なし、EAなしのAUDJPY M15、USDJPY M15、EURUSD M15チャートがあります。iMACD and IndicatorRelease.mq5 Count MACD indicatorsパラメータは6です。

ターミナルを再起動した直後にiMACD and IndicatorRelease.mq5をAUDJPY M15 (ChartID 131571247244850509) に取り付けます。

2018.02.16 09:36:30.240 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (10)
2018.02.16 09:36:30.240 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (11)
2018.02.16 09:36:30.240 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (12)
2018.02.16 09:36:30.240 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (13)
2018.02.16 09:36:30.240 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (14)
2018.02.16 09:36:30.240 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (15)

ハンドルの番号付けが0ではなくて10で始まることが分かります。

実験2

ソースデータ: iMACD and IndicatorRelease.mq5がAUDJPY M15で起動され、Count MACD indicatorsは6です。

iMACD and IndicatorRelease.mq5をUSDJPY、M15 (ChartID 131571247244850510)に取り付けます。

2018.02.16 09:37:32.118 iMACD and IndicatorRelease (USDJPY,M15) ChartID: 131571247244850510: USDJPY,M15, create handle iMACD (10)
2018.02.16 09:37:32.118 iMACD and IndicatorRelease (USDJPY,M15) ChartID: 131571247244850510: USDJPY,M15, create handle iMACD (11)
2018.02.16 09:37:32.118 iMACD and IndicatorRelease (USDJPY,M15) ChartID: 131571247244850510: USDJPY,M15, create handle iMACD (12)
2018.02.16 09:37:32.118 iMACD and IndicatorRelease (USDJPY,M15) ChartID: 131571247244850510: USDJPY,M15, create handle iMACD (13)
2018.02.16 09:37:32.118 iMACD and IndicatorRelease (USDJPY,M15) ChartID: 131571247244850510: USDJPY,M15, create handle iMACD (14)
2018.02.16 09:37:32.118 iMACD and IndicatorRelease (USDJPY,M15) ChartID: 131571247244850510: USDJPY,M15, create handle iMACD (15)

チャート (USDJPY M15)の番号付けもまた0ではなく10から始まることが分かります。

結論: ターミナル(ユーザに提供されるもの)の指標ハンドルの番号付けは連続しておらず、ゼロで開始しません。

実験3

AUDJPY, M15(ChartID 131571247244850509)とAUDJPY、M15(ChartID 131571247244850510)の同一のチャートです。それぞれがiMACD and IndicatorRelease.mq5と6に等しいCount MACD indicators を持ちます。

作成された指標ハンドルの連続していない番号付けによって、MQL5が内部的計算(各ユニークハンドルのカウンタ)を維持していることが確認されます。これを確認するには、期間拡張をコメントアウトしてみましょう。

int OnInit()
  {
***
   ArrayInitialize(handles_array,0);
   for(int i=0;i<count;i++)
     {
      handles_array[i]=CreateHandleMACD(12/*+i*/);
      //--- ハンドルが作成されない場合 

このように、まったく同じ設定で複数のMACD指標ハンドルの作成を試みます。

実験1と2の後に残ったチャートを削除し、iMACD and IndicatorRelease.mq5をAUDJPY、M15(ChartID 131571247244850509)を起動します。

2018.02.18 07:53:13.600 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:13.600 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:13.600 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:13.600 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:13.600 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:13.600 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, create handle iMACD (10)

お分かりのように、同じ指標を作成すると同じハンドルが返されます

iMACD and IndicatorRelease.mq5 EA(期間拡張はコメントアウト)をAUDJPY、M15(ChartID 131571247244850510)に取り付けます。

2018.02.18 07:53:20.218 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:20.218 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:20.218 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:20.218 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:20.218 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, create handle iMACD (10)
2018.02.18 07:53:20.218 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, create handle iMACD (10)

再び、同じハンドルが返されます。1番目のチャートと2番目のチャートの "10"ハンドルは同じなのか異なるのかを確認するためにチャートからEAを削除します(覚えていらっしゃるように、EAはOnDeinit()でハンドルの配列を渡し、IndicatorReleaseを使用してそれぞれを削除します)。

2018.02.18 07:53:26.716 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, remove handle iMACD (10): true
2018.02.18 07:53:26.716 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, remove handle iMACD (10): false
2018.02.18 07:53:26.716 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, remove handle iMACD (10): false
2018.02.18 07:53:26.716 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, remove handle iMACD (10): false
2018.02.18 07:53:26.716 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, remove handle iMACD (10): false
2018.02.18 07:53:26.716 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850509: AUDJPY,M15, remove handle iMACD (10): false

2018.02.18 07:53:36.116 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, remove handle iMACD (10): true
2018.02.18 07:53:36.117 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, remove handle iMACD (10): false
2018.02.18 07:53:36.117 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, remove handle iMACD (10): false
2018.02.18 07:53:36.117 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, remove handle iMACD (10): false
2018.02.18 07:53:36.117 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, remove handle iMACD (10): false
2018.02.18 07:53:36.117 iMACD and IndicatorRelease (AUDJPY,M15) ChartID: 131571247244850510: AUDJPY,M15, remove handle iMACD (10): false

ドキュメントのプログラム実行セクションを考慮すれば、結果は期待通りです。

EAは独自のスレッドで実行され、EAと同じ数の実行スレッドがあります。

つまり、同じチャート(同じ銘柄と時間枠)上で2つのEAが同じ入力を持つ指標を作成した場合、MQL5はそれらを内部計算で2つの異なるハンドルとして識別します。

EAにおける指標の開発に関する一般的な結論

ターミナルの指標ハンドル(ユーザに提供されるもの)の番号付けは連続しておらず、ゼロで始まりませんが、MQL5は内部ハンドルアカウンティングで以下を考慮します。

  • テクニカル指標関数(iMA、iAC、iMACD、iIchimokuなど)
  • 指標の入力
  • 指標が作成される銘柄
  • 指標が作成される時間枠
  • EAが作動するチャートのChartID

ハンドルキャッシュにには意味があるのでしょうか

初期データ(時間枠、銘柄、テスト時間間隔、ティック生成タイプ)は次の通りです。

キャッシュテスト設定

図1 設定

Cache test.mq5EAの助けを借りてMQL4形式の指標にアクセスするテスト(ハンドルキャッシュ有り/無し)はMQL5 test.mq5を使用して実行されます。

//+------------------------------------------------------------------+
//|                                                    MQL5 test.mq5 |
//|                              Copyright © 2018, Vladimir Karputov |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2018, Vladimir Karputov"
#property link      "http://wmua.ru/slesar/"
#property version   "1.000"
//--- 入力パラメータ
input bool     UseOneIndicator=false;  // Use indicator: "false" -> 9 indicators, "true" - 1 indicator
//---
int            arr_handle_iMACD[];     // iMACD指標のハンドルを格納するための配列
//+------------------------------------------------------------------+
//| エキスパート初期化関数                                              |
//+------------------------------------------------------------------+
int OnInit()
  {
   if(UseOneIndicator)
      ArrayResize(arr_handle_iMACD,1);
   else
      ArrayResize(arr_handle_iMACD,9);
   if(!CreateHandle(arr_handle_iMACD))
      return(INIT_FAILED);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| エキスパート初期化解除関数                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| エキスパートティック関数                                            |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   int arr_size=ArraySize(arr_handle_iMACD);
   for(int i=0;i<arr_size;i++)
     {
      double macd_main_30=iMACDGet(arr_handle_iMACD[i],MAIN_LINE,0);
     }
  }
//+------------------------------------------------------------------+
//| CreateHandle                                                     |
//+------------------------------------------------------------------+
bool CreateHandle(int &arr_handles[])
  {
   int arr_size=ArraySize(arr_handles);
   for(int i=0;i<arr_size;i++)
     {
      int fast_ema_repiod=30+10*i;
      //--- iMACD指標ハンドルを作成する
      arr_handles[i]=iMACD(NULL,0,fast_ema_repiod,26,9,PRICE_CLOSE);
      //--- ハンドルが作成されない場合 
      if(arr_handles[i]==INVALID_HANDLE)
        {
         //--- 失敗について報告してエラーコードを出力する 
         PrintFormat("Failed to create handle of the iMACD indicator for the symbol %s/%s, error code %d",
                     Symbol(),
                     EnumToString(Period()),
                     GetLastError());
         //--- 指標が早期に停止された 
         return(false);
        }
     }
   return(true);
  }
//+------------------------------------------------------------------+
//| iMACDバッファの値を取得する                                         |
//|  バッファ番号は                                                    |
//|   0 - MAIN_LINE, 1 - SIGNAL_LINE                                 |
//+------------------------------------------------------------------+
double iMACDGet(const int handle_iMACD,const int buffer,const int index)
  {
   double MACD[1];
//--- エラーコードをリセットする 
   ResetLastError();
//--- iMACDBuffer配列の一部に、0インデックスを持つ指標バッファの値を入力する 
   if(CopyBuffer(handle_iMACD,buffer,index,1,MACD)<0)
     {
      //--- コピーが失敗したら、エラーコードを報告する 
      PrintFormat("Failed to copy data from the iMACD indicator, error code %d",GetLastError());
      //--- 結果がゼロで終了し、指標が計算されていないとみなされることを意味する 
      return(0.0);
     }
   return(MACD[0]);
  }
//+------------------------------------------------------------------+

MQL5 test.mq5 EAパラメータは下記の通りです。

MQL5テスト1

図2 MQL5 test.mq5、9指標

Cache test.mq5 EAパラメータは下記の通りです。

  • Use Timer("0" -> タイマーオフ) — タイマーを使用する(0 — 未使用)
  • Use indicator("false" -> 9指標、"true" - 1指標) — 検査された指標の数(1または9)

キャッシュテスト1

図3 Cache test.mq5. タイマーなし、9指標

「ハンドルキャッシュなしのMQL4形式」の測定にはIndicatorsMQL4.mqファイルが使用されます。ファイルはSimpleCallMQL4.mqhトレーダーのハック: 定義と FOREACH のブレンド (#DEFINE) 稿を参照)を使用して接続されます。

#include <SimpleCall\SimpleCallMQL4.mqh> // ハンドルキャッシュなしのテスト
//#include <SimpleCall\SimpleCallMQL4Caching.mqh> // ハンドルキャッシュありのテスト
//#include <SimpleCall\SimpleCallString.mqh> // 文字列テスト

「ハンドルキャッシュ付きMQL4形式」を測定するには、ポスト#113 のハンドルキャッシュコードをIndicatorsMQL4.mqhに追加します(MACDのみ、他の機能は削除されます)。このファイルはIndicatorsMQL4Caching.mqhをして保存され、SimpleCallCaching.mqhとして接続されます。

//#include <SimpleCall\SimpleCallMQL4.mqh> // ハンドルキャッシュなしのテスト
#include <SimpleCall\SimpleCallMQL4Caching.mqh> // ハンドルキャッシュありのテスト
//#include <SimpleCall\SimpleCallString.mqh> // 文字列テスト

9つの指標とのアクセススタイルを比較した結果(図1に設定値があります)は下記の通りです。

MQL5対MQL4 9指標

図4 9指標へのアクセスにかかった時間

結果を比較する際には、テストEAはタスクをかなり複雑にしていることに注意してください。

  • データはNINE指標から同時に取得されます。
  • 指標は各ティックでアクセスされます。
  • M1時間枠 — 26 169 180ティックと370 355バーが生成されました。

ここでテストをしましょう。1つの指標のみを呼び出します(両方のEAに対してMQL5 test.mq5Cache test.mq5Use indicator...パラメータは"true"で、Cache test.mq5ではUse Timerが "0")。

MQL5対MQL4 1指標

図5 1指標へのアクセスにかかった時間

結論

ハンドルキャッシュを持つMQL4形式は、ハンドルキャッシュを持たないMQL4形式に比べて利点があります。しかし、MQL4形式はMQL5形式には完全に劣ります。 

ハンドル有効性についての制御の欠如

ここでハンドルキャッシュを使用することの大きな欠点について言及する必要があります。ユーザのキャッシュにハンドルが存在するかどうかを確認しないということです。つまり、指標ハンドル削除された場合にも何も処理されません。 

以下の状況を考えてみましょう。MQL4形式の指標を使用してハンドルをキャッシュします。EAからの最初のアクセス

   double macd_main_30=iMACD(NULL,0,30,26,9,PRICE_CLOSE,MODE_MAIN,0);

の後、ハンドルはユーザキャッシュに格納されます(これは構造体の配列または文字列の配列になります)。その後のEAからのすべてのアクセス

   double macd_main_30=iMACD(NULL,0,30,26,9,PRICE_CLOSE,MODE_MAIN,0);

はMQL5コアに渡されません。代わりに、キャッシュから取られたハンドルによる指標値が返されます。ここで、OnTimer()のハンドルを削除します。 それが "10"であることがわかっているとします。テストとして、SimpleCallMQL4Caching.mqhファイルがインクルードされるCache test.mq5 ファイルを使用しましょう。

//#include <SimpleCall\SimpleCallMQL4.mqh> // ハンドルキャッシュなしのテスト
#include <SimpleCall\SimpleCallMQL4Caching.mqh> // ハンドルキャッシュありのテスト
//#include <SimpleCall\SimpleCallString.mqh> // 文字列テスト

タイマーを必ず設定してください(ここでは、タイマーは6秒に設定され、1指標にアクセスします)。

キャッシュテスト2

図6 ハンドル削除のテスト設定

一番初めのOnTimer()エントリの後

OnTimer, IndicatorRelease(10)=true
iMACD: CopyBuffer error=4807
iMACD: CopyBuffer error=4807
iMACD: CopyBuffer error=4807
iMACD: CopyBuffer error=4807 

エラー4807が発生します。

 ERR_INDICATOR_WRONG_HANDLE  4807  Invalid indicator handle(無効な指標ハンドル)

これは、指標ハンドルの有効性に対する制御が不在であることを意味します。


指標ハンドルのキャッシュの仕方

指標ハンドルをキャッシュする一般的な原則は次の通りです。

  • カスタムハンドルキャッシュを作成します。
  • 指標からデータを要求するときに、要求された設定(銘柄、時間枠、平均期間など)でハンドルがすでに作成されているかどうかを確認します。
    • すでにカスタムキャッシュに存在する場合は、指標からデータを返します。
    • そのようなハンドルがまだ存在しない場合は、作成し、キャッシュに保存し、指標からデータを返します。


オプション1: 構造体の配列

実装はIndicatorsMQL4Caching.mqhCache test.mq5SimpleCallMQL4Caching.mqhを介して接続)で行われます。

Cache test.mq5SimpleCallMQL4Caching.mqhをインクルードします。

//#include <SimpleCall\SimpleCallMQL4.mqh> // ハンドルキャッシュなしのテスト
#include <SimpleCall\SimpleCallMQL4Caching.mqh> // ハンドルキャッシュありのテスト
//#include <SimpleCall\SimpleCallString.mqh> // 文字列テスト

まず、ファイルとiMACD関数に挿入された大きなコードブロックを見てみましょう。

...         
//+------------------------------------------------------------------+
//| CHandle構造体                                                     |
//+------------------------------------------------------------------+
template<typename T>
struct SHandle
  {
private:
   int               Handle;
   T                 Inputs;

public:
   //+------------------------------------------------------------------+
   //| 初期化リストを持つコンストラクタ                                     |
   //+------------------------------------------------------------------+
                     SHandle() : Handle(INVALID_HANDLE)
     {
     }
   //+------------------------------------------------------------------+
   //| オペレーションオーバーロード "=="                                    |
   //+------------------------------------------------------------------+
   bool operator==(const T &Inputs2) const
     {
      return(this.Inputs == Inputs2);
     }
   //+------------------------------------------------------------------+
   //| オペレーションオーバーロード "="                                     |
   //+------------------------------------------------------------------+
   void operator=(const T &Inputs2)
     {
      this.Inputs=Inputs2;
     }
   //+------------------------------------------------------------------+
   //| SHandle::GetHandle                                               |
   //+------------------------------------------------------------------+
   int GetHandle()
     {
      return((this.Handle != INVALID_HANDLE) ?this.Handle : (this.Handle = this.Inputs.GetHandle()));
     }
  };
//+------------------------------------------------------------------+
//| ハンドルを取得する                                                 |
//+------------------------------------------------------------------+
template<typename T>
int GetHandle(SHandle<T>&Handles[],const T &Inputs)
  {
   const int Size=ArraySize(Handles);

   for(int i=0; i<Size; i++)
      if(Handles[i]==Inputs)
         return(Handles[i].GetHandle());

   ArrayResize(Handles,Size+1);
   Handles[Size]=Inputs;

   return(Handles[Size].GetHandle());
  }
//+------------------------------------------------------------------+
//| Macd構造体                                                        |
//+------------------------------------------------------------------+
struct SMacd
  {
   string            symbol;
   ENUM_TIMEFRAMES   period;
   int               fast_ema_period;
   int               slow_ema_period;
   int               signal_period;
   ENUM_APPLIED_PRICE applied_price;
   //+------------------------------------------------------------------+
   //| デフォルトの空のコンストラクタ                                       |
   //+------------------------------------------------------------------+
                     SMacd(void)
     {
     }
   //+------------------------------------------------------------------+
   //| 初期化リストを持つコンストラクタ                                     |
   //+------------------------------------------------------------------+
                     SMacd(const string             &isymbol,
                                             const ENUM_TIMEFRAMES    &iperiod,
                                             const int                &ifast_ema_period,
                                             const int                &islow_ema_period,
                                             const int                &isignal_period,
                                             const ENUM_APPLIED_PRICE &iapplied_price) :
                                             symbol((isymbol== NULL)||(isymbol == "") ?Symbol() : isymbol),
                                             period(iperiod == PERIOD_CURRENT ?Period() : iperiod),
                                             fast_ema_period(ifast_ema_period),
                                             slow_ema_period(islow_ema_period),
                                             signal_period(isignal_period),
                                             applied_price(iapplied_price)
     {
     }
   //+------------------------------------------------------------------+
   //| SMacd::GetHandle                                                 |
   //+------------------------------------------------------------------+
   int GetHandle(void) const
     {
      return(iMACD(this.symbol, this.period, this.fast_ema_period, this.slow_ema_period, this.signal_period, this.applied_price));
     }
   //+------------------------------------------------------------------+
   //| オペレーションオーバーロード "=="                                   |
   //+------------------------------------------------------------------+
   bool operator==(const SMacd &Inputs) const
     {
      return((this.symbol == Inputs.symbol) &&
             (this.period == Inputs.period) &&
             (this.fast_ema_period == Inputs.fast_ema_period) &&
             (this.slow_ema_period == Inputs.slow_ema_period) &&
             (this.signal_period == Inputs.signal_period) &&
             (this.applied_price == Inputs.applied_price));
     }
  };
//+------------------------------------------------------------------+
//| MQL4表記でのiMACD2関数                                             |
//|   バッファ番号は次のとおりである                                     |
//|      MQL4 0 - MODE_MAIN, 1 - MODE_SIGNAL                         |
//|      MQL5 0 - MAIN_LINE, 1 - SIGNAL_LINE                         |
//+------------------------------------------------------------------+
int iMACD2(const string             symbol,
           const ENUM_TIMEFRAMES    period,
           const int                fast_ema_period,
           const int                slow_ema_period,
           const int                signal_period,
           const ENUM_APPLIED_PRICE applied_price)
  {
   static SHandle<SMacd>Handles[];
   const SMacd Inputs(symbol,period,fast_ema_period,slow_ema_period,signal_period,applied_price);

   return(GetHandle(Handles, Inputs));
  }
//+------------------------------------------------------------------+
//| MQL4表記でのiAC関数                                               |
...
//+------------------------------------------------------------------+
//| MQL4表記でのiMACD関数                                              |
//|   バッファ番号は次のとおりである                                     |
//|      MQL4 0 - MODE_MAIN, 1 - MODE_SIGNAL                         |
//|      MQL5 0 - MAIN_LINE, 1 - SIGNAL_LINE                         |
//+------------------------------------------------------------------+
double   iMACD(
               string                     symbol,              // 銘柄名 
               ENUM_TIMEFRAMES            timeframe,           // 時間枠
               int                        fast_ema_period,     // 高速平均計算の期間
               int                        slow_ema_period,     // 低速平均計算の期間
               int                        signal_period,       // 差の平均化期間
               ENUM_APPLIED_PRICE         applied_price,       // 価格またはハンドルの種類
               int                        buffer,              // バッファ
               int                        shift                // シフト
               )
  {
   double result=NaN;
//---
   int handle=iMACD2(symbol,timeframe,fast_ema_period,slow_ema_period,signal_period,applied_price);
   if(handle==INVALID_HANDLE)
...

その動作について説明します。初めに、MACDからデータがリクエストされます。

   double macd_main_30=iMACD(NULL,0,30,26,9,PRICE_CLOSE,MODE_MAIN,0);

次にiMACD関数に入ってiMACD2に移動します。

//+------------------------------------------------------------------+
//| MQL4表記でのiMACD2関数                                             |
//|   バッファ番号は次のとおりである                                     |
//|      MQL4 0 - MODE_MAIN, 1 - MODE_SIGNAL                         |
//|      MQL5 0 - MAIN_LINE, 1 - SIGNAL_LINE                         |
//+------------------------------------------------------------------+
int iMACD2(const string             symbol,
           const ENUM_TIMEFRAMES    period,
           const int                fast_ema_period,
           const int                slow_ema_period,
           const int                signal_period,
           const ENUM_APPLIED_PRICE applied_price)
  {
   static SHandle<SMacd>Handles[];
   const SMacd Inputs(symbol,period,fast_ema_period,slow_ema_period,signal_period,applied_price);

   return(GetHandle(Handles, Inputs));
  }

SMacd型のHandles[]静的配列がここで宣言されます(初めのエントリで作成されその後のエントリでは再度作成されません)。またSMacd型のInputsオブジェクトは作成されると同時にパラメータで初期化されます。

その後、リンクを使用して、GetHandle関数に(SHandle::GetHandleとSMacd::GetHandleではなく) Handle[]配列とInputsオブジェクトを渡します。

//+------------------------------------------------------------------+
//| ハンドルを取得する                                                 |
//+------------------------------------------------------------------+
template<typename T>
int GetHandle(SHandle<T>&Handles[],const T &Inputs)
  {
   const int Size=ArraySize(Handles);

   for(int i=0; i<Size; i++)
      if(Handles[i]==Inputs)
         return(Handles[i].GetHandle());

   ArrayResize(Handles,Size+1);
   Handles[Size]=Inputs;
   return(Handles[Size].GetHandle());
  }

この関数では、見つかった指標を配列で返すか、ハンドルが見つからない場合にはSHandle::GetHandleで受け取ります。

しかしこれは最初のアクセスdえそのようなハンドルはまだありません。 

   //+------------------------------------------------------------------+
   //| SHandle::GetHandle                                               |
   //+------------------------------------------------------------------+
   int GetHandle()
     {
      return((this.Handle != INVALID_HANDLE) ?this.Handle : (this.Handle = this.Inputs.GetHandle()));
     }

SMacd::GetHandle:に作成します。

   //+------------------------------------------------------------------+
   //| SMacd::GetHandle                                                 |
   //+------------------------------------------------------------------+
   int GetHandle(void) const
     {
      return(iMACD(this.symbol, this.period, this.fast_ema_period, this.slow_ema_period, this.signal_period, this.applied_price));
     }


オプション2: 文字列配列

実装はIndicatorsMQL4String.mqhファイル(Cache test.mq5SimpleCallString.mqhを介して接続)で行われます。

Cache test.mq5 EAで、SimpleCallString.mqhをインクルードします。

//#include <SimpleCall\SimpleCallMQL4.mqh> // ハンドルキャッシュなしのテスト
//#include <SimpleCall\SimpleCallMQL4Caching.mqh> // ハンドルキャッシュありのテスト
#include <SimpleCall\SimpleCallString.mqh> // 文字列テスト

文字列を使った作業には、スピードの点で非常にコストがかかります。これは少し後でわかります。よって、パラメータを文字列として保管するというアイディアは下記の通りです

   string Hashes[];
   static int Handles[];
   string hash=((symbol==NULL) || (symbol=="") ?Symbol() : symbol)+
               (string)(timeframe==PERIOD_CURRENT ?Period() : timeframe)+
               (string)(fast_ema_period)+
               (string)(slow_ema_period)+
               (string)(signal_period)+
               (string)(applied_price);

上記の図1のパラメータを使用して、EAからiMACDにアクセスします。

 NN  コード 時間
  1
//--- NN2
//static string Hashes[];
//static int Handles[];
//string hash=((symbol==NULL) || (symbol=="") ?Symbol() : symbol)+
//            (string)(timeframe==PERIOD_CURRENT ?Period() : timeframe)+
//            (string)(fast_ema_period)+
//            (string)(slow_ema_period)+
//            (string)(signal_period)+
//            (string)(applied_price);
//--- NN3
//static string Hashes[];
//static int Handles[];
//string hash="";
//StringConcatenate(hash,
//                  ((symbol==NULL) || (symbol=="") ?Symbol() : symbol),
//                  (timeframe==PERIOD_CURRENT ?Period() : timeframe),
//                  fast_ema_period,
//                  slow_ema_period,
//                  signal_period,
//                  applied_price);
 0:01:40.953
  2
//--- NN2
   static string Hashes[];
   static int Handles[];
   string hash=((symbol==NULL) || (symbol=="") ?Symbol() : symbol)+
               (string)(timeframe==PERIOD_CURRENT ?Period() : timeframe)+
               (string)(fast_ema_period)+
               (string)(slow_ema_period)+
               (string)(signal_period)+
               (string)(applied_price);
//--- NN3
//static string Hashes[];
//static int Handles[];
//string hash="";
//StringConcatenate(hash,
//                  ((symbol==NULL) || (symbol=="") ?Symbol() : symbol),
//                  (timeframe==PERIOD_CURRENT ?Period() : timeframe),
//                  fast_ema_period,
//                  slow_ema_period,
//                  signal_period,
//                  applied_price);
 0:05:20.953
  3
//--- NN2
//static string Hashes[];
//static int Handles[];
//string hash=((symbol==NULL) || (symbol=="") ?Symbol() : symbol)+
//            (string)(timeframe==PERIOD_CURRENT ?Period() : timeframe)+
//            (string)(fast_ema_period)+
//            (string)(slow_ema_period)+
//            (string)(signal_period)+
//            (string)(applied_price);
//--- NN3
   static string Hashes[];
   static int Handles[];
   string hash="";
   StringConcatenate(hash,
                     ((symbol==NULL) || (symbol=="") ?Symbol() : symbol),
                     (timeframe==PERIOD_CURRENT ?Period() : timeframe),
                     fast_ema_period,
                     slow_ema_period,
                     signal_period,
                     applied_price);
 0:04:12.672

テスト1では、文字列は処理されず指標へMQL4形式でアクセスするベンチマークテストです。テスト2では、文字列が"+"を使用して作成されています。テスト3では、文字列はStringConcatenateを使用して形成されます。

時間測定によれば、StringConcatenateはテスト2と比較して21%の時間利益を与えますが、全体のパフォーマンスはテスト1の2.5倍になります。

したがって、指標ハンドルを文字列として保存するという考え方は破棄することができます。


オプション3 — クラスキャッシュハンドル iIndicators.mqhクラスはCache test.mq5EAにSimpleCallMQL4CachingCiIndicators.mqhを介して接続されます)。

Cache test.mq5 EAにSimpleCallMQL4CachingCiIndicators.mqhをインクルードします。

//#include <SimpleCall\SimpleCallMQL4.mqh> // ハンドルキャッシュなしのテスト
//#include <SimpleCall\SimpleCallMQL4Caching.mqh> // ハンドルキャッシュありのテスト
//#include <SimpleCall\SimpleCallString.mqh> // 文字列テスト
#include <SimpleCall\SimpleCallMQL4CachingCiIndicators.mqh>

各指標について、CHandleクラスの静的オブジェクトが作成されます(該当するMQL4形式関数で)。それはCiIndicatorsクラス(指標パラメータと設定を含むクラス)オブジェクトの保管として機能します。

スキーム

図7 構造

CiIndicatorsクラスは5つの'private'変数に基づいています。

//+------------------------------------------------------------------+
//| CiIndicatorsクラス                                                |
//+------------------------------------------------------------------+
class CiIndicators
  {
private:
   string            m_symbol;                        // 銘柄名
   ENUM_TIMEFRAMES   m_period;                        // 時間枠
   ENUM_INDICATOR    m_indicator_type;                // ENUM_INDICATOR列挙体からの指標の種類 
   int               m_parameters_cnt;                // パラメータ数
   MqlParam          m_parameters_array[];            // パラメータの配列

public:

これは完全にIndicatorCreate関数の変数に対応します。IndicatorCreateを介して指標ハンドルを受け取るので、これには意味があります。

CHandleクラスは2つの配列を使用して構築されています。

//+------------------------------------------------------------------+
//| CHandleクラス                                                     |
//+------------------------------------------------------------------+
class CHandle
  {
private:
   int               m_handle[];
   CiIndicators      m_indicators[];

public:

m_handle配列は指標ハンドルを含み、m_indicatorsアイ列はCiIndicatorsクラス配列です。

MACDを例としてCiIndicators及びCHandleクラスで作業するコードは下記の通りです。

//+------------------------------------------------------------------+
//| MQL4表記でのiMACD関数                                              |
//|   バッファ番号は次のとおりである                                     |
//|      MQL4 0 - MODE_MAIN, 1 - MODE_SIGNAL                         |
//|      MQL5 0 - MAIN_LINE, 1 - SIGNAL_LINE                         |
//+------------------------------------------------------------------+
double   iMACD(
               string                     symbol,              // 銘柄名 
               ENUM_TIMEFRAMES            timeframe,           // 時間枠
               int                        fast_ema_period,     // 高速平均計算の期間
               int                        slow_ema_period,     // 低速平均計算の期間
               int                        signal_period,       // 差の平均化期間
               ENUM_APPLIED_PRICE         applied_price,       // 価格またはハンドルの種類
               int                        buffer,              // バッファ
               int                        shift                // シフト
               )
  {
//---
   static CHandle Handles_MACD;
//--- 構造体に指標パラメータを書き込む      
   MqlParam pars[4];
//--- 高速MA期間 
   pars[0].type=TYPE_INT;
   pars[0].integer_value=fast_ema_period;
//--- 低速MA期間 
   pars[1].type=TYPE_INT;
   pars[1].integer_value=slow_ema_period;
//--- 高速移動平均と低速移動平均の差を平均化する期間 
   pars[2].type=TYPE_INT;
   pars[2].integer_value=signal_period;
//--- 価格の種類 
   pars[3].type=TYPE_INT;
   pars[3].integer_value=applied_price;

   CiIndicators MACD_Indicator;
   MACD_Indicator.Init(Symbol(),Period(),IND_MACD,4);
   int handle=Handles_MACD.GetHandle(MACD_Indicator,Symbol(),Period(),IND_MACD,4,pars);
//---
   double result=NaN;
//---
   if(handle==INVALID_HANDLE)
     {
      Print(__FUNCTION__,": INVALID_HANDLE error=",GetLastError());
      return(result);
     }
   double val[1];
   int copied=CopyBuffer(handle,buffer,shift,1,val);
   if(copied>0)
      result=val[0];
   else
      Print(__FUNCTION__,": CopyBuffer error=",GetLastError());
   return(result);
  }

  • CHandleクラスのHandles_MACD静的配列が宣言されます。これは、生成されたハンドルとMACDパラメータを格納するためです。
  • CiIndicatorsクラスのMACD_Indicatorオブジェクトが作成されて初期化されます。
  • Handles_MACD::GetHandle関数で指標ハンドルが作成されます(または、そのようなパラメータに対してすでに作成されている場合は渡されます)。

CiIndicators.mqhクラスのMQL4形式アクセスとキャッシュを扱う操作時間は2分30秒かかりました。


9指標へのアクセス速度の最終グラフ

キャッシュを持つ/持たないMQL4形式はCache test.mq5を使用して確認され、標準MQL5形式テストはMQL5 test.mq5を使用して行われます。

MQL5対MQL4 9指標の概要チャート


終わりに

指標への正しいMQL5アクセスのパラダイムに逆らって、いくつか興味深い実験を行いました。その結果、MQL5コア内でハンドルを処理する内部メカニズムの詳細を学びました。

  • ハンドルカウンターについて
  • キャッシングと管理について

指標にアクセスするさまざまな方法をテストした結果、MQL5アクセススタイルはMQL4形式(ハンドルキャッシュの有無にかかわらず)よりもはるかに高速でした。