English Русский 中文 Español Deutsch Português 한국어 Français Italiano Türkçe
初心者のためのMQL5のカスタムインディケーター

初心者のためのMQL5のカスタムインディケーター

MetaTrader 5 | 7 10月 2015, 16:05
4 823 0
Nikolay Kositsin
Nikolay Kositsin

はじめに

いかなる知的題材 (数学、音楽またはプログラミングだろうと) を深く理解する基本はその根本を勉強することにあります。かなり若い時に同じような勉強が始まっていると、基本の理解がより簡単で知識が具体的かつ包括的でいいです。

残念ながら、多くの人は金融市場や株式市場を中年から勉強し始めますので、勉強が簡単ではありません。本記事では、MQL5を理解し、MetaTrader 5 クライアントターミナルでカスタムインディケーターを書く際に立ちふさがる初期難関を乗り越える手助けをします。


シンプルな例としてのSMAインディケーター

何かを学ぶ最も効果的で合理的な方法は実用的問題の解決策です。カスタムインディケーターを考えるので、 MQL5でのインディケーター操作の基本を示したコードを含むシンプルインディケーターの勉強から始めます。

例として、最も有名なテクニカル分析のインディケーター 、単純移動平均 (SMA)を考えましょう。その計算はシンプルです。:

SMA = SUM (CLOSE (i), MAPeriod) / MAPeriod

ここで:

  • SUM — 値の合計;
  • CLOSE (i) — i番目のバーの終値;
  • MAPeriod — 平均するバー数(平均期間)

以下がこのインディケーターの理解しやすいコードです。:

//+------------------------------------------------------------------+
//|                                                          SMA.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
#property indicator_type1   DRAW_LINE
#property indicator_color1  Red
  
input int MAPeriod = 13;
input int MAShift = 0; 
  
double ExtLineBuffer[]; 
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+  
void OnInit()
  {
   SetIndexBuffer(0, ExtLineBuffer, INDICATOR_DATA);
   PlotIndexSetInteger(0, PLOT_SHIFT, MAShift);
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, MAPeriod - 1);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
  {
   if (rates_total < MAPeriod - 1)
    return(0);
    
   int first, bar, iii;
   double Sum, SMA;
   
   if (prev_calculated == 0)
    first = MAPeriod - 1 + begin;
   else first = prev_calculated - 1;

   for(bar = first; bar < rates_total; bar++)
    {
     Sum = 0.0;
     for(iii = 0; iii < MAPeriod; iii++)
      Sum += price[bar - iii];
     
     SMA = Sum / MAPeriod;
      
     ExtLineBuffer[bar] = SMA;
    }
     
   return(rates_total);
  }
//+------------------------------------------------------------------+

そしてこれがMetaTrader 5 クライアントターミナルでの動作の結果です。:

まずコードの各文字列の目的と、プログラムコードとクライアントターミナルの相互作用の2つの事を考えることが必要です。


コメントの使用

インディケーターコードを一見すると、これらのようなオブジェクトに目が行きます。:

//+------------------------------------------------------------------+
//|                                                          SMA.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+  
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+

それらがコードと直接関連していなく、単なるコメントで、コード可読性のためにデザインされており、 このコードのある部分の意味内容を示していることに注目することが必要です。 もちろん更なる単純化のためコードから損害なしに削除することが可能ですが、それではコードを理解する上での簡潔さを失います。我々のケースでは、 常に「//」文字から始まり、改行文字で終わるsingle-line コメントを取り扱います。

著者がコメントに後でこのコードを理解する上で助けになる必要事項をすべてを書けることは明瞭です。我々のケースでは、コメント文字列の最初の部分にインディケーターの名前と著者についての情報があり、第二と第三部分のコメントではOnInit()とOnCalculate()関数を分けています。最後の最終ラインは単にプログラムコードを閉じています。


SMAコードの構造

ご覧のとおり、インディケーターの全体コードは3部分に分かれます。:

1. 括弧なしで グローバルレベルで書かれるコード。最初の2つのコメントの間にあります。
2. OnInit() 関数の説明

3. OnCalculate() 関数の説明

