English Русский 中文 Español Deutsch Português 한국어 Français Italiano Türkçe
MQL5入門: シンプルなExpert Advisorとカスタムインディケーターの書き方

MQL5入門: シンプルなExpert Advisorとカスタムインディケーターの書き方

MetaTrader 5 | 17 9月 2015, 16:52
34 475 0
Denis Zyatkevich
Denis Zyatkevich

はじめに

MetaTrader 5 クライアントターミナルのMetaQuotesプログラミング言語 5 (MQL5)は, MQL4に比べ新しい可能性を含み, 性能がより高いです。本記事はこの新しいプログラミング言語を紹介します。Expert Advisorとカスタムインディケーターの書き方のシンプルな例を本記事で紹介します。これらの例を理解するのに必要なMQL5言語の詳細についても考察します。

記事詳細とMQL5の詳細はMetaTrader 5に含まれるMQL5 レファレンスにあります。MQL5内蔵ヘルプに含まれる情報は, この言語を勉強するのに十分です。本記事は, MQL4を熟知しいる人にとっても, トレーディングシステムとインディケーターのプログラムを開始したばかりの人にとっても有益であると思います。


MQL5入門

MetaTrader 5のトレーディングプラットフォームによって, 金融商品(通貨ペア)のテクニカル分析を行い, 手動・自動の両モードで売買できます。 MetaTrader 5 は以前のMetaTrader 4と異なります。 特にディールのコンセプト, ポジション, オーダーが洗練されました。

  • Position - 市場での約定, 金融商品の現有建玉数。
  • Order - ある条件下で金融商品を売買するオーダー。
  • Deal - ブローカーによるオーダー実行の事実。それがポジションのオープニング, 修正, クロージングにつながる。

クライアントターミナルは, 異なった目的の様々なタイプのプログラムを書くことができる内蔵プログラミング言語 MQL5を持っています。

  • Expert Advisor - 指定アルゴリズムによってトレードをするプログラム。Expert Advisorで自動トレーディングのトレード システムを実行することができます。 (自動売買)。Expert Advisorはトレード操作, ポジションのオープンとクローズそしてペンディングオーダーを管理することができます。
  • Indicator - 現在のデータをチャートに表示。分析に便利。
  • Script - 一連の操作を一度に実行するプログラム。

Expert Advisor・インディケーター・スクリプトは, 操作システムライブラリを含めMQL5 標準ライブラリの関数とDLL関数を呼ぶかもしれません。 他のファイルにあるコードの部分はMQL5で書かれたプログラムのテキストに含まれます。

プログラム(Expert Advisor・インディケーター・スクリプト)を書くには , MetaTrader 5 クライアントターミナルを立ち上げ, Tools メニューからMetaQuotes 言語 Editor を選択するか, もしくは単にF4キーを押します。

図 1. MetaEditorの立ち上げ

図 1. MetaEditorの立ち上げ

MetaEditor 5 ウインドウでFile メニューから New を選ぶか Ctrl+Nを押します。

図 2. 新しいプログラムの作成

図 2. 新しいプログラムの作成

MQL5 ウィザード ウインドウで作成したいプログラムのタイプを選択します。

図 3. MQL5 ウィザード

図 3. MQL5 ウィザード

次のステップでは, プログラム立ち上げ後, ユーザーから要求されるプログラム名, 著者についての情報, パラメータを指定します。

図 4. Expert Advisorの全般プロパティ

図 4. Expert Advisorの全般プロパティ

その後, あなたのコードを編集し記入するプログラムテンプレート(Expert Advisor・インディケーター・スクリプト) が作成されます。:

図 5. 新しいプログラムのテンプレート

図 5. 新しいプログラムのテンプレート

プログラムの準備が整うと, それをコンパイルする必要があります。プログラムをコンパイルするには, File メニューからコンパイル を選択するか, F7 キーを押します。

図 6. プログラムのコンパイル

図 6. プログラムのコンパイル

プログラムにエラーがなければ, .ex5 拡張子付きのファイルが作成されます。その後, 実行のためにこの新しいExpert Advisor・インディケーター・スクリプトをMetaTrader 5 クライアントターミナルのチャートに添付できます。

MQL5プログラムは 演算子の一連です。それぞれの演算子はセミコロンシンボル 「;」で終わります。利便性のためにcommentをコードに追加できます。それはシンボル 「/*」 と 「*/」の間, または 「//」の後の行末までです。MQL5は「イベント指向」のプログラミング言語です。これは, イベント (プログラム立ち上げ, 終了, または新しい気配値の到来等)の発生時, クライアントターミナルが, ユーザーによって書かれた対応する 関数 (サブログラム)を立ち上げ, 指定操作が実行されることを意味します。クライアントターミナルは以下の 未定義イベントを持ちます。

  • Start イベントは, スクリプト実行時に起こります。(スクリプトでのみ使用)それはOnStart 関数の実行をもたらします。MQL4では, スクリプト内start 関数。
  • Init イベントは, Expert Advisorまたはインディケーター立ち上げ時に発生します。 それは OnInit 関数の実行をもたらします。MQL4 では init 関数。
  • DeinitイベントはExpert Advisorまたはインディケーターが終了した時に発生します。 (例えば, チャートから検知した後, クライアントターミナルを閉じる等)。 OnDeinit 関数の実行をもたらします。MQL4では deinit 関数。
  • NewTick イベントは現在の金融商品の新しい気配値(ティック)が到着した時発生します。 (Expert Advisorでのみ使用される)それは OnTick 関数の実行をもたらします。MQL4では Expert Advisor内のstart 関数。
  • Calculate イベントは, インディケーターの立ち上げ時(OnInit 関数実行後) と現在の金融商品の新しい気配値の到着時に発生します。 (インディケーターのみに使用) それはOnCalculate 関数の実行をもたらします。MQL4ではインディケーター内のstart 関数。
  • Trade イベントはオーダーの実行・修正・削除時, ポジションのオープン・修正・クローズ時に発生します。(Expert Advisorでのみ使用されます。)これはOnTrade 関数の実行をもたらします。MQL4には, 同等のイベントと関数がありません。
  • BookEvent イベントマーケットの深さが変わった時は発生します。(Expert Advisorでのみ使用される) OnBookEvent 関数の実行をもたらします。MQL4では, 同等のイベントと関数, そしてマーケットの深さがありません。
  • ChartEvent イベントはユーザーがチャートに取り組む時に発生します。: チャートウインドウがフォーカスにある時にマウスをクリックしキーを押します。それは, グラフィックオブジェクトの作成・移動・削除中等でも発生します。 (Expert Advisorとインディケーターで使用される)それはOnChartEvent 関数の実行をもたらします。MQL4には, 同等のイベントと関数がありません。
  • Timerイベントは, EventSetTimer 関数を使ってタイマーが起動した時 定期的に発生します。 それは, OnTimer 関数の実行をもたらします。MQL4には同等の イベントと関数さらにタイマーがありません。

