【メモ】MQL4をMQL5に移植する。（その１：レートの受信）

MQL4をMQL5に移植するので、備忘録としてメモを作成。

基本情報をざっと確認してから移植してみる。

###MQL4になじみのあるプログラマの MQL5 言語へのコード適応の簡易化を意図した情報
[MQL5リファレンス「MQL4からMQL5への移行」](https://www.mql5.com/ja/docs/migration)参照

##特別関数 init、start 及び deinit

| MQL4             | MQL5              | 
|:-----------------|:------------------| 
| init             | OnInit            | 
| start            | OnStart           | 
| deinit           | OnDeinit          | 
MQL4で既にOnInit()/OnDeinit()を利用しているので、気にせず先に進む。

| MQL5プログラム    | メインファンクション| 
|:-----------------|:------------------| 
| script           | OnStart           | 
| indicator        | OnCalculate       | 
| エキスパートアドバイザー | OnTick      | 
"script"の説明は以下。
スクリプトは、アクションを1回実行するためのプログラムです。エキスパートアドバイザーとは異なり、スクリプトはトリガ、初期化と初期化解除以外のイベントを処理しません。スクリプトコードには OnStart 処理関数が必要です。スクリプトは "MQL5\Scripts" に格納されています。

MT5にデフォルトでインストールされているサンプルを見ると、クラスが格納されていて、OnStart処理関数の記述がある。Onstartで処理が必要なクラスは、別ファイルで作成して "Scripts"フォルダに保存する様子。これは後で試してみる。

##定義済み関数
MQL5 には Ask、Bid、Bars といった事前定義された変数は無く、Point 及び Digits 変数はわずかに異なるスペルを持っている。

| MQL4             | MQL5              | 
|:-----------------|:------------------| 
| Digits           | _Digits           | 
| Point            | _Point            | 
|                  | _LastError        | 
|                  | _Period           | 
|                  | _Symbol           | 
|                  | _StopFlag         | 
|                  | _UninitReason     | 
MQL4が空欄になっているものは、関数で値を取得しているもの。例えば、"_LastError"に該当する直近のエラーコードは、GetLastError()の戻り値として取得している。

##時系列へのアクセス
MQL5 では Open[]、High[]、Low[]、Close[]、Volume[] 及び Time[] の定義済み時系列は存在しない。時系列の必要な深さは、対応する時系列アクセス関数を使用して設定出来る。

例えば、それぞれの足でのOpen(始値)は、CopyOpen()関数で取得できる。

--
int  CopyOpen(
  string          symbol_name,      銘柄名
  ENUM_TIMEFRAMES  timeframe,       期間
  int              start_pos,       開始位置
  int              count,           複製するデータ数
  double           open_array[]     始値の複製先の配列
); 

##エキスパートアドバイザー
MQL4 と異なって、MQL5 の エキスパートアドバイザーは新しいティックの受信 イベント を処理する関数である OnTick を必要としない。（MQL4 iのstart 関数は新しいティックが受信された際に実行される。）MQL5 では、エキスパートアドバイザーは複数の種類のイベントの事前定義されたハンドラ関数を含むことが出来る。

- OnTick – 新しいティックの受信
- OnTimer – タイマーイベント
- OnTrade - 取引イベント
- OnChartEvent – キーボードやマウスからの入力のイベント、グラフィックオブジェクトの移動イベント、ラベル編集オブジェクトの入力フィールドでのテキスト編集完了のイベント
- OnBookEvent – 板情報の状態変化のイベント

OnTick(), OnTime()はMT4で既に利用しているので違和感はない。OnTrade()以下は、今回の実装では必要無さそうなので気にせず先に進む。

基本動作の確認はここまでとし、実際にMT4のコードをMT5に移植してみる。検証用のサンプルとして、MT4から価格情報を取得し、外部に送信するEAをした。

---
###実際にMT4のコードをMT5に移植してみる。

検証用のコードの拡張子を ".mq4" から ".mq5" に変更しコンパイルするとエラーが出るので、これらのエラーを順番に調べながら修正して移植する。

##口座情報関の移植
接続業者の情報を取得する AccountCompany(), AccountServer()関数がエラーとなっているので調べたところ、MT5ではそれぞれ、AccountInfoString(ACCOUNT_COMPANY), AccountInfoString(ACCOUNT_SERVER)で取得可能なので修正。

口座情報は、AccountInfoInteger(), AccountInfoDouble(), AccountInfoString()関数で取得可能。関数のパラメータは、対応するENUM_ACCOUNT_INFO の列挙からの値を受け入れる。

ENUM_ACCOUNT_INFO_STRING

| 識別子            | 説明              | 種類      | 
|:-----------------|:------------------|:----------| 
| ACCOUNT_NAME     | クライアント名          | string  |
| ACCOUNT_SERVER   | 取引サーバー名          | string  |
| ACCOUNT_CURRENCY | 口座通貨                | string  |
| ACCOUNT_COMPANY  | 口座を提供する会社の名称 | string  |
AccountInfoStringでは、これら4つの口座情報プロパティを取得することが可能。他にも多くのプロパティを取得できる関数がある。詳細は[MT5のドキュメントにある口座プロパティの項目](https://www.mql5.com/ja/docs/constants/environment_state/accountinformation)を参照。

```mq5:sample.mq5
//+------------------------------------------------------------------+
//| スクリプトプログラムを開始する関数                                  |
//+------------------------------------------------------------------+
void OnStart()
 {
//--- 会社名
  string company=AccountInfoString(ACCOUNT_COMPANY);
//--- クライアント名
  string name=AccountInfoString(ACCOUNT_NAME);
//--- 口座番号
  long login=AccountInfoInteger(ACCOUNT_LOGIN);
//--- サーバ名
  string server=AccountInfoString(ACCOUNT_SERVER);
//--- 口座通貨
  string currency=AccountInfoString(ACCOUNT_CURRENCY);
//--- デモ、コンテスト、またはリアル（実際の）口座
  ENUM_ACCOUNT_TRADE_MODE account_type=(ENUM_ACCOUNT_TRADE_MODE)AccountInfoInteger(ACCOUNT_TRADE_MODE);
//--- 列挙値を理解出来る形式に変換する
  string trade_mode;
  switch(account_type)
    {
    case ACCOUNT_TRADE_MODE_DEMO:
        trade_mode="demo";
        break;
    case ACCOUNT_TRADE_MODE_CONTEST:
        trade_mode="contest";
        break;
    default:
        trade_mode="real";
        break;
    }
//--- ストップアウトは百分率または通貨で設定される
  ENUM_ACCOUNT_STOPOUT_MODE stop_out_mode=(ENUM_ACCOUNT_STOPOUT_MODE)AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE);
//--- 証拠金コールとストップアウトが発生した時のレベルの値を取得する
  double margin_call=AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL);
  double stop_out=AccountInfoDouble(ACCOUNT_MARGIN_SO_SO);
//--- 簡単な口座情報を表示する
  PrintFormat("The account of the client '%s' #%d %s opened in '%s' on the server '%s'",
              name,login,trade_mode,company,server);
  PrintFormat("Account currency - %s, MarginCall and StopOut levels are set in %s",
              currency,(stop_out_mode==ACCOUNT_STOPOUT_MODE_PERCENT)?"percentage":" money");
  PrintFormat("MarginCall=%G, StopOut=%G",margin_call,stop_out);
 }
```

MT5の設定情報もアカウント情報として取得出来る。例えば、自動売買ボタンのON/OFFは、MQL4で "bool IsExpertEnabled();" で取得していたが、MQL5では "bool AccountInfoInteger(ACCOUNT_TRADE_EXPERT);" で取得できる。

MQL5の記事に[MQL4からMQL5への移植](https://www.mql5.com/ja/articles/81)の比較表を見つけたのでリンクを掲載する。

##時系列データへのアクセス（Bid, Ask）
Bid, Ask の取得がエラーとなっていたので修正する。MQL5のドキュメントを確認すると、SymbolInfoTick() 関数とMqlTick構造体でBid, Askとまとめて取得するとのこと。

```
struct MqlTick
 {
  datetime   time;         // 最新の価格更新の時間
  double       bid;           // 現在のBid価格
  double       ask;           // 現在のAsk価格
  double       last;         // 最後の取引の現在価格(Last)
  ulong       volume;       // 現在のLast価格の数量
  long         time_msc;     // ミリ秒単位の最新の価格更新の時間
  uint       flags;         // ティックフラグ
  double       volume_real;   // 現在のLast価格のより正確な数量
 };
```

```mq5:bid取得の例：
MqlTick last_tick;

void OnTick()
 {
//---
  if(SymbolInfoTick(Symbol(),last_tick))
    {
    Print(last_tick.time,": Bid = ",last_tick.bid,
          " Ask = ",last_tick.ask,"  Volume = ",last_tick.volume);
    }
  else Print("SymbolInfoTick() failed, error = ",GetLastError());
//---
 }
```
bid, askがどこでも使えるよう、Global変数として MqlTick オブジェクトを定義し、OnTick()で更新する。あとは、MQL4の Bid/Ask を、last_tick.bid/last_tick.ask で置き換える。

今回はレート取得用のEAを移植したので、これだけの修正で移植が完了。次は取引用のEAを移植してみる。