MT5のテクニカル指標関数をいくつかのライブラリで実行してみた

#はじめに
MetaTrader5(MT5)でテクニカル指標関数を使う方法はいくつかあります。今回、MQL5の組み込み関数をそのまま使う方法、MQL5の標準クラスライブラリを使う方法、筆者独自のライブラリを使う方法の３通りについて、実行時間を計測してみました。

#MQL5の組み込み関数をそのまま使う方法
テクニカル指標関数はたくさん種類がありますが、ここでは一番基本的な移動平均を取り上げます。関数はiMA()です。

サンプルプログラムとして、２本の移動平均線の交差のシグナルを出するだけのEAを作ってみます。

プログラムの詳細は省略しますが、MQL5の組み込み関数だけ使ったEAは次のように書けます。

```nativeMA.mq5
double FastMA[];  //短期移動平均用配列
double SlowMA[];  //長期移動平均用配列
int FastMAHandle; //短期移動平均用ハンドル
int SlowMAHandle; //長期移動平均用ハンドル

input int FastMAPeriod = 20;  //短期移動平均の期間
input int SlowMAPeriod = 50;  //長期移動平均の期間

//初期化関数
int OnInit()
{
   //テクニカル指標の初期化
   FastMAHandle = iMA(_Symbol, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
   SlowMAHandle = iMA(_Symbol, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
   ArraySetAsSeries(FastMA, true);
   ArraySetAsSeries(SlowMA, true);
   return 0;
}

//ティック時実行関数
void OnTick()
{
   static ulong _sum = 0; //合計
   static int _count = -10; //カウント
   ulong _start = GetMicrosecondCount(); //開始時刻
   
   int signal = EntrySignal(); //仕掛けシグナル
   
   if(_count >= 0) _sum += GetMicrosecondCount() - _start;
   _count++;
   if(_count == 100) {Print("100 ticks = ", _sum, " μs"); ExpertRemove();}
}

//仕掛けシグナル関数
int EntrySignal()
{
   //テクニカル指標の更新
   CopyBuffer(FastMAHandle, 0, 0, 3, FastMA);
   CopyBuffer(SlowMAHandle, 0, 0, 3, SlowMA);

   int ret = 0; //シグナルの初期化

   //買いシグナル
   if(FastMA[2] <= SlowMA[2] && FastMA[1] > SlowMA[1]) ret = 1;
   //売りシグナル
   if(FastMA[2] >= SlowMA[2] && FastMA[1] < SlowMA[1]) ret = -1;

   return ret; //シグナルの出力
}
```
OnTick()中で、シグナルを計算するEntrySignal()にかかる時間を計測しています。各tickでばらつきがあるので、100tick分をトータルしてみました。ここで使ったGetMicrosecondCount()という関数は、マイクロ秒単位で時間を計測する（らしい）ので、結果はμsということになります。

あと、_countを-10から始めているのは、EAを起動して10tick経って落ち着いてから計測を始めるためです。

#MQL5の標準クラスライブラリを使う方法
２番目の方法は、MQL5の標準クラスライブラリを使う方法です。

前述のようにMQL5のテクニカル指標関数は、ハンドルと配列を用意して、初期化、tickごとに配列にコピーしてシグナルの計算、といった具合に手続きが面倒です。

そこで、MQL5では、クラスライブラリを使ってテクニカル指標関数にアクセスする方法を用意しています。
https://www.mql5.com/ja/docs/standardlibrary

MQL5のレファレンスでは、これを標準ライブラリと呼んでいます。これを使って移動平均のシグナルを算出するだけのEAは次のように書けます。

```stdlibMA.mq5
#include <Indicators\Indicators.mqh> //テクニカル指標クラス

CiMA FastMA, SlowMA;         //移動平均のオブジェクト

input int FastMAPeriod = 20;  //短期移動平均の期間
input int SlowMAPeriod = 50;  //長期移動平均の期間

//初期化関数
int OnInit()
{
   //テクニカル指標の初期化
   FastMA.Create(_Symbol, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
   SlowMA.Create(_Symbol, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
   return 0;
}

//ティック時実行関数
void OnTick()
{
   static ulong _sum = 0; //合計
   static int _count = -10; //カウント
   ulong _start = GetMicrosecondCount(); //開始時刻
   
   int signal = EntrySignal(); //仕掛けシグナル
   
   if(_count >= 0) _sum += GetMicrosecondCount() - _start;
   _count++;
   if(_count == 100) {Print("100 ticks = ", _sum, " μs"); ExpertRemove();}
}

//仕掛けシグナル関数
int EntrySignal()
{
   //テクニカル指標の更新
   FastMA.Refresh();
   SlowMA.Refresh();

   int ret = 0; //シグナルの初期化

   //買いシグナル
   if(FastMA.Main(1) > SlowMA.Main(1) && FastMA.Main(2) <= SlowMA.Main(2)) ret = 1;
   //売りシグナル
   if(FastMA.Main(1) < SlowMA.Main(1) && FastMA.Main(2) >= SlowMA.Main(2)) ret = -1;

   return ret; //シグナルの出力
}
```