変数を使う前に, データタイプを指定する必要があります。MQL5ではMQL4よりも多くのデータタイプに対応しています。

  • bool は論理値(trueまたはfalse)の保存を目的としています。1バイトのメモリを占めます。
  • char は-128から127までの整数 値 の保存を目的としています。1バイトのメモリを占めます。
  • uchar は0から255までの整数値の保存を目的としています。1バイトのメモリを占めます。
  • short は -32 768から32 767までの整数値の保存を目的としています。2 バイトのメモリを占めます。
  • ushort は0から65 535までの符号無し整数値の保存を目的としています。2 バイトのメモリを占めます。
  • int は -2 147 483 648 から 2 147 483 647までの整数値の保存を目的としています。4バイトのメモリを占めます。
  • uint は 0 から4 294 967 295までの符号無し整数値の保存を目的としています。4バイトのメモリを占めます。
  • long は-9 223 372 036 854 775 808 から 9 223 372 036 854 775 807までの整数値の保存を目的としています。8バイトのメモリを占めます。
  • ulong は 0 から18 446 744 073 709 551 615までの符号無し整数値の保存を目的としています。8バイトのメモリを占めます。
  • float は浮動小数点価値の保存を目的としています。4バイトのメモリを占めます。
  • double は浮動小数点価値の保存を目的としています。通常それは価格データの保存に使われます。8バイトのメモリを占めます。
  • datetime は日付と時間値, 01.01.1970 00:00:00からの秒経過数の保存を目的としています。8バイトのメモリを占めます。
  • color は色情報の保存を目的としています。 それは赤, 緑, 青の3つの色素の特徴を持ちます。4バイトのメモリを占めます。
  • enum はenumeration(列挙)のことです。これで制限的データ一式のタイプを指定できます。4バイトのメモリを占めます。
  • string は文字列の保存を目的としています。その内部表現は8バイト構造で, 文字列とポインター付きのバッファーのサイズを含みます。

パフォーマンスの最適化, メモリの合理的な使用には適切なデータタイプを選択する必要があります。MQL5では構造と呼ばれる新しいコンセプトがあります。関連データを論理的に組み合わせる構造です。


トレーディングシステム

本記事で例として使われるトレーディングシステムは, 欧州金融機関が朝オープンし, その後米国で経済的事象が公表され, それが EURUSDのトレンドをもたらすことを想定しています。チャート期間は重要ではありませんが, 一日 (もしくはその一部) が一目でわかり, 監視においてとても便利なため分バーを使うことを推奨します。

図 7. トレーディングシステム

図 7. トレーディングシステム

午前7時 (サーバータイム) Buy Stop と Sell Stop ペンディングオーダーが, 現在の日付の価格範囲外のポイントの距離で注文されます。Buy Stop ペンディングオーダーはスプレッドを考慮に入れます。StopLoss レベルが反対側の範囲に置かれます。実行後, 損益がプラスの時だけStopLoss オーダーが単純移動平均に移動されます。

このタイプのトレーリングは古典的なTrailing Stopよりも以下の有効性があります。価格の戻りが急騰した場合にポジションが早く刈られることを防ぐことができます。一方, トレンドが終了し動きが平行になるとポジション閉鎖になります。単純移動平均は1分足を使って計算されており, 期間は240です。

利確レベルは現在のマーケットボラティリティによります。マーケットボラティリティを確認するには, Average True Range (ATR) インディケーター が使われます。(日足で期間5) そのため, それは過去5日間の平均日次範囲を示します。ロングポジションのテイクプロフィットレベル値を確認するには , ATR インディケーター値を現在の日足の安値に加算します。ショートポジションはその逆です。ATR インディケーター値を現在の日足の高値から差し引きます。オーダー価格値がストップロスまたは テイクプロフィットレベルを超えないとオーダーが注文されません。午後7時(サーバータイム)後, すべてのペンディングオーダー が削除され, この日は注文されません。 (オープンポジションはクロージングまで継続します)


インディケーターの作成

上記のトレードシステムの利確レベルを示すインディケーターを書きましょう。

もしも行の最初のシンボルが「#」の場合, これはこの文字列が プリプロセッサ ディレクティブであることを意味します。ディレクティブは追加プログラム プロパティを指定し, 定数を宣言し, ヘッダーファイルとインポート関数含むために使用されます。ディレクティブの後, セミコロン (;) がないことに注目して下さい。

#property copyright   "2010, MetaQuotes Software Corp."
#property link        "http://www.mql5.com"
#property description "This indicator calculates TakeProfit levels"
#property description "using the average market volatility. It uses the values"
#property description "of Average True Range (ATR) indicator, calculated"
#property description "on daily price data. Indicator values are calculated"
#property description "using maximal and minimal price values per day."
#property version     "1.00"

著者についての情報とウェブサイトをcopyrightlink プロパティに指定できます。 description プロパティで概説を追加し, version プロパティでプログラムバージョンを指定できます。インディケーター実働時, この情報は以下のように見えます。:

図 8. インディケーター情報

図 8. インディケーター 情報

インディケータ表示位置を, メインチャートかサブウインドウかの指定をすることが必要です。indicator_chart_windowまたはindicator_separate_windowの内, 1つのプロパティを指定します。

#property indicator_chart_window

さらに, 使用されるインディケーターのバッファー数とグラフィックシリーズ数を指定する必要があります。我々のケースでは2つのラインがあり, それぞれ自身にバッファーがあります。- プロットされるデータ付きの配列 。

#property indicator_buffers 2
#property indicator_plots   2

それぞれのインディケーターラインに以下のプロパティを指定しましょう。 : 描画タイプ (indicator_type property), カラー (indicator_color property), ラインスタイル (indicator_style property) そしてインディケータ名ラベル (indicator_label property):

#property indicator_type1   DRAW_LINE
#property indicator_color1  C'127,191,127'
#property indicator_style1  STYLE_SOLID
#property indicator_label1  "Buy TP"
#property indicator_type2   DRAW_LINE
#property indicator_color2  C'191,127,127'
#property indicator_style2  STYLE_SOLID
#property indicator_label2  "Sell TP"

基本的描画タイプは: DRAW_LINE - ライン用, DRAW_SECTION - セクション用, DRAW_HISTORAM ヒストグラム用。他にも多くの描画タイプがあります。3つのRGB 構成要素の明るさの指定または 事前定義カラーの使用によって, 赤, 緑, 青, 白など色を定義できます。ラインスタイル: STYLE_SOLID - 実線, STYLE_DASH - 破線, STYLE_DOT - 点線, STYLE_DASHDOT - 鎖線, STYLE_DASHDOTDOT - 二点鎖線。

図 9. インディケーターラインの説明

図9. インディケーター ラインの説明

input モディファイアを使ってパラメータ(インディケーター立ち上げ後それらの値が指定可能), タイプ, デフォルト値を指定しましょう。