プログラミングでは 関数 の意味が数学よりも幅広いことに注意する必要があります。例えば、プログラミング言語では 数学関数は常にある入力パラメーターを受け取り計算値を戻します。 さらに、MQL5では関数によって、チャート操作、 トレード操作、ファイル操作などを実行できます。

実際、MQL5で書かれた全てのインディケーターは必ずユーザーが書いた部分の一式があります。その内容は個々で作成されたインディケーターの特徴によります。

これらの構成要素の他に、関数の最小構成はもう一つのMQL5関数 OnDeInit()があります。:

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+  
void OnDeinit(const int reason)
  {

  }

我々のケースでは、必要ないので省略されています。


SMAとMetaTrader クライアントターミナルの間の相互作用

SMA.mq5をMetaEditor内で開き「コンパイル」キーを押した後、コンパイルされたファイルSMA.ex5の働きを考えましょう。.mq5 拡張子付きのテキストファイルは単なるテキスト形式のソースコードで、 クライアントターミナルで使用するには最初にコンパイルする必要があります。

Navigator ウインドウからこのインディケーターをチャートに添付した後、 MetaTraderがインディケーターの最初の部分のコードを実行します。その後、始めの一回の実行のために OnInit() 関数を呼びます。そして、新しい tick毎に (新しい気配値の到着後) OnCalculate() 関数を呼び、結果的にこの関数のコードを実行します。もしOnDeInit() がインディケーター内に存在し、MetaTraderがインディケーターをチャートから削除した後またはタイムフレームの変更後、この関数を一回呼びます。

インディケーターのすべての部分の意味と目的はこの説明の後クリアになります。global レベルでのコードの最初の部分にはインディケーター開始後に一回実行されるシンプル演算子があります。さらに、すべてのインディケーターのブロックで「見ることができ」、インディケーターがチャート上にある時それらの値を覚える変数の宣言があります。

関数OnCalculate()のブロックの中にそれらを置くことは不便なので、一回だけ実行される定数と関数はOnInit()関数の中にあるべきです。各バーの値を計算できるインディケーター計算のコードは関数 OnCalculate()の中にあるべきです。

インディケーターの削除後不要なごみをチャートから削除する手順 (もし、あれば) はOnDeInit()内にあるべきです。例えば、それはインディケーターによって作成された グラフィックオブジェクトの削除に必要です。

これらの説明の後、上記で考察したインディケーターのコードの詳細を考察する準備ができます。


SMAインディケーターのプログラムコード

コードラインの最初のグループは、インディケーター設定の追加的パラメータを指定できる演算子#propertyで始まります。可能なプログラムプロパティの完全なリストは documentation of MQL5にあります。必要な場合、インディケーターの追加的なプロパティを書くことが可能です。我々のケースでは5行のコードあり、それぞれのラインの目的はコメントに説明されています。:

//---- the indicator will be plotted in the main window
#property indicator_chart_window
//---- one buffer will be used for the calculations and plot of the indicator
#property indicator_buffers 1
//---- only one graphic plot is used 
#property indicator_plots   1
//---- the indicator should be plotted as a line
#property indicator_type1   DRAW_LINE
//---- the color of the indicator's line is red 
#property indicator_color1  Red 

セミコロン (";")がラインの後にないことに注意して下さい。理由は以下です。: 実際我々のケースでそれは定数の定義ですが別の方法で表されているからです。

我々の単純移動平均はユーザーが変えられる2つのパラメータがあります。 - それは平均期間と時間軸に沿ったインディケーター の水平シフト (バー数指定)です。2つのコードラインで宣言されており、これらの2つのパラメータはインディケーターの入力変数 として宣言されるべきです。:

//---- indicator input parameters
input int MAPeriod = 13; //averaging period
nput int MAShift = 0; //horizontal shift (in bars)

これらの入力パラメーターの宣言の後にコメントがあり、これらのコメントは入力パラメーターの名前としてインディケーターの「プロパティ」ウインドウ で見れます。:

我々のケースではこれらの名前はインディケーターの変数名よりも断然明瞭です。そのため、 これらのコメントはシンプルであるべきです。

そして括弧がない最後のコードラインは動的配列 ExtLineBuffer[]の宣言です。

//---- the declaration of the dynamic array
//that will be used further as an indicator's buffer
double ExtLineBuffer[];  