この場合、CiMAのオブジェクトを宣言して、初期化、更新すれば指標値がそのまま使えるので、MQL5の関数をそのまま使うよりは便利です。時間を計測する部分は前のプログラムと同じです。

#独自のライブラリを使う方法
最後に独自のライブラリを使う方法です。独自のライブラリといっても、以下のkindle本に掲載されているものなので、詳しいことは省略します。
[『メタトレーダー４＆５共通ライブラリによるＥＡ開発入門』](https://amzn.to/33JHotW)

このライブラリは、MQL4、MQL5でEAを開発する際の利便性を考慮して作ったものですが、そのなかに、MQL5のテクニカル指標関数をMQL4と同じ形式で使えるようにしたライブラリがあります。今回はテクニカル指標関数だけを調べているので、そのライブラリだけを使ってみます。同様にプログラムは次のように書けます。

```libeaMA.mq5
#include "LibMQL4.mqh"  //MQL4互換ライブラリ

input int FastMAPeriod = 20;  //短期移動平均の期間
input int SlowMAPeriod = 50;  //長期移動平均の期間

//ティック時実行関数
void OnTick()
{
   static ulong _sum = 0; //合計
   static int _count = -10; //カウント
   ulong _start = GetMicrosecondCount(); //開始時刻
   
   int signal = EntrySignal(); //仕掛けシグナル
   
   if(_count >= 0) _sum += GetMicrosecondCount() - _start;
   _count++;
   if(_count == 100) {Print("100 ticks = ", _sum, " μs"); ExpertRemove();}
}

//仕掛けシグナル関数
int EntrySignal()
{
   //１本前と２本前の移動平均
   double FastMA1 = iMA(_Symbol, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
   double FastMA2 = iMA(_Symbol, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 2);
   double SlowMA1 = iMA(_Symbol, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
   double SlowMA2 = iMA(_Symbol, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 2);

   int ret = 0; //シグナルの初期化

   //買いシグナル
   if(FastMA2 <= SlowMA2 && FastMA1 > SlowMA1) ret = 1;
   //売りシグナル
   if(FastMA2 >= SlowMA2 && FastMA1 < SlowMA1) ret = -1;

   return ret; //シグナルの出力
}
```
これは、MQL4と同じように、指標値が必要になったところでiMA()を呼び出す方法です。ハンドルやオブジェクトを用意する必要はありません。

#測定結果
以上３つのプログラムを以下の条件で実行してみました。

```
CPU: AMD Ryzen5 3400G 3.70GHz
RAM: 16GB
OS: Windows 10 Home
MT5: Alpari build 2560
```
それぞれ５回ずつ実行した結果です。100tickの合計でもこの程度のばらつきがあります。

##nativeMA.mq5
```
100 ticks = 395 μs
100 ticks = 361 μs
100 ticks = 361 μs
100 ticks = 304 μs
100 ticks = 309 μs
```

##stdlibMA.mq5
```
100 ticks = 914 μs
100 ticks = 993 μs
100 ticks = 838 μs
100 ticks = 883 μs
100 ticks = 883 μs
```

##libeaMA.mq5
```
100 ticks = 472 μs
100 ticks = 440 μs
100 ticks = 407 μs
100 ticks = 552 μs
100 ticks = 389 μs
```

#まとめ
以上のようにMQL5のテクニカル指標関数は、ライブラリを使うことで実行時間はかかってしまいます。ただ、同じライブラリでも独自ライブラリの方が標準ライブラリより高速に計算できることがわかりました。

これはソースプログラムを見れば一目瞭然ですが、標準ライブラリは、色々な人が利用する可能性があるので、多くの機能を組み込んでいます。それに対して独自ライブラリは、作者が必要な機能だけを組み込んでいるので、それほど実行効率が下がらなかったのだと思います。