input int             ATRper       = 5;         //ATR Period
input ENUM_TIMEFRAMES ATRtimeframe = PERIOD_D1; //Indicator timeframe

パラメータの名前はコメントで指定でき, 変数名と違いそれらは明らかです。

図 10. インディケーターの入力パラメーター.

図10. インディケーターの 入力パラメータ

(全ての関数で閲覧できる)グローバルレベルで, インディケーターの関数で使われる変数 (とそのタイプ)を指定します。

double bu[],bd[];
int hATR;

bu[]bd[] 配列はインディケーターの上側と下側のラインで使用されます。使用されるエレメント数が正確にわからないので, 動的配列(つまり, 指定エレメント数のない配列)を使用します。 (それらのサイズは自動で割り振られる)。内蔵テクニカルインディケーターのハンドルは hATR 変数に保存されます。インディケーターハンドルはインディケーターを使用するために必要です。

関数 OnInit はインディケーターの実行の後呼ばれます。(チャートへの添付後)

void OnInit()
  {
   SetIndexBuffer(0,bu,INDICATOR_DATA);
   SetIndexBuffer(1,bd,INDICATOR_DATA);
   hATR=iATR(NULL,ATRtimeframe,ATRper);
  }

bu[]bd[]配列はインディケーター値を保存するのに使われ, インディケーターのバッファー でインディケーターのラインとしてプロットされるために関数 SetIndexBufferが必要です。最初のパラメータはインディケーターバッファーの0から始まるインデックスを定義します。第二のパラメータはインディケーターバッファーに割り当てられる配列を指定します。第三のパラメータはインディケーターバッファーに保存されるデータタイプを指定します。: INDICATOR_DATA - プロット用データ, INDICATOR_COLOR_INDEX - 作画カラー, INDICATOR_CALCULATIONS - 中間計算用の補助バッファー。

iATR 関数で戻されるインディケーターハンドルは hATR 変数に保存されています。 iATR 関数の最初のパラメータはトレード シンボル NULLです - 現チャートのシンボル。第二のパラメータはインディケーター 計算に使われるチャートタイムフレームを指定します。第三のパラメータはATR インディケーターの平均期間です。

OnCalculate 関数がOnInit 関数実行終わりの直後と現在シンボルの新しい気配値の到着ごとに呼ばれます。この関数を呼び出すには二つの方法がありますが, そのうちの一つで我々のインディケーターに使用する方は, 以下のようコードです。:

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime& time[],
                const double& open[],
                const double& high[],
                const double& low[],
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[])

OnCalculate 関数呼び出しの後, クライアントターミナルは以下のパラメータを渡します。: rates_total - 現チャートのバー数, prev_calculated - インディケーターによって既に計算されたバー数, time[], open[], high[], low[], close[], tick_volume[], volume[], spread[] - それぞれ各バーにおける時間, 始値・高値・安値・終値, ティックボリューム, ボリューム, スプレッド値を含む配列。計算時間を減らすため, 計算済みで変更のないインディケーター値を再度計算する必要はありません。 OnCalculate 関数呼び出しの後, 計算済みのバーの数を戻します。

OnCalculate 関数のコードは波括弧で囲みます。関数で使われるローカル変数, つまりタイプと名前で始まります。

{
   int i,day_n,day_t;
   double atr[],h_day,l_day;

i 変数はサイクルカウントとして使われ, day_nday_t 変数は日の番号の保存と, 一日の間の最大・最少価格値を計算する時に一時的に日の番号を保存します。atr[] 配列はATR インディケーター値を保存し, h_dayl_day 変数は一日の間の最大・最少価格値を保存するために使われます。

最初に, CopyBuffer 関数を使って, ATR インディケーター値をatr[] 配列にコピーしなければなりません。ATR インディケーターのハンドルをこの関数最初のパラメータとして使います。第二のパラメータはインディケーターバッファー数(0から番号が始まる)で, ATRインディケーターは1つのバッファーのみ持ちます。第三のパラメータは, 開始する最初のエレメントを指定します。インデックスは現在から過去に作成され, 0番目のエレメントは現在(未完了)バーに対応します。第四のパラメータはコピーされるべきエレメント数を指定します。

最後(完了)バーに対応する最後から2番目のエレメントに興味があるので, 2つのエレメントをコピーしましょう。最後のパラメータはデータをコピーするターゲット配列です。

CopyBuffer(hATR,0,0,2,atr);

配列インデックスの方向は AS_SERIES フラグによります。それがtrueに設定されると, 配列は時系列として考えられ, エレメントインデックスが最新データから最古データに作成されます。 falseまたはそれが設定されないと, 古いエレメントが小さいインデックス, 最新エレメントが大きいインデックスを持ちます。

ArraySetAsSeries(atr,true);

atr[]配列では, ArraySetAsSeries 関数を使ってAS_SERIES フラグに true を設定します。(この関数の最初のパラメータはフラグが変更される配列です。第二パラメータは新しいフラグ値。) 今, 現在の(未完了)インデックスは 0に等しく, 最後から2番目(完了済)のインデックスは1に等しいです。

for 演算子でループ作成が可能です。

for(i=prev_calculated;i<rates_total;i++)
     {
      day_t=time[i]/PeriodSeconds(ATRtimeframe);
      if(day_n<day_t)
        {
         day_n=day_t;
         h_day=high[i];
         l_day=low[i];
        }
        else
        {
         if(high[i]>h_day) h_day=high[i];
         if(low[i]<l_day) l_day=low[i];
        }
      bu[i]=l_day+atr[1];
      bd[i]=h_day-atr[1];
     }

for 演算子の後, 括弧内の最初の演算子は文(statement)です。: i=prev_calculated次に, 式(expression)があり, 我々のケースでは: i<rates_totalです。それはループ条件で, trueのときループが実行します。第三は各ループ実行後に実行する文です。我々のケースでは, それはi++です。 (i=i+1に等しく, i 変数が1つづ増えることを意味します。)

我々のループでは, i 変数が prev_calculated 値から始まり, 1ずつ加算され, rates_total-1 に等しくなるまで変化します。ヒストリカルデータ配列 (time[], high[]low[])はディフォルトで時系列ではなく, ゼロ番目インデックスはヒストリーの最古のバーにあたり, 最後は現在の未完了バーにあたります。ループでは最初の未計算 (prev_calculated)から最後のバー (rates_total-1) までのすべてのバーが処理されます。これらのバーそれぞれに関して, 我々のインディケーター値を計算します。

time[] 配列内の時間値は秒数として保存され, 01.01.1970 00:00:00からの経過秒数です。それを一日の秒数で割ると, 結果の整数部が01.01.1970から始まる日数になります。PeriodSeconds 関数は時間期間の秒数を戻し, パラメータとしてとして定義されます。day_t 変数は, i インデックス付きのバーに対応する日の番号です。day_n 変数は最大と最小価格値が計算される日の番号です。

if 演算子を考えましょう。もしこの演算子の括弧内の式がtrueの場合, if キーワードの以下の演算子が実行されます。もしも falseの場合, else キーワード以下の演算子が実行されます。各演算子は複合できます。つまり, いくつかの演算子を含むことができ, 我々のケースではそれらは波括弧で囲まれます。

処理された日の最大と最小価格値はh_dayl_day 変数にそれぞれ保存されます。我々のケースでは以下の条件を確認します。: もし分析するバーが新しい日である場合, 最大と最小価格値を再度計算し始めます。そうでなければ続けます。各インディケーターライン値を計算します。上部ラインにはその日の安値を使い, 下のラインには高値を使っています。

OnCalculate 関数の最後に, return 演算子が計算済みバー数を戻します。

return(rates_total);
  }

