MQL4のMovingAverageEA を MQL5 に移植する。

最新のMetaTrader5 のインストーラからインストールすると、MQL5 Reference の英語版が手に入ります。関数名が変わった部分もあるので、それを参照するのがよさそうです。
(ちなみに、この英語版、google翻訳版を校正して作られているっぽいです..。「C++」という表記が「C + +」と間延びしているのが特徴です。笑)



さて、Nero Tulipさんから、単純なEAの移植をしてみて欲しいとのリクエストがありましたので、MQL4付属のサンプルEA Moving Average.mq4でロット管理、ポジション管理を割愛したサンプルを移植してみました。MovingAverageEA_MQL4to5.mq5


このEA自体、いくつか問題があって、例えば

if(Volume[0]>1) return;

は、新規 Bar の初回更新時に、既に Volume[0]=2 で来てしまったらダメじゃん..とか、

if(Open[1]>ma && Close[1] < ma)  

は、偶然 ma=Open[1] やma=Close[1] だったら成立しないし、窓明け気味に ma を超えてしまったら検出できない..という致命的な欠陥があるのですが、シンプルな構造なのでサンプルとして分かりやすいかなと思って選んでいます。


それから、既にご存知の通り、MetaTrader5 ではポジションが合算されているのと、売買履歴は、注文履歴と約定履歴の2つに分かれてしまったので、今回の移植からは、履歴を参照するロット管理、ポジション管理を省略しています。



まず、移植の方針ですが、
(A) MQL5時代にふさわしいコードとして新規に書き直す
(B) MQL4のコードを出来る限り再利用する
があると思います。(A) をやりたい人は、MACD_Sample_1.zipを参考に自力でどうにかできるでしょうから、当分は、(B)を想定して移植を進めます。現状では、MQL5 のプログラミングに構造体の知識は必須ですが、クラスは一切使わなくても、EAは作れます。ただ、便利なクラスを敢えて使わないでおくのも、もったいないので、一部使用することにします。


Close[ ],Open[ ],High[ ],Low[ ],Bid,Ask,BarsをMQL4 互換にする。


これまでグローバルな配列、変数として、プログラム内の任意の場所で利用できていましたので、MQL5 でもそのようにしてしまいます。
(..そんなことしたらバグの温床に…と拒否反応を示す人は、真似しないでください...T_T)

(1)グローバルな領域に変数宣言し、

//MQL4 Compatible start
double Ask,Bid;
double Open,Close,High,Low;
long   Volume[];
int Bars;
//MQL4 Compatible end

(2)OnInit()内で、配列を数える方向を MQL4方式に変更し

int OnInit()
  {
//---
//MQL4 Compatible start
   ArraySetAsSeries(Open,true);
   ArraySetAsSeries(Close,true);
   ArraySetAsSeries(High,true);
   ArraySetAsSeries(Low,true);
   ArraySetAsSeries(Volume,true);
//MQL4 Compatible end

(3)Tick更新のたびに、全データをコピーする。

void OnTick() // void start()
  {
//MQL4 Compatible start
   Ask  = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   Bid  = SymbolInfoDouble(Symbol(),SYMBOL_BID);
   Bars = Bars(Symbol(),PERIOD_CURRENT);
   CopyClose(_Symbol,PERIOD_CURRENT,0,Bars,Close);
   CopyOpen(_Symbol,PERIOD_CURRENT,0,Bars,Open);
   CopyHigh(_Symbol,PERIOD_CURRENT,0,Bars,High);
   CopyLow(_Symbol,PERIOD_CURRENT,0,Bars,Low);
   CopyTickVolume(_Symbol,PERIOD_CURRENT,0,Bars,Volume);
//MQL4 Compatible end

とすればOKです。
自分は Close[ ] しか使わないよって人は、余分な宣言やコピーを削除してしまってもよいですし、
配列の順序が、Close[0] が最新ではなくて Close[Bars-1] が最新でかまわないのなら (2) は省略可能です。


テクニカル指標関数 iMA を使う

(1)グローバルな領域にiMA のハンドルと配列を宣言し、

// use iMA
int iMA_handle;
double MABuffer[];


(2)OnInit()内で、iMA のハンドルを設定し、配列を数える方向を MQL4方式に変更し

// use iMA
   iMA_handle=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE);
   ArraySetAsSeries(MABuffer,true);


(3)Tick更新のたびに、データをコピーする。

// use iMA
   int copied=CopyBuffer(iMA_handle,0,0,Bars,MABuffer);
   if(copied<3) return;

これで、移動平均の値が MABuffer[0] のような形で取得できます。
iMACD やiBands のような複数の値を持つテクニカルでは、

CopyBuffer(iMACD_handle,0,0,Bars,MACDBuffer);
CopyBuffer(iMACD_handle,1,0,Bars,MACDSignalBuffer);

のように CopyBuffer の2番目の引数を指定して取得します。


iMA の呼び出し回数が少ないプログラムでは、http://www.dr-ea.com/meta-blog/mql5/mql4tomql5no1.html で紹介されている簡潔な手法でもかまわないと思います。
iMAと互換性のある関数を用意する実装なので、移植は楽なのですが、forループの中でiMA を呼び出している場合に、無駄な処理が多くなるので、動作速度が気になります..^^;;


とりあえず、今日はここまで。