それは、いくつかの理由でグローバル変数として宣言されました。

まず最初に、この配列がインディケーターバッファーに変換されなければなりません。OnInit() 関数のブロックで実行されます。次に、インディケーターバッファー自身がOnCalculate() 関数内で使用されます。第三に、この配列がチャートに曲線としてプロットされるインディケーター値を保存します。グローバル変数として宣言された事実のため、すべてのインディケーターのブロックに利用可能で、インディケーターがチャートから離されるまでそのすべて値を保存します。

OnInit() 関数の内容はたった3つの演算子で表され、それらはMetaTrader クライアントターミナルの内蔵関数です。

最初の関数の呼び出しは、ゼロ個目のインディケーターバッファーに1次元動的配列 ExtLineBuffer[]を割り当てます。 入力パラメーター別の値付の別の関数の2つの呼び出しで、インディケーターを価格軸に沿ってシフトし、バーからのプロットをnumber MAPeriodで指定できます。

void OnInit()
  {
//----+
//---- assign the dynamic array ExtLineBuffer with 0th indicator's buffer
   SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA);
//---- set plot shift along the horizontal axis by MAShift bars
   PlotIndexSetInteger(0,PLOT_SHIFT,MAShift);
//---- set plot begin from the bar with number MAPeriod
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,MAPeriod);
//----+
  }

我々のインディケーター値にそれが適用される場合、PlotIndexSetInteger() 関数の最後の呼び出しはMAPeriodの値を渡します。 (OnCalculate()関数) のパラメータ beginから他のインディケーターへ)。ロジックはシンプルです。最初のMaPeriod-1 バーを平均するものがありません。そのためこの インディケーターのプロットが無益です。しかし、別のインディケーターの計算の起源をシフトするためこの値が渡されなければなりません。

それはカスタムインディケーターに使われる内蔵関数の全てのリストではなく、インディケーターのブロックにあるかもしれません。詳細はMQL5 documentation を参照。

最後にOnCalculate() 関数のコードを考えましょう。これらの関数はMetaTrader クライアントターミナルによって呼ばれるため、この関数に OnInit()関数のようなカスタム呼び出しはありません。この理由のため、その関数の入力パラメーターは定数として宣言されます。

int OnCalculate(
                const int rates_total,    // number of available bars in history at the current tick
                const int prev_calculated,// number of bars, calculated at previous tick
                const int begin,          // index of the first bar
                const double &price[]     // price array for the calculation
                )

これらの入力パラメーターは変更できません。 それらの値は、この関数コード内で更に使用するため、クライアントターミナルによって渡されます。OnCalculateの入力変数はdocumentation of MQL5に説明されています。OnCalculate()関数は、return(rates_total) 関数を使って、クライアントターミナルのためにその値を戻します。クライアントターミナルは、OnCalculate()実行後、 現在tick値を受け取り、別のパラメータprev_calculatedの値を戻します。そのため、バーインデックスの範囲を決め、以前のtickの後に現れたインディケーターの新しい値のみを一度に計算を実行することが常に可能です。

MetaTraderクライアントターミナルのバーは左から右に実行されるため、チャートに表示される最も古いバー(左)はインデックス 0を持ち、次はインデックス 1などであることに留意する必要があります。 バッファーExtLineBuffer[] のエレメントも同じ順です。

我々のインディケーターのOnCalculate関数内のシンプルなコード構造は一般的で、多くのテクニカル分析インディケーターに典型的です。そのため、 詳細を考えましょう。OnCalcualte() 関数のロジックは:

1. 計算に必要なバーの存在を確認
2. ローカル変数の宣言
3. 計算のために開始バーのインデックスを入手
4. インディケーターの計算のメインループ
5. rates_totalの値を演算子 return()を使ってクライアントターミナルに戻す。

最初の項は明らかだと思います。例えば、もし、 移動平均の平均期間が200に等しいがクライアントターミナルが100バーしかない場合、 この計算に必要なバーがないので、計算を実行する必要がありません。そのため演算子 returnを使ってクライアントターミナルに0を戻します。

//---- check for the presence of bars, sufficient for the calculation
   if(rates_total<MAPeriod-1+begin)
      return(0);