DeInit 関数 (インディケーターがチャートから削除された時, またはクライアントターミナルが終了した時に作動) 内, ATR インディケーターが貯えたメモリがIndicatorRelease 関数を使ってリリースされます。この関数は1つのパラメータ, インディケーターハンドルしか持ちません。

void OnDeinit(const int reason)
  {
   IndicatorRelease(hATR);
  }

これで我々のインディケーターが完了です。それをコンパイルするには, MetaEditorでFile メニューからコンパイルを選択または F7 キーを押します。コードにエラーがなければ, コンパイルが成功です。 コンパイル結果は Toolbox ウインドウの Errorsタブに表示されています。あなたのケースでは, コンパイラーが以下の文字列に関して, 「変換データ損失のおそれ」の警告を出力するかもしれません。:

day_t=time[i]/PeriodSeconds(ATRtimeframe);

このラインでは, 意図的に小数部分を捨てているのでこのデータ損失はエラーではありません。

インディケーターが完全でコンパイルされているとき, それをタトレーダー 5 クライアントターミナルのチャートに添付したり, 他のインディケーター・Expert Advisor・スクリプトで使用することができます。このインディケーターのソースコードは本記事に添付があります。


Expert Advisorの作成

さて, 上記のトレーディングシステムを実行する Expert Advisorを書くときが来ました。1つの金融商品(通貨ペア)のみをトレードすると想定します。いくつかのExpert Advisorが1つの金融商品で売買するためには , 全体ポジションでそれぞれの影響を注意深く分析することが必要ですが, それは本記事の範囲外です。

#property copyright   "2010, MetaQuotes Software Corp."
#property version     "1.00"
#property description "This Expert Advisor places the pending orders during the"
#property description "time from StartHour till EndHour on the price levels, that"
#property description "are 1 point below/lower the current trade range."
#property description "The StopLoss levels are placed at the opposite side"
#property description "of the price range. After order execution, the TakeProfit value"
#property description "is set at the 'indicator_TP' level. The StopLoss level is moved"
#property description "to the SMA values only for the profitable orders."

プリプロセッサ ディレクティブの目的はインディケーターを書くセクションで既に考察しました。Expert Advisorでも同じように働きます。

入力パラメーター値 (Expert Advisor開始後ユーザーによって定義される)のタイプとデフォルト値を指定しましょう。

input int    StartHour = 7;
input int    EndHour   = 19;
input int    MAper     = 240;
input double Lots      = 0.1;

StartHourEndHour パラメーターはペンディングオーダの時間期間 (開始と終了時間) を定義します。MAper パラメーターは, オープンポジションのトレーリングストップロスレベルに使用される単純移動平均の平均期間を定義します。Lots パラメーターは売買に使用される金融商品のボリュームを定義します。

異なったトレード関数で使用されるグローバル変数を指定しましょう。:

int hMA,hCI;

hMA 変数はMA インディケーターハンドルを保存するために使用され, hCI 変数はカスタムインディケーターハンドル (上記インディケーター)を保存するために使用されます。

OnInit 関数はExpert Advisor の立ち上げ時に実行されます。

void OnInit()
  {
   hMA=iMA(NULL,0,MAper,0,MODE_SMA,PRICE_CLOSE);
   hCI=iCustom(NULL,0,"indicator_TP");
  }

この関数では, MA インディケーター と我々のカスタムインディケーターのハンドルを入手します。iMA 関数とそのパラメータは上記のiATR 関数と同じように使用されます。

iCustom 関数の最初のパラメータは, シンボル名で, NULLで現チャートの金融商品を意味します。第二パラメータはchart timeframeで, 0で現チャートの期間を意味します。第三パラメータはインディケーターのファイル名前(拡張子なし)です。ファイルパスはMQL5\Indicators\フォルダに関連付けられます。

新しい気配値の到着後に実行するOnTick 関数を作りましょう。 :

void OnTick()
  {

この関数のコードは波括弧で囲まれます。

Expert Advisorで使用される未定義の データ 構造を指定しましょう。 :

MqlTradeRequest request;
   MqlTradeResult result;
   MqlDateTime dt;

MqlTradeRequest 事前定義構造は, トレード操作内のOrderSend 関数に渡されるオーダーとポジションパラメータを持ちます。 MqlTradeResult構造の目的は OrderSend 関数で戻されるトレード操作の結果情報を保存することです。MqlDateTime 事前定義構造の目的はデータと時間情報の保存です。

OnTick 関数で使用されるローカル変数 (とそのタイプ)を指定しましょう。:

bool bord=false, sord=false;
   int i;
   ulong ticket;
   datetime t[];
   double h[], l[], ma[], atr_h[], atr_l[],
          lev_h, lev_l, StopLoss,
          StopLevel=_Point*SymbolInfoInteger(Symbol(),SYMBOL_TRADE_STOPS_LEVEL),
          Spread   =NormalizeDouble(SymbolInfoDouble(Symbol(),SYMBOL_ASK) - SymbolInfoDouble(Symbol(),SYMBOL_BID),_Digits);

bordsord ブール変数はフラグとして, Buy StopとSell Stopペンディングオーダーの存在を示します。存在の場合, 対応する変数は trueに等しい値を持ち, そうでない場合falseです。i 変数はループ 演算子でカウンターとして使われ, 中間データ保存に使用されます。ペンディングオーダーのticketはticket 変数に保存されます。

t[], h[]l[] 配列はヒストリー データの各バーの時間, 最大・最小値を保存するのに使われます。ma[] 配列はMAインディケーター値を保存するのに使用されます atr_h[]atr_l[] 配列は我々が作成したインディケーター_TP カスタムインディケーターの上下ラインの値を保存するのに使われます。

lev_hlev_l変数は現在の日の高値安値(ペンディングオーダー価格)を保存するのに使われます。 StopLoss 変数はオープンポジションストップロス値を一時的に保存するのに使用されます。

StopLevel 変数はSTOP_LEVEL値(現在の価格とペンディングオーダー価格の最小距離)を保存するのに使用されます。ポイントで定義されるSTOP_LEVEL 変数値に, 最小桁数の1ユニット(_Point 事前定義変数)を掛けて, 価格の幅を計算する。STOP_LEVEL値はSymbolInfoInterger 関数によって戻されます。この関数の最初のパラメータはシンボル名で, 第二は 要求されたパラメーターの識別子です。シンボル (金融商品の名前) は シンボル 関数(パラメータなし)を使って入手できます。

Spread 値はスプレッド値を保存するのに使用されます。その値は, 正規化された現在の買値と売値間の相違として NormalizeDouble 関数を使って計算されます。この関数の最初のパラメータは正規化するダブルタイプ値で, 第二は_Digits 事前定義変数から入手したポイント後の桁数です。現在の買値と売値は SymbolInfoDouble 関数を使って入手できます。 この関数 の最初のパラメータはシンボル名で, 第二はプロパティ識別子です。

構造 requestを, 多くのOrderSend 関数呼び出しに共通である値と共に埋めましょう。:

request.symbol      =Symbol();
   request.volume      =Lots;
   request.tp          =0;
   request.deviation   =0;
   request.type_filling=ORDER_FILLING_FOK;

request.symbol エレメントは売買する金融商品のシンボル名を含みます。request.volume エレメント- 金融商品のボリューム (ロットサイズ), request.tp - TakeProfit数値 (場合によってはそれを使わず0で記入), request.deviation - トレード操作実行時の価格スリッページを考慮 , request.type_fillingはオーダー執行タイプです。 - そのオーダータイプは以下。:

  • ORDER_FILLING_FOK - ディールはボリュームが指定オーダーロットに同等またはそれ以上である時だけ実行されます。十分なボリュームがないと, オーダーは実行されません。
  • ORDER_FILLING_IOC - 十分なボリュームがなく, オーダーが最大可能マーケットボリュームで売買が成立します。
  • ORDER_FILLING_RETURN - ORDER_FILING_IOCと同じだが この場合, 欠けているボリューム の追加オーダーが注文されます。

TimeCurrent 関数を使って現在のサーバータイム (最後の気配値の時間)を入手しましょう。この関数の唯一のパラメータが結果付の構造へのポインターです。

TimeCurrent(dt);

すべての我々の計算に現在の日のみのヒストリー価格データが必要です。バー数はこの式を使って計算できます。 : i = (dt.hour + 1)*60, ここで dt.hour - は現在時刻を含む構造エレメント。時間値, 高値と安値はt[], h[]l[] 配列に CopyTime, CopyHigh, CopyLow 関数を使ってそれぞれコピーされます。 :

i=(dt.hour+1)*60;
   if(CopyTime(Symbol(),0,0,i,t)<i || CopyHigh(Symbol(),0,0,i,h)<i || CopyLow(Symbol(),0,0,i,l)<i)
     {
      Print("Can't copy timeseries!");
      return;
     }

CopyTime, CopyHighCopyLow 関数内の最初のパラメータはシンボル名です。 第二はchart timeframe, 第三はコピーする始めのエレメント, 第四はコピーするエレメント数, 第五はデータのターゲット配列です。これらの各関数はコピーされたエレメント数またはエラーの場合-1を戻します。

if 演算子は, 3つの配列すべてのコピーされたエレメント数を確認するのに使用されます。コピーされたエレメント数が計算に必要な量よりも小さい (配列の1つでさえも), またはエラーのケースの場合, 「時系列をコピーできません!」 メッセージがExperts logに出力され, return 演算子を使って OnTick 関数の実行を終了します。メッセージはPrint 関数によって出力されます。カンマで区切られたどんなタイプのデータも印刷できます。

価格データがt[], h[]l[] 配列にコピーされた時, 上記でも使った ArraySetAsSeries 関数を使って, AS_SERIES フラグを trueに設定します。時系列(現在の価格から最古の価格まで)として配列インデックス作成を設定することが必要です。 :

ArraySetAsSeries(t,true);
   ArraySetAsSeries(h,true);
   ArraySetAsSeries(l,true);

現在の日の高値と安値が lev_hlev_l 変数の中に置かれます。:

lev_h=h[0];
   lev_l=l[0];
   for(i=1;i<ArraySize(t) && MathFloor(t[i]/86400)==MathFloor(t[0]/86400);i++)
     {
      if(h[i]>lev_h) lev_h=h[i];
      if(l[i]<lev_l) lev_l=l[i];
     }

ループは, MathFloor(t[i]/86400) == MathFloor(t[0]/86400) 条件が true である時だけ実行され, 現在の日に属するバーでの検索を制限します。この式の左側には現在のバーの日の数, 右側には現在の日の数 (86400は一日の秒数)があります。MathFloor 関数は 数値を切り捨てます。つまり, 正の値の整数部のみ使います。この関数のたった一つのパラメータは切り捨てする 式 です。MQL4と同様MQL5で, 式は "==" シンボル と共に定義されます。(Operations of relations参照)

オーダー価格が以下のように計算されます。: Buy Stop タイプのペンディングオーダーには, 1つのポイント(_Point 事前定義変数は価格ユニット内のポイントサイズに等しい ) と Spreadlev_h 変数 (lev_h+=Spread+_Pointまたはlev_h=lev_h+Spread+_Point)に追加します。Sell Stop タイプのペンディングオーダー では, 1つの ポイントをlev_l 変数値 (lev_l-=_Pointまたはlev_l=lev_l-_Point)から差し引きます。

lev_h+=Spread+_Point;
   lev_l-=_Point;

次に, 値をインディケーターバッファーから配列へCopyBuffer 関数を使ってコピーします。MA値は ma[] 配列へコピーされ, 我々のカスタムインディケーター の上側のライン値はatr_h[] 配列にコピーされ, インディケーター の下側のライン値はatr_l[] 配列にコピーされます。CopyBuffer関数は, インディケーター詳細を考察した時に上記で説明しました。

if(CopyBuffer(hMA,0,0,2,ma)<2 || CopyBuffer(hCI,0,0,1,atr_h)<1 || CopyBuffer(hCI,1,0,1,atr_l)<1)
     {
      Print("Can't copy indicator buffer!");
      return;
     }

結びの (最後に完了された) バーに対応するMA インディケーター 値と最後のバーに対応する我々のインディケーター値が必要なので, これらの2つのエレメントを ma[] 配列にコピーし, 1つの エレメントを atr_h[]atr_l[] 配列にコピーします。コピー中のエラーまたはコピーされた値の数が必要数よりも少ない場合, (これらの配列すべてにおいて), メッセージがExperts logに出力され, OnTick 関数がreturn 演算子を使って終了します。

ma[] 配列では, 配列の時系列インデックス作成を示すAS_SERIES フラグを設定します。

ArraySetAsSeries(ma,true);

atr_h[]atr_l[] 配列は1つのエレメントしか持たないので, 時系列インデックス作成が重要でありません。atr_l[0]値がさらにTakeProfit レベルを決めるのに使用されるので, ショートポジションは買値でクローズしますが, 価格チャートでは売値が使われるのでスプレッドをatr_l[0]値に追加します。

atr_l[0]+=Spread;

PositionsTotal 関数はオープンポジション数(パラメータなし)を戻します。ポジションのインデックスは0から始まります。すべてのオープンポジションを探すループを作りましょう。:

// in this loop we're checking all opened positions
   for(i=0;i<PositionsTotal();i++)
     {
      // processing orders with "our" symbols only
      if(Symbol()==PositionGetSymbol(i))
        {
         // we will change the values of StopLoss and TakeProfit
         request.action=TRADE_ACTION_SLTP;
         // long positions processing
         if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
           {
            // let's determine StopLoss
            if(ma[1]>PositionGetDouble(POSITION_PRICE_OPEN)) StopLoss=ma[1]; else StopLoss=lev_l;
            // if StopLoss is not defined or lower than needed            
            if((PositionGetDouble(POSITION_SL)==0 || NormalizeDouble(StopLoss-PositionGetDouble(POSITION_SL),_Digits)>0
               // if TakeProfit is not defined or higer than needed
               || PositionGetDouble(POSITION_TP)==0 || NormalizeDouble(PositionGetDouble(POSITION_TP)-atr_h[0],_Digits)>0)
               // is new StopLoss close to the current price?
               && NormalizeDouble(SymbolInfoDouble(Symbol(),SYMBOL_BID)-StopLoss-StopLevel,_Digits)>0
               // is new TakeProfit close to the current price?
               && NormalizeDouble(atr_h[0]-SymbolInfoDouble(Symbol(),SYMBOL_BID)-StopLevel,_Digits)>0)
              {
               // putting new value of StopLoss to the structure
               request.sl=NormalizeDouble(StopLoss,_Digits);
               // putting new value of TakeProfit to the structure
               request.tp=NormalizeDouble(atr_h[0],_Digits);
               // sending request to trade server
               OrderSend(request,result);
              }
           }
         // short positions processing
         if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
           {
            // let's determine the value of StopLoss
            if(ma[1]+Spread<PositionGetDouble(POSITION_PRICE_OPEN)) StopLoss=ma[1]+Spread; else StopLoss=lev_h;
            // if StopLoss is not defined or higher than needed
            if((PositionGetDouble(POSITION_SL)==0 || NormalizeDouble(PositionGetDouble(POSITION_SL)-StopLoss,_Digits)>0
               // if TakeProfit is not defined or lower than needed
               || PositionGetDouble(POSITION_TP)==0 || NormalizeDouble(atr_l[0]-PositionGetDouble(POSITION_TP),_Digits)>0)
               // is new StopLoss close to the current price?
               && NormalizeDouble(StopLoss-SymbolInfoDouble(Symbol(),SYMBOL_ASK)-StopLevel,_Digits)>0
               // is new TakeProfit close to the current price?
               && NormalizeDouble(SymbolInfoDouble(Symbol(),SYMBOL_ASK)-atr_l[0]-StopLevel,_Digits)>0)
              {
               // putting new value of StopLoss to the structure
               request.sl=NormalizeDouble(StopLoss,_Digits);
               // putting new value of TakeProfit to the structure
               request.tp=NormalizeDouble(atr_l[0],_Digits);
               // sending request to trade server
               OrderSend(request,result);
              }
           }
         // if there is an opened position, return from here...
         return;
        }
     }

ループ内のif 演算子を使って, 現チャートのシンボルでオープンしたポジションを選択します。PositionGetSymbol 関数は金融商品のシンボルを戻し, その上, 自動で同時に扱うポジションを選択します。この関数は1つのパラメーターのみ持ちます。 オープンポジションリスト内のポジションのインデックスオープンポジションのストップロスとテイクプロフィット値を変える必要がある可能性があるので, TRADE_ACTION_SLTP 値をrequest.action エレメントに置きましょう。次に, 方向にしたがって, ポジションがロング, ショートに分かれます。

ロングポジションではストップロスレベルが以下のように決まります。: もし MA インディケーター 値がポジションの始値より高い場合, ストップロス値がMA インディケーター 値に等しいと想定され, そうでない場合 ストップロス値は lev_l 変数値に等しいと想定されます。オープンポジションのストップロス現在値は, 1つのパラメータ(ポジションプロパティの識別子)しかもたない PositionGetDouble 関数を使って取得します。 ストップロス値が設定されていない(0に等しい)または高すぎの場合, このポジションのストップロスとテイクプロフィットの値を修正します。テイクプロフィットが設定されていない(0に等しい )または高すぎ(我々の インディケーターの上側のラインよりも高い)場合, このポジションのストップロスとテイクプロフィットの値を修正します。

ストップロス とテイクプロフィット値の変更の可能性を確認しなければなりません。ストップロスの新しい値は現在のBid 価格マイナスSTOP_LEVEL 値より低くあるべきです。テイクプロフィットの新しい値は現在のBid 価格よりも少なくとも STOP_LEVEL 値分大きいはずです。比較値はダブルタイプの浮動小数点2進数の10進数への変換による不正確性のため最後の桁が異なるかもしれないため, 正規化相違を比較に使いました。

オープンポジションのストップロスまたはテイクプロフィットレベルを変更する必要があり, 新しい値がトレーディングルールに応じて有効な場合, ストップロスとテイクプロフィットの新しい値を対応する構造のエレメントに置き, トレードサーバーにデータを送るOrderSend 関数を呼び出します。

ショートポジションのストップロスとテイクプロフィット値の変更は同じです。ロングに比べショートポジションは買値によってクローズされるので買値が比較に使用されます。現チャートでオープンポジションがある場合, return 演算子を使ってOnTick 関数の実行を終了します。

OrdersTotal 関数 (パラメータなし) はペンディングオーダー数を戻します。0から始まるインデックスすべてのペンディングオーダーを処理するのに使用されるループを作りましょう。

// in this loop we're checking all pending orders
   for(i=0;i<OrdersTotal();i++)
     {
      // choosing each order and getting its ticket
      ticket=OrderGetTicket(i);
      // processing orders with "our" symbols only
      if(OrderGetString(ORDER_SYMBOL)==Symbol())
        {
         // processing Buy Stop orders
         if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP)
           {
            // check if there is trading time and price movement is possible
            if(dt.hour>=StartHour && dt.hour<EndHour && lev_h<atr_h[0])
              {
               // if the opening price is lower than needed
               if((NormalizeDouble(lev_h-OrderGetDouble(ORDER_PRICE_OPEN),_Digits)>0
                  // if StopLoss is not defined or higher than needed
                  || OrderGetDouble(ORDER_SL)==0 || NormalizeDouble(OrderGetDouble(ORDER_SL)-lev_l,_Digits)!=0)
                  // is opening price close to the current price?
                  && NormalizeDouble(lev_h-SymbolInfoDouble(Symbol(),SYMBOL_ASK)-StopLevel,_Digits)>0)
                 {
                  // pending order parameters will be changed
                  request.action=TRADE_ACTION_MODIFY;
                  // putting the ticket number to the structure
                  request.order=ticket;
                  // putting the new value of opening price to the structure
                  request.price=NormalizeDouble(lev_h,_Digits);
                  // putting new value of StopLoss to the structure
                  request.sl=NormalizeDouble(lev_l,_Digits);
                  // sending request to trade server
                  OrderSend(request,result);
                  // exiting from the OnTick() function
                  return;
                 }
              }
            // if there is no trading time or the average trade range has been passed
            else
              {
               // we will delete this pending order
               request.action=TRADE_ACTION_REMOVE;
               // putting the ticket number to the structure
               request.order=ticket;
               // sending request to trade server
               OrderSend(request,result);
               // exiting from the OnTick() function
               return;
              }
            // setting the flag, that indicates the presence of Buy Stop order
            bord=true;
           }
         // processing Sell Stop orders
         if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP)
           {
            // check if there is trading time and price movement is possible
            if(dt.hour>=StartHour && dt.hour<EndHour && lev_l>atr_l[0])
              {
               // if the opening price is higher than needed
               if((NormalizeDouble(OrderGetDouble(ORDER_PRICE_OPEN)-lev_l,_Digits)>0
                  // if StopLoss is not defined or lower than need
                  || OrderGetDouble(ORDER_SL)==0 || NormalizeDouble(lev_h-OrderGetDouble(ORDER_SL),_Digits)>0)
                  // is opening price close to the current price?
                  && NormalizeDouble(SymbolInfoDouble(Symbol(),SYMBOL_BID)-lev_l-StopLevel,_Digits)>0)
                 {
                  // pending order parameters will be changed
                  request.action=TRADE_ACTION_MODIFY;
                  // putting ticket of modified order to the structure
                  request.order=ticket;
                  // putting new value of the opening price to the structure
                  request.price=NormalizeDouble(lev_l,_Digits);
                  // putting new value of StopLoss to the structure
                  request.sl=NormalizeDouble(lev_h,_Digits);
                  // sending request to trade server
                  OrderSend(request,result);
                  // exiting from the OnTick() function
                  return;
                 }
              }
            // if there is no trading time or the average trade range has been passedе
            else
              {
               // we will delete this pending order
               request.action=TRADE_ACTION_REMOVE;
               // putting the ticket number to the structure
               request.order=ticket;
               // sending request to trade server
               OrderSend(request,result);
               // exiting from the OnTick() function
               return;
              }
            // setting the flag, that indicates the presence of Sell Stop order
            sord=true;
           }
        }
     }

OrderGetTicket 関数を使って, ticket 変数にオーダーチケットを保存し, さらにオーダーを今後使用するため選択します。この関数は1つの パラメータ(オープンオーダーリスト内のオーダーのインデックス)しか持ちません。シンボルの名前を入手するために OrderGetString 関数が使用されます。それは1つの パラメータ(オーダープロパティ識別子)しか持ちません。シンボル名を現チャート名と比較します。 そのことでExpert Advisorが作動している金融商品だけのオーダーを選択することができます。対応する オーダータイプ 識別子とOrderGetInteger 関数によってオーダータイプが決まります。 Buy StopとSell Stop オーダーを別々に処理します。

もし現在時刻がStartHourからEndHourまでの範囲内で, Buy Stopオーダーの注文価格がインディケーターの上側のラインを超えない場合, 注文価格とストップロスレベル値を修正し(必要な場合), そうでなければオーダーを削除します。

次に, ペンディングオーダーの注文価格またはストップロスレベルを修正することが必要か決めます。Buy Stopオーダーの注文価格が低すぎるか, ストップロスが未定義または高すぎの場合, TRADE_ACTION_MODIFY 値をrequest.action エレメントに置きます。 - それはペンディングオーダー パラメータが変更されるべきことを意味しています。オーダーチケットをrequest.ticket エレメントに置き, 売買リクエストをOrderSend 関数を使ってトレード サーバーに送ります。スプレッド値が異なるかもしれないので, 比較では注文価格が低すぎる場合を決めますが, 各スプレッドの変更後オーダーを変更しません。最大スプレッド値に対応する最大レベルに設定されます。

また比較で, ストップロス値が高すぎのケースのみを決めます。なぜなら価格幅が一日の間で拡張し, オーダー 価格の新しい lowの後ストップロス値を下に動かす必要があるからです。トレード サーバーにリクエスト送信後, return 演算子を使ってOnTick 関数実行が決められます。Buy Stop オーダーが存在する場合, bord 変数値はtrueに設定されます。

Sell Stop オーダーがBuy Stop オーダーと同じように処理されます。

それらが不在の場合, Buy StopとSell Stop ペンディングオーダーを注文しましょう。TRADE_ACTION_PENDING 値をrequest.action エレメントに置きます。 (それはペンディングオーダーが出された事を意味します。)

request.action=TRADE_ACTION_PENDING;

もし現在時刻値がオーダー注文時間内の場合, オーダーを出します。 :

if(dt.hour>=StartHour && dt.hour<EndHour)
     {
      if(bord==false && lev_h<atr_h[0])
        {
         request.price=NormalizeDouble(lev_h,_Digits);
         request.sl=NormalizeDouble(lev_l,_Digits);
         request.type=ORDER_TYPE_BUY_STOP;
         OrderSend(request,result);
        }
      if(sord==false && lev_l>atr_l[0])
        {
         request.price=NormalizeDouble(lev_l,_Digits);
         request.sl=NormalizeDouble(lev_h,_Digits);
         request.type=ORDER_TYPE_SELL_STOP;
         OrderSend(request,result);
        }
     }
  }

Buy StopとSell Stop オーダーを出している間, bordsord 変数値を分析して同じオーダーの存在を確認します。また以下の状況を確認します。 オーダー価格はインディケーター値の範囲内であるべきです。正規化オーダー価格はrequest.priceエレメント内に置かれ, 正規化StopLoss値がrequest.sl 変数内に置かれ, オーダータイプ (ORDER_BUY_STOPまたはORDER_SELL_STOP)がrequest.type 変数内に置かれます。その後, トレード サーバーにリクエストを送ります。OnTick 関数のコードはセミコロンで終わります。

インディケーターで割り当てられたリソースは, 上記で考察されたインディケーターRelease 関数を使ってOnDeinit 関数内にリリースされます。

void OnDeinit(const int reason)
  {
   IndicatorRelease(hCI);
   IndicatorRelease(hMA);
  }

Expert Advisor が完了で, エラーがなければコンパイルが成功です。これでチャートに添付して実行できます。ソースコードは本記事の添付でダウンロードできます。


立ち上げとデバッグ

Expert Advisorとインディケーターが使用可能状態である時, 内蔵 MetaEditorデバッガーを使ったそれらの立ち上げ方, デバッグの仕方を考えましょう。