我々のインディケーターは、計算のための最小バー数を持っている他のインディケーターのデータに適用できます。定数beginの使用はこの事実を考慮に入れる必要があります。詳細は記事 1つの インディケーターを別の インディケーターに適用を参照。

このブロックで宣言されるローカル変数はOnCalculate() 関数内の中間計算にのみ必要です。これらの変数はこの関数の呼び出しの後、コンピュータRAMからリリースされます。

//---- declaration of local variables 
   int first,bar,iii;
   double Sum,SMA;

メイン ループ (変数 first)の開始インデックスには注意が必要です。その関数の最初の呼び出し後 (それはパラメータ prev_calculatedの値から決めることが可能) すべてのバーのインディケーター値の計算を実行しなければなりません。クライアントターミナルの更なるtickのすべてには、 新しいバーが現れた時だけ計算を実行しなければなりません。3行のコードでそれが完了します。:

//---- calculation of starting index first of the main loop
   if(prev_calculated==0) // check for the first start of the indicator
      first=MAPeriod-1+begin; // start index for all the bars
   else first=prev_calculated-1; // start index for the new bars

インディケーター再計算のメインループ演算子内の変数変更の範囲はすでに考察しました。

//---- main loop of the calculation
   for(bar=first;bar<rates_total;bar++)

メインループのバー処理は昇順(bar++)、つまり、左から右へ自然で正しい方向で行われます。我々のインディケーターでは別の方法で実行できます。 (逆順で)インディケーターで昇順を使用する方がいいです。メインループの変数は「bar」と名づけられるが、多くのプログラマーは「i」の名前の使用を好みます。コードを明瞭で可読にするため、私は「bar」の名前をを使用することを好みます。

メインループで実行されたアルゴリズムの平均はシンプルです。

{
      Sum=0.0;
       //---- summation loop for the current bar averaging
      for(iii=0;iii<MAPeriod;iii++)
         Sum+=price[bar-iii]; // Sum = Sum + price[bar - iii]; // eqaual to 
      
      //---- calculate averaged value
      SMA=Sum/MAPeriod;

      //---- set the element of the indicator buffer with the value of SMA we have calculated
      ExtLineBuffer[bar]=SMA;
     }

第二ループでは、その期間の以前のバーからの価格の累積総和を平均期間で割ることをを実行します。その結果、SMAの最終値を持ちます。

メインループの終了後、 OnCalculate 関数は、変数 rates_totalから利用可能なバー数を戻します。OnCalculate() 関数の次の呼び出しでは、 クライアントターミナルによってこの値が変数 prev_calculatedへ渡されます。1つ減らされたこの値はメインループの開始インデックスとして使用されます。

以下が各コードラインの詳細コメント付きインディケーターの完全なソースコード です。:

//+------------------------------------------------------------------+
//|                                                          SMA.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//---- the indicator will be plotted in the main window
#property indicator_chart_window
//---- one buffer will be used for the calculations and plot of the indicator
#property indicator_buffers 1
//---- only one graphic plot is used 
#property indicator_plots   1
//---- the indicator should be plotted as a line
#property indicator_type1   DRAW_LINE
//---- the color of the indicator's line is red 
#property indicator_color1  Red 

//---- indicator input parameters
input int MAPeriod = 13; //Averaging period
input int MAShift = 0; //Horizontal shift (in bars)

//---- the declaration of the dynamic array
//that will be used further as an indicator's buffer
double ExtLineBuffer[]; 
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+  
void OnInit()
  {
//----+
//---- assign the dynamic array ExtLineBuffer with 0th indicator's buffer
   SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA);
//---- set plot shift along the horizontal axis by MAShift bars
   PlotIndexSetInteger(0,PLOT_SHIFT,MAShift);
//---- set plot begin from the bar with number MAPeriod
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,MAPeriod);  
//----+
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(
                const int rates_total,    // number of available bars in history at the current tick
                const int prev_calculated,// number of bars, calculated at previous tick
                const int begin,          // index of the first bar
                const double &price[]     // price array for the calculation
                )
  {
//----+   
   //---- check for the presence of bars, sufficient for the calculation
   if (rates_total < MAPeriod - 1 + begin)
    return(0);
   
   //---- declaration of local variables 
   int first, bar, iii;
   double Sum, SMA;
   
   //---- calculation of starting index first of the main loop
   if(prev_calculated==0) // check for the first start of the indicator
      first=MAPeriod-1+begin; // start index for all the bars
   else first=prev_calculated-1; // start index for the new bars

   //---- main loop of the calculation
   for(bar = first; bar < rates_total; bar++)
    {    
      Sum=0.0;
      //---- summation loop for the current bar averaging
      for(iii=0;iii<MAPeriod;iii++)
         Sum+=price[bar-iii]; // It's equal to: Sum = Sum + price[bar - iii];
         
      //---- calculate averaged value
      SMA=Sum/MAPeriod;

      //---- set the element of the indicator buffer with the value of SMA we have calculated
      ExtLineBuffer[bar]=SMA;
    }
//----+     
   return(rates_total);
  }
//+------------------------------------------------------------------+

このように書かれたコードは断然わかりやすく、読みやすいです。

コードの理解をシンプルにするためのもう一つの方法は、 スペースと空行を使って明瞭にすることです。


結論

カスタムインディケーターのコードとMetaTrader クライアントターミナルの連携についてはこれでおしまいです。もちろんこの主題は考察したよりももっと幅広いですが、 本記事の目的は初めての人が基本を学ぶ手助けをするです。そのため詳細はドキュメンテーションを参照してください。

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

添付されたファイル |
sma.mq5 (1.78 KB)
sma_.mq5 (3.32 KB)
定義済みリスクおよびRRレシオに基づく半自動化ドラッグドロップExpert Advisor連携構築 定義済みリスクおよびRRレシオに基づく半自動化ドラッグドロップExpert Advisor連携構築
すべての取引を自動で行うトレーダーもいれば、複数インディケータのアウトプットを基にして自動と手動のミックスで取引を実行するトレーダーもいます。後者のグループの一員として、私はリスクと利益をチャートから直接、動的に評価するための連携ツールが必要でした。本稿は、定義済みの資本リスクおよびR/Rレシオを連携する半自動化Expert Advisorを実装する方法を提供します。EA パネル実行中には、 Expert Advisor リスク、R/R、ロットサイズ パラメータが変更可能です。
MQL5でのオブジェクトポインターの使用 MQL5でのオブジェクトポインターの使用
デフォルトで、MQL5ではすべてのオブジェクトがレファレンスによって渡されますが、オブジェクトポインターを使用する可能性があります。しかし、オブジェクトは初期化されないかもしれないので、ポインター 確認が必要です。 この場合、 MQL5はクリティカルエラーでアップロードされずにプログラムが終了します。自動作成されたオブジェクトはこのようなエラーが発生しないのでその意味で安全です。本記事ではオブジェクトレファレンスとオブジェクトポインターの違いを理解し、ポインターを使うセキュアコードの書き方を考察します。
CChartObject クラスに基づく新規GUIウィジェット設計と実装 CChartObject クラスに基づく新規GUIウィジェット設計と実装
前稿『GUI を使用した半自動Expert Advisor』を書いてのち、より複雑なインディケータやExpert Advisorsに新しい関数を伴うインターフェースを強化したいと思うようになりました。MQL5 標準ライブラリクラスを知ってから、新しいウィジェットを実装しました。本稿は、インディケータやExpert Advisorsで使用可能な新しい MQL5 GUI ウィジェットの設計と実装について述べます。本稿で提供しているウィジェットは CChartObjectSpinner、 CChartObjectProgressBar、CChartObjectEditTable です。
MetaTrader 5テスターのストラテジーのビジュアル化 MetaTrader 5テスターのストラテジーのビジュアル化
”百聞は一見にしかず”ということわざがあります。パリやヴェニスに関する本を読んだとしても、心のイメージに関して言えばこれらの美しい都市を夜に実際に歩いてみるのと同じ感覚を得ることはできません。ビジュアル化の利点は、私たちの生活のあらゆる部分で見つけることができます。それには市場も含まれます。例えば、チャートのプライスアナリストはインディケーターや、ストラテジーテスティングのビジュアル化ももちろん利用します。この記事ではMetaTrader 5のストラテジーテスターのすべてのヴィジュアライゼーション機能の説明をしていきます。