Expert Advisor立ち上げには, Navigator ウインドウのExpert Advisor グループでそれを見つける必要があります。その後, context メニューから右クリックすると表示されるAttach to chart を選択します。:

図 11. Expert Advisorの立ち上げ

図 11. Expert Advisorの立ち上げ

Expert Advisorの入力パラメーターウインドウが現れ, 必要な場合, これらのパラメータを変更できます。OKを押した後, アイコン がチャートの右上に現れ, Expert Advisorが作動していることを示します。Navigator ウインドウの表示・非表示には View メニューからNavigator を選択するか, Ctrl+Nを押します。Expert Advisorの第二の立ち上げ方法は , Insert メニューのExperts サブメニュー でそれを選択します。

Expert Advisorが売買できるようするには, AutoTradingがクライアントターミナルオプションで許可されていなければなりません。: Tools メニュー -> オプション ウインドウ -> Exprert Advisors タブ -> AutoTrading許可 オプションを有効にするべきです。 Expert AdvisorがDLLから関数を呼び出せるようにするにはAllow DLL imports オプションも有効にするべきです。

図 12. ターミナルオプション - AutoTradingの許可

図 12. ターミナルオプション - AutoTradingの許可

さらに, 個々のExpert Advisorの売買の承認または禁止, そして外部DLLライブラリのインポートは対応するオプションを確認することによって設定できます。

我々のExpert Advisorがインディケーターを使うという事実にかかわらず, インディケーターのラインはチャートにプロットされません。必要な場合, インディケーター を手動で添付できます。

インディケーターの立ち上げはExpert Advisorと同じです。: 内蔵インディケーターを立ち上げたい場合, Navigator ウインドウでインディケーター treeを拡大し (カスタムインディケーターでは, Custom インディケーター treeの拡大が必要。 ), 右クリックしてポップアップメニューを表示し, context メニューからAttach to Chart を選択します。 第二の方法は Insert メニューからインディケーターを選択, グループ (カスタムインディケーターはCustomを選択) とインディケーター自身を選択します。

スクリプトはExpert Advisor とインディケーターと同じように立ち上げられます。

クライアントターミナル イベントについての情報 (トレード サーバーへの接続/断絶, 自動更新, ポジションとオーダー 変更, Expert Advisorとスクリプト実行, エラーメッセージ) はToolbox ウインドウの Journal タブにあります。Expert Advisor, インディケーター, スクリプトによって出力されたメッセージは, Expertsタブにあります。

MetaEditorは内蔵 デバッガーがあります。プログラムをデバッグできます。 - Expert Advisor, インディケーター , スクリプトのスッテプバイスッテプの実行。デバッグはプログラムコード内のエラーを探す手助けをし, Expert Advisor・インディケーター・スクリプト実行時処理を見守ります。デバッグモードでプログラムを実行するには, デバッグメニューから Start を選択するかF5 キーを押す必要があります。プログラムがコンパイルされ, 別のチャートにてデバッグモードで実行します。その期間とシンボルはMetaEditorのオプション ウインドウのデバッグ タブで指定できます。

図 13. Editor オプション - デバッグ.

図 13. Editor オプション - デバッグ.

ブレークポイントの設定は F9 キーを押すか, ラインのサイドをダブルクリックするか, デバッグ ウインドウから Toggle ブレークポイントを選ぶことでできます。デバッグモードでは, プログラムの実行はブレークポイントの演算子の前で停止します。 停止後, デバッグ タブがToolbox ウインドウに現れます。 (図14) 左側にcalls stack パネルがあり, ファイル, 関数 , ライン数がそこに表示されます。右側にはウォッチパネルがあり, 監視されている変数値がそこに表示されます。変数をウォッチリストに追加するには, パネルで右クリックし, Addを選択するかInsert キーを押します。

図 14. プログラムデバッグ

図 14. プログラムデバッグ

ステップバイスッテプのプログラム実行はF11, F10またはShift+F11 キーを押すことで可能です。F11 キーを押すかデバッグ メニューからStep Into を選択後, プログラム実行の1つのステップを渡し, すべての呼び出し関数を入力します。F10 キーを押すか デバッグ メニューから Step Over を選択後, すべての呼び出し関数を入力せずにプログラム実行の1つのステップを渡します。Shift+F11 を押すか デバッグ メニューからStep Out を選択後, 1つレベルが上のプログラムステップを実行します。コードの左側の緑の矢印が実行されるコードのラインをマークします。


結論

本記事では, シンプルなExpert Advisorとインディケーターの書き方例を紹介し, MQL5 プログラミング言語の基本を説明しました。 ここで紹介されたトレーディングシステムは例として取り上げられ, リアルトレードでの使用において著者は責任を負いません。

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

添付されたファイル |
indicator_tp.mq5 (2.17 KB)
expert.mq5 (11.3 KB)
の処理 トレードイベント in Expert Advisor を使って OnTrade() 関数 の処理 トレードイベント in Expert Advisor を使って OnTrade() 関数
MQL5は様々なタイプのイベント (タイマーイベント、トレードイベント、カスタムイベントなど)を含め、非常に多くの革新をもたらしました。イベントを取り扱う性能で全く新しいタイプの自動・準自動の売買プログラムを作成できます。本記事ではトレードイベントを考え、トレードイベントを処理するOnTrade() 関数のコードを書きます。
初心者のためのMQL5: Expert Advisorでのテクニカルインディケーター使用ガイド 初心者のためのMQL5: Expert Advisorでのテクニカルインディケーター使用ガイド
Expert Advisorで内蔵またはカスタムインディケーターの値を入手するには、まずそのハンドラーを対応する関数を使って作成しなければなりません。本記事の例は、自分のプログラム作成時にどのようにテクニカルインディケーターを使うか説明します。本記事はMQL5 言語の内蔵のインディケーターを説明します。これはトレーディングストラテジー策定の経験が浅い人向けで、関数のライブラリを使ったシンプルで分りやすいインディケーターの使用法を紹介します。
MQL5でのインディケーター の呼び方 MQL5でのインディケーター の呼び方
MQLプログラミング言語の新バージョンでは、 インディケーター扱いアプローチが変化しただけでなく、インディケーター作成の新しい方法があります。さらに、 インディケーターバッファーの取り組みがより柔軟になり、今では希望のインデックス方向を指定し、好きなだけインディケーター値を入手できます。本記事ではインディケーター呼び出しとインディケーターバッファーからのデータ検索の基本方法を 説明します。
MQL5でのオブジェクト作成と削除の順番 MQL5でのオブジェクト作成と削除の順番
MQL5プログラムでは、すべてのオブジェクトはカスタムオブジェクトだろうと、動的配列またはオブジェクト配列は特定の方法で作成され削除されます。 しばしば オブジェクトは他のオブジェクトの一部で、非初期化のオブジェクト削除の順は特に重要になります。本記事ではオブジェクトを使用するメカニズムの例を紹介します。