MQL4でサインツールを作ろう!初心者向け解説②

MQL4で高度なインジケーター作成:マルチタイムフレーム平均足チャート

 

なおと

こんにちは!naoto555です。前回の記事で基本的なMQL4プログラミングを学びましたね。今回は、より高度なインジケーター作成に挑戦します。特に、マルチタイムフレームの平均足チャートを描画する素晴らしいコードを見つけたので、それを詳しく解説していきます!

謎こんにゃく

わぁ、高度なインジケーターですか?
でも、なおとさん、マルチタイムフレームって
どういう意味ですか?

なおと

いい質問です!マルチタイムフレームとは、
複数の時間軸のデータを同時に扱うことです。
例えば、1時間足のチャートで、4時間足や日足の
データも同時に表示できるんです。これにより、
より包括的な市場分析が可能になります。

謎こんにゃく

へぇ、すごいですね!でも、そんな複雑なことを
プログラムで実現できるんですか?

なおと

はい、できるんです!今回紹介するコードは、
まさにその複雑な処理を見事に実現しています。
このコードは私が考えたものではなく、
ネット上で見つけた素晴らしい作品なんです。
ローソク足の描画方法など、私では思いつかない
ような巧妙な技が使われています。

本記事のポイント
  • マルチタイムフレーム平均足チャートの実装: 複数の時間軸のデータを1つのチャートに表示
  • 高度なMQL4プログラミング技法: 配列操作、カスタム関数の活用、効率的なデータ処理
  • インジケーターのカスタマイズ: 外部パラメーターの設定と活用方法

このインジケーターを通じて、MQL4でのプログラミングスキルを一段階上のレベルに引き上げることができます。複雑そうに見えますが、一つずつ解説していくので、しっかりついてきてくださいね。

まずは、このインジケーターが実際にどのように表示されるのか、見てみましょう。

マルチタイムフレーム平均足チャートの表示例

この画像は、完成したマルチタイムフレーム平均足チャートの表示例です。メインチャートの下に別ウィンドウとして表示され、選択した時間軸(例えば日足)の平均足チャートが表示されています。赤と青のロウソク足は、それぞれ陰線(下降)と陽線(上昇)を表しており、赤と青の細い線はヒゲを示しています。この表示により、トレンドの方向や強さを視覚的に把握しやすくなっています。

これから、このインジケーターがどのように動作するのか、コードを細かく見ていきましょう。複雑に見えるかもしれませんが、一つずつ理解していけば、きっと同じようなインジケーターを作れるようになります。

マルチタイムフレーム平均足チャートのコード全文

以下が作成したコードの全文です。次の見出しで細かく抜粋してみていきます。

#property indicator_separate_window
#property indicator_buffers 6
#property indicator_color1 OrangeRed
#property indicator_color2 DodgerBlue
#property indicator_color4 Red
#property indicator_color5 RoyalBlue
#property indicator_color3  Black
#property indicator_color6  Black

#property indicator_width1 2
#property indicator_width2 2
#property indicator_width3 2
#property indicator_width4 1
#property indicator_width5 1
#property indicator_width6 1

extern int TimeFrame=240;
extern int Shift=0;
extern int xShift=0;

double ExtMapBuffer1[];
double ExtMapBuffer2[];
double ExtMapBuffer3[];
double ExtMapBuffer4[];
double ExtMapBuffer5[];
double ExtMapBuffer6[];

int init()
{
   SetIndexStyle(3,DRAW_HISTOGRAM);
   SetIndexBuffer(3, ExtMapBuffer1);
   if(TimeFrame==0) TimeFrame = Period();
   SetIndexShift(3,xShift+Shift*TimeFrame/Period());
   SetIndexLabel(3,"HA hi/lo dn ["+TimeFrame+"]"+Shift+"");
   SetIndexStyle(4,DRAW_HISTOGRAM);
   SetIndexBuffer(4, ExtMapBuffer2);
   SetIndexShift(4, xShift+Shift*TimeFrame/Period());
   SetIndexLabel(4,"HA hi/lo up["+TimeFrame+"]"+Shift+"");
   SetIndexStyle(0,DRAW_HISTOGRAM);
   SetIndexBuffer(0, ExtMapBuffer3);
   SetIndexShift(0, xShift+Shift*TimeFrame/Period());
   SetIndexLabel(0,"HA O/Cl dn ["+TimeFrame+"]"+Shift+"");
   SetIndexStyle(1,DRAW_HISTOGRAM);
   SetIndexBuffer(1, ExtMapBuffer4);
   SetIndexShift(1, xShift+Shift*TimeFrame/Period());
   SetIndexLabel(1,"HA O/Cl up ["+TimeFrame+"]"+Shift+"");
   SetIndexBuffer(5,ExtMapBuffer5);
   SetIndexBuffer(2,ExtMapBuffer6);
   SetIndexStyle(5,DRAW_HISTOGRAM);
   SetIndexStyle(2,DRAW_HISTOGRAM);
   SetIndexShift(5, xShift+Shift*TimeFrame/Period());
   SetIndexShift(2, xShift+Shift*TimeFrame/Period());

   if (TimeFrame <= Period()) TimeFrame = Period();

   string TimeFrameStr;
   switch(TimeFrame)
   {
      case 1 : TimeFrameStr="Period_M1"; break;
      case 5 : TimeFrameStr="Period_M5"; break;
      case 15 : TimeFrameStr="Period_M15"; break;
      case 30 : TimeFrameStr="Period_M30"; break;
      case 60 : TimeFrameStr="Period_H1"; break;
      case 240 : TimeFrameStr="Period_H4"; break;
      case 1440 : TimeFrameStr="Period_D1"; break;
      case 10080 : TimeFrameStr="Period_W1"; break;
      case 43200 : TimeFrameStr="Period_MN1"; break;
      default : TimeFrameStr="Current Timeframe";
   }
   IndicatorShortName("Heiken_Ashi["+TimeFrameStr+"]");
   
   return(0);
}

#property indicator_separate_window
#property indicator_buffers 6
#property indicator_color1 OrangeRed
#property indicator_color2 DodgerBlue
#property indicator_color4 Red
#property indicator_color5 RoyalBlue
#property indicator_color3  Black
#property indicator_color6  Black

#property indicator_width1 2
#property indicator_width2 2
#property indicator_width3 2
#property indicator_width4 1
#property indicator_width5 1
#property indicator_width6 1

extern int TimeFrame=240;
extern int Shift=0;
extern int xShift=0;

double ExtMapBuffer1[];
double ExtMapBuffer2[];
double ExtMapBuffer3[];
double ExtMapBuffer4[];
double ExtMapBuffer5[];
double ExtMapBuffer6[];

int init()
{
   SetIndexStyle(3,DRAW_HISTOGRAM);
   SetIndexBuffer(3, ExtMapBuffer1);
   if(TimeFrame==0) TimeFrame = Period();
   SetIndexShift(3,xShift+Shift*TimeFrame/Period());
   SetIndexLabel(3,"HA hi/lo dn ["+TimeFrame+"]"+Shift+"");
   SetIndexStyle(4,DRAW_HISTOGRAM);
   SetIndexBuffer(4, ExtMapBuffer2);
   SetIndexShift(4, xShift+Shift*TimeFrame/Period());
   SetIndexLabel(4,"HA hi/lo up["+TimeFrame+"]"+Shift+"");
   SetIndexStyle(0,DRAW_HISTOGRAM);
   SetIndexBuffer(0, ExtMapBuffer3);
   SetIndexShift(0, xShift+Shift*TimeFrame/Period());
   SetIndexLabel(0,"HA O/Cl dn ["+TimeFrame+"]"+Shift+"");
   SetIndexStyle(1,DRAW_HISTOGRAM);
   SetIndexBuffer(1, ExtMapBuffer4);
   SetIndexShift(1, xShift+Shift*TimeFrame/Period());
   SetIndexLabel(1,"HA O/Cl up ["+TimeFrame+"]"+Shift+"");
   SetIndexBuffer(5,ExtMapBuffer5);
   SetIndexBuffer(2,ExtMapBuffer6);
   SetIndexStyle(5,DRAW_HISTOGRAM);
   SetIndexStyle(2,DRAW_HISTOGRAM);
   SetIndexShift(5, xShift+Shift*TimeFrame/Period());
   SetIndexShift(2, xShift+Shift*TimeFrame/Period());

   if (TimeFrame <= Period()) TimeFrame = Period();

   string TimeFrameStr;
   switch(TimeFrame)
   {
      case 1 : TimeFrameStr="Period_M1"; break;
      case 5 : TimeFrameStr="Period_M5"; break;
      case 15 : TimeFrameStr="Period_M15"; break;
      case 30 : TimeFrameStr="Period_M30"; break;
      case 60 : TimeFrameStr="Period_H1"; break;
      case 240 : TimeFrameStr="Period_H4"; break;
      case 1440 : TimeFrameStr="Period_D1"; break;
      case 10080 : TimeFrameStr="Period_W1"; break;
      case 43200 : TimeFrameStr="Period_MN1"; break;
      default : TimeFrameStr="Current Timeframe";
   }
   IndicatorShortName("Heiken_Ashi["+TimeFrameStr+"]");
   
   return(0);
}

int init()
{
   SetIndexStyle(3, DRAW_HISTOGRAM);
   SetIndexBuffer(3, ExtMapBuffer1);
   if (TimeFrame == 0) TimeFrame = Period();
   SetIndexShift(3, xShift + Shift * TimeFrame / Period());
   SetIndexLabel(3, "HA hi/lo dn [" + TimeFrame + "]" + Shift + "");
   
   SetIndexStyle(4, DRAW_HISTOGRAM);
   SetIndexBuffer(4, ExtMapBuffer2);
   SetIndexShift(4, xShift + Shift * TimeFrame / Period());
   SetIndexLabel(4, "HA hi/lo up[" + TimeFrame + "]" + Shift + "");
   
   SetIndexStyle(0, DRAW_HISTOGRAM);
   SetIndexBuffer(0, ExtMapBuffer3);
   SetIndexShift(0, xShift + Shift * TimeFrame / Period());
   SetIndexLabel(0, "HA O/Cl dn [" + TimeFrame + "]" + Shift + "");
   
   SetIndexStyle(1, DRAW_HISTOGRAM);
   SetIndexBuffer(1, ExtMapBuffer4);
   SetIndexShift(1, xShift + Shift * TimeFrame / Period());
   SetIndexLabel(1, "HA O/Cl up [" + TimeFrame + "]" + Shift + "");
   
   SetIndexBuffer(5, ExtMapBuffer5);
   SetIndexBuffer(2, ExtMapBuffer6);
   SetIndexStyle(5, DRAW_HISTOGRAM);
   SetIndexStyle(2, DRAW_HISTOGRAM);
   SetIndexShift(5, xShift + Shift * TimeFrame / Period());
   SetIndexShift(2, xShift + Shift * TimeFrame / Period());

   if (TimeFrame <= Period()) TimeFrame = Period();

   string TimeFrameStr;
   switch(TimeFrame)
   {
      case 1 : TimeFrameStr = "Period_M1"; break;
      case 5 : TimeFrameStr = "Period_M5"; break;
      case 15 : TimeFrameStr = "Period_M15"; break;
      case 30 : TimeFrameStr = "Period_M30"; break;
      case 60 : TimeFrameStr = "Period_H1"; break;
      case 240 : TimeFrameStr = "Period_H4"; break;
      case 1440 : TimeFrameStr = "Period_D1"; break;
      case 10080 : TimeFrameStr = "Period_W1"; break;
      case 43200 : TimeFrameStr = "Period_MN1"; break;
      default : TimeFrameStr = "Current Timeframe";
   }
   IndicatorShortName("Heiken_Ashi[" + TimeFrameStr + "]");
   
   return(0);
}

int start()
{
   datetime TimeArray[];
   int i, limit, y = 0, counted_bars = IndicatorCounted();
 
   ArrayCopySeries(TimeArray, MODE_TIME, Symbol(), TimeFrame); 
   
   limit = Bars - counted_bars + TimeFrame / Period();
   for(i = 0, y = 0; i < limit; i++)
   {
      if (Time[i] < TimeArray[y]) y++;

      ExtMapBuffer1[i] = iCustom(NULL, TimeFrame, "Heiken Ashi", 0, y);
      ExtMapBuffer2[i] = iCustom(NULL, TimeFrame, "Heiken Ashi", 1, y);
      ExtMapBuffer3[i] = iCustom(NULL, TimeFrame, "Heiken Ashi", 2, y);
      ExtMapBuffer4[i] = iCustom(NULL, TimeFrame, "Heiken Ashi", 3, y);
      ExtMapBuffer5[i] = MathMin(ExtMapBuffer1[i], ExtMapBuffer2[i]);
      ExtMapBuffer6[i] = MathMin(ExtMapBuffer3[i], ExtMapBuffer4[i]);
   }
   
   if (TimeFrame > Period()) {
      int PerINT = TimeFrame / Period() + 1;
      datetime TimeArr[];
      ArrayResize(TimeArr, PerINT);
      ArrayCopySeries(TimeArr, MODE_TIME, Symbol(), Period()); 
      for (i = 0; i < PerINT + 1; i++) {
         if (TimeArr[i] >= TimeArray[0]) {
            ExtMapBuffer1[i] = ExtMapBuffer1[0];
            ExtMapBuffer2[i] = ExtMapBuffer2[0];
            ExtMapBuffer3[i] = ExtMapBuffer3[0];
            ExtMapBuffer4[i] = ExtMapBuffer4[0];
         }
      }
   }
  
   return(0);
}

インジケーターの基本設定

まずは、このマルチタイムフレーム平均足チャートインジケーターの基本的な設定部分を見ていきましょう。

#property indicator_separate_window
#property indicator_buffers 6
#property indicator_color1 OrangeRed
#property indicator_color2 DodgerBlue
#property indicator_color4 Red
#property indicator_color5 RoyalBlue
#property indicator_color3 Black
#property indicator_color6 Black

#property indicator_width1 2
#property indicator_width2 2
#property indicator_width3 2
#property indicator_width4 1
#property indicator_width5 1
#property indicator_width6 1

extern int TimeFrame = 240;
extern int Shift = 0;
extern int xShift = 0;

double ExtMapBuffer1[];
double ExtMapBuffer2[];
double ExtMapBuffer3[];
double ExtMapBuffer4[];
double ExtMapBuffer5[];
double ExtMapBuffer6[];

なおと

このコードブロックでは、インジケーターの基本的な設定を行っています。特に注目すべき点がいくつかありますね。

1. インジケーターの表示設定

  • #property indicator_separate_window: このインジケーターを別ウィンドウで表示します。
  • #property indicator_buffers 6: 6つのバッファ(データ保存用の配列)を使用します。
  • #property indicator_color1#property indicator_color6: 各バッファの色を設定しています。
  • #property indicator_width1#property indicator_width6: 各線の太さを設定しています。

2. 外部パラメーター

  • extern int TimeFrame = 240;: デフォルトで4時間足(240分)を使用します。ユーザーが変更可能です。
  • extern int Shift = 0;extern int xShift = 0;: チャートのシフト(ずらし)を調整できます。

3. データバッファ

  • double ExtMapBuffer1[];double ExtMapBuffer6[];: 6つのデータバッファを宣言しています。これらにインジケーターの計算結果が格納されます。

謎こんにゃく

うわぁ、たくさんの設定がありますね。
でも、なぜ6つものバッファが必要なんですか?

なおと

良い質問です!
平均足チャートは通常のローソク足よりも複雑で、
高値、安値、始値、終値に加えて、平均足が上昇か下降の区別も表現する必要があります。
6つのバッファを使うことで、これらの情報を全て表現できるんです。
具体的な使い方は後ほど詳しく説明しますね。
TimeFrameの設定について

TimeFrameパラメーターには以下の値を使用できます:

  • PERIOD_M1 (1): 1分足
  • PERIOD_M5 (5): 5分足
  • PERIOD_M15 (15): 15分足
  • PERIOD_M30 (30): 30分足
  • PERIOD_H1 (60): 1時間足
  • PERIOD_H4 (240): 4時間足
  • PERIOD_D1 (1440): 日足
  • PERIOD_W1 (10080): 週足
  • PERIOD_MN1 (43200): 月足

デフォルトは4時間足(240)ですが、ニーズに応じて変更可能です。

これらの基本設定を理解することで、インジケーターの全体像が見えてきますね。次のセクションでは、これらの設定を活用して実際にインジケーターを初期化する方法を見ていきます。

インジケーターの初期化処理

インジケーターの動作を理解する上で重要なのが初期化処理です。この処理は、インジケーターがチャートに適用されたときに一度だけ実行されます。init()関数の中身を見ていきましょう。

int init()
{
   SetIndexStyle(3,DRAW_HISTOGRAM);
   SetIndexBuffer(3, ExtMapBuffer1);
   if(TimeFrame==0) TimeFrame = Period();
   SetIndexShift(3,xShift+Shift*TimeFrame/Period());
   SetIndexLabel(3,"HA hi/lo dn ["+TimeFrame+"]"+Shift+"");
   
   // ... (同様の設定が他のインデックスにも適用されます)

   if (TimeFrame <= Period()) TimeFrame = Period();

   string TimeFrameStr;
   switch(TimeFrame)
   {
      case 1 : TimeFrameStr="Period_M1"; break;
      case 5 : TimeFrameStr="Period_M5"; break;
      // ... (他の時間軸の設定)
      default : TimeFrameStr="Current Timeframe";
   }
   IndicatorShortName("Heiken_Ashi["+TimeFrameStr+"]");
   
   return(0);
}

なおと

init()関数では、インジケーターの表示方法や各バッファの設定を行っています。特に重要なのは、SetIndexStyle()とSetIndexBuffer()の使い方です。

こちらが、Period()関数の説明を含めた、置き換え用の文章です:

1. インデックスのスタイル設定

  • SetIndexStyle(3,DRAW_HISTOGRAM);: 3番目のインデックスをヒストグラム(棒グラフ)として描画するよう設定しています。
  • SetIndexBuffer(3, ExtMapBuffer1);: 3番目のインデックスにExtMapBuffer1を関連付けています。
  • SetIndexShift(3,xShift+Shift*TimeFrame/Period());: チャート上での表示位置を調整しています。ここでPeriod()関数が使用されていますが、これは現在のチャートの時間軸を返す関数です。
  • SetIndexLabel(3,"HA hi/lo dn ["+TimeFrame+"]"+Shift+"");: インデックスにラベルを付けています。

2. 時間軸の設定

  • if(TimeFrame==0) TimeFrame = Period();: TimeFrameが設定されていない場合、現在のチャートの時間軸を使用します。Period()関数は、現在のチャートの時間軸を分単位で返します(例:5分足なら5、1時間足なら60)。
  • if (TimeFrame <= Period()) TimeFrame = Period();: 選択された時間軸が現在のチャートの時間軸より小さい場合、現在のチャートの時間軸を使用します。これにより、常に現在の時間軸以上の値が使用されることが保証されます。

3. インジケーター名の設定

  • switch文を使用して、選択された時間軸に応じたインジケーター名を設定しています。これにより、ユーザーは現在使用している時間軸を容易に識別できます。
  • IndicatorShortName("Heiken_Ashi["+TimeFrameStr+"]");: インジケーターの短縮名を設定しています。TimeFrameStrには、選択された時間軸に応じた文字列(例:’Period_H1’)が格納されます。
Period()関数について

Period()関数は、現在のチャートの時間軸の値を取得するための重要な関数です。
この関数は以下のような値を戻り値として返します:

  • 1分足: 1
  • 5分足: 5
  • 15分足: 15
  • 30分足: 30
  • 1時間足: 60
  • 4時間足: 240
  • 日足: 1440
  • 週足: 10080
  • 月足: 43200

この関数を使用することで、インジケーターは現在のチャートの時間軸に適応し、適切なデータを表示することができます。

謎こんにゃく

へぇ、こんなに細かい設定があるんですね。
でも、なぜインデックスを0から始めずに3から始めているんですか?

なおと

鋭い質問ですね!実は、このコードでは0から5までの6つのインデックスを使っています。3から始めているのは、表示の順序や重要度を調整するためです。例えば、3と4のインデックスは高値と安値を、0と1は始値と終値を表しています。この順序で設定することで、チャート上で正しく重ねて表示されるようになっているんです。
init()関数の重要性

init()関数は、インジケーターの「顔」とも言える部分です。ここでの設定が、チャート上でのインジケーターの見た目や動作を決定します。適切に設定することで、トレーダーにとって使いやすく、情報を読み取りやすいインジケーターを作成できます。

この初期化処理により、インジケーターの基本的な構造が決定されます。次のセクションでは、実際にデータを計算し、チャートに表示する処理について見ていきましょう。

インジケーターのメイン処理:start()関数

start()関数は、新しい価格データが来るたびに実行される、インジケーターの心臓部とも言える部分です。ここでは、実際にマルチタイムフレームの平均足データを計算し、バッファに格納します。

int start()
{
   datetime TimeArray[];
   int i, limit, y = 0, counted_bars = IndicatorCounted();
 
   ArrayCopySeries(TimeArray, MODE_TIME, Symbol(), TimeFrame); 
   
   limit = Bars - counted_bars + TimeFrame / Period();
   for(i = 0, y = 0; i < limit; i++)
   {
      if (Time[i] < TimeArray[y]) y++;

      ExtMapBuffer1[i] = iCustom(NULL, TimeFrame, "Heiken Ashi", 0, y);
      ExtMapBuffer2[i] = iCustom(NULL, TimeFrame, "Heiken Ashi", 1, y);
      ExtMapBuffer3[i] = iCustom(NULL, TimeFrame, "Heiken Ashi", 2, y);
      ExtMapBuffer4[i] = iCustom(NULL, TimeFrame, "Heiken Ashi", 3, y);
      ExtMapBuffer5[i] = MathMin(ExtMapBuffer1[i], ExtMapBuffer2[i]);
      ExtMapBuffer6[i] = MathMin(ExtMapBuffer3[i], ExtMapBuffer4[i]);
   }
   
   if (TimeFrame > Period()) {
      int PerINT = TimeFrame / Period() + 1;
      datetime TimeArr[];
      ArrayResize(TimeArr, PerINT);
      ArrayCopySeries(TimeArr, MODE_TIME, Symbol(), Period()); 
      for (i = 0; i < PerINT + 1; i++) {
         if (TimeArr[i] >= TimeArray[0]) {
            ExtMapBuffer1[i] = ExtMapBuffer1[0];
            ExtMapBuffer2[i] = ExtMapBuffer2[0];
            ExtMapBuffer3[i] = ExtMapBuffer3[0];
            ExtMapBuffer4[i] = ExtMapBuffer4[0];
         }
      }
   }
  
   return(0);
}

なおと

start()関数は複雑に見えますが、大きく分けて3つの部分から構成されています。時系列データの準備、平均足データの計算、そして時間軸の調整です。一つずつ見ていきましょう。

1. 時系列データの準備

  • ArrayCopySeries(TimeArray,MODE_TIME,Symbol(),TimeFrame);: 選択された時間軸の時系列データをTimeArray配列にコピーします。
  • limit=Bars-counted_bars+TimeFrame/Period();: 計算が必要なバーの数を決定します。

2. 平均足データの計算

  • メインのfor文内で、iCustom()関数を使用して各時間軸の平均足データを計算し、それぞれのバッファに格納しています。
  • ExtMapBuffer5[i]=MathMin(ExtMapBuffer1[i], ExtMapBuffer2[i]);ExtMapBuffer6[i]=MathMin(ExtMapBuffer3[i], ExtMapBuffer4[i]);: 高値と安値の最小値を計算し、別のバッファに格納しています。

3. 時間軸の調整

  • if文以降の部分では、選択された時間軸が現在のチャートの時間軸より大きい場合の処理を行っています。
  • これにより、異なる時間軸間でのデータの整合性を保っています。

謎こんにゃく

うわぁ、複雑そうですね。でも、iCustom()関数って何をしているんですか?

なおと

良い質問です!iCustom()関数は、他のカスタムインジケーターの値を取得するための関数です。ここでは、”Heiken Ashi”という名前の平均足インジケーターの値を取得しています。0,1,2,3の数字は、そのインジケーターの異なるバッファ(Open, High, Low, Close)を指定しています。つまり、この関数を使うことで、別の時間軸の平均足データを簡単に取得できるんです。
start()関数の効率性

start()関数は新しいティックごとに呼び出されるため、効率的な処理が重要です。このコードでは、必要最小限のバーのみを計算することで、処理速度を向上させています。また、ArrayCopySeries()関数を使用して時系列データを効率的に取得しています。

この start() 関数により、マルチタイムフレームの平均足データがリアルタイムで計算され、チャート上に表示されます。次のセクションでは、このインジケーターの使い方と実際のトレードへの応用について見ていきましょう。

承知しました。以下が、ご指示に基づいて修正したセクション6の全文です。

平均足ローソク足の描画メカニズム

このインジケーターの核心部分である平均足ローソク足の描画方法について、詳しく解説していきます。平均足チャートは通常のローソク足とは異なる計算方法を用いており、その描画にはいくつかの工夫が施されています。

なおと

平均足の描画方法は、通常のローソク足よりも少し複雑です。6つのバッファを使用して、ローソク足の各要素を表現しています。それぞれの役割を見ていきましょう。

1. バッファの役割

  • ExtMapBuffer1: 平均足の下ヒゲ(ローソク足の下側の線)
  • ExtMapBuffer2: 平均足の上ヒゲ(ローソク足の上側の線)
  • ExtMapBuffer3: 平均足の始値
  • ExtMapBuffer4: 平均足の終値
  • ExtMapBuffer5: 高値と安値の最小値(下ヒゲの描画に使用)
  • ExtMapBuffer6: 始値と終値の最小値(ローソク足の本体の描画に使用)
平均足ローソク足の構造

上の図は平均足ローソク足の構造を示しています。各バッファがどのようにローソク足の要素に対応しているかがわかります。

2. 描画ロジック

平均足の描画は、以下のようなロジックで行われています:

ExtMapBuffer1[i]=iCustom(NULL,TimeFrame,"Heiken Ashi",0,y);
ExtMapBuffer2[i]=iCustom(NULL,TimeFrame,"Heiken Ashi",1,y);
ExtMapBuffer3[i]=iCustom(NULL,TimeFrame,"Heiken Ashi",2,y);
ExtMapBuffer4[i]=iCustom(NULL,TimeFrame,"Heiken Ashi",3,y);
ExtMapBuffer5[i]=MathMin(ExtMapBuffer1[i], ExtMapBuffer2[i]);
ExtMapBuffer6[i]=MathMin(ExtMapBuffer3[i], ExtMapBuffer4[i]);

ここで重要なのが、iCustom関数の使用です。この関数は、同じフォルダ内にある既存の”Heiken Ashi.ex4″インジケーターから値を取得しています。0,1,2,3はそれぞれ下ヒゲ、上ヒゲ、始値、終値に対応します。

※ SetIndexBuffer(3, ExtMapBuffer1); のような関数呼び出しは、MT4のチャーティングエンジンにバッファを「登録」します。これにより、MT4は自動的にこれらのバッファの内容を使用してインジケーターを描画します。

 

なおと

このように既存のインジケーターから値を受け取ることで、平均足の計算ロジックをゼロから書く必要がなくなります。これは非常に効率的で、コードの再利用性を高めるテクニックです。複雑な計算を含むインジケーターを作成する際、このアプローチを使うことで開発時間を大幅に短縮できるんです。

謎こんにゃく

なるほど!でも、なぜExtMapBuffer5とExtMapBuffer6が必要なんですか?

なおと

いい質問です!ExtMapBuffer5とExtMapBuffer6は、ローソク足を正確に描画するために重要な役割を果たします。ExtMapBuffer5は高値と安値の最小値を取ることで、下ヒゲの正確な位置を決定します。同様に、ExtMapBuffer6は始値と終値の最小値を取ることで、ローソク足の本体の下端を正確に描画します。これにより、上昇・下降にかかわらず、常に正しい形状のローソク足が描画されるんです。
平均足の特徴と既存インジケーターの活用

平均足チャートは、通常のローソク足よりもトレンドを捉えやすいという特徴があります。これは、平均値を使用することで価格のノイズを減らし、全体的な方向性をより明確に表現できるためです。既存のインジケーターを活用することで、こうした複雑な計算を簡単に自分のインジケーターに組み込むことができます。ただし、リアルタイムの価格変動に対しては若干の遅れが生じる可能性があることに注意が必要です。

このようなメカニズムにより、マルチタイムフレームの平均足チャートが描画されています。既存のインジケーターを活用することで、効率的に高度な機能を実現できることがわかりましたね。次のセクションでは、このインジケーターのカスタマイズ方法について見ていきましょう。

はい、最後のセクションとしてまとめを書きましょう。以下に、このインジケーターの重要なポイントを振り返り、今後の発展の可能性について触れるセクションを作成します。

まとめと今後の展望

ここまで、マルチタイムフレーム平均足チャートのインジケーターについて詳しく見てきました。このインジケーターの特徴と重要なポイントを振り返ってみましょう。

なおと

このインジケーターの開発を通じて、MQL4プログラミングの高度なテクニックをいくつか学びましたね。特に重要なポイントを整理してみましょう。

1. 主要な特徴と学んだこと

  • マルチタイムフレーム分析:異なる時間軸のデータを1つのチャートで表示
  • 既存インジケーターの活用:iCustom関数を使用して既存の”Heiken Ashi”インジケーターのデータを取得
  • 効率的なバッファ管理:6つのバッファを使用して複雑な平均足データを表現
  • MT4チャーティングエンジンの活用:SetIndexBufferを使用した自動描画の実現

2. インジケーター開発の重要なテクニック

  • init()関数での適切な初期化:バッファの設定、スタイルの指定
  • start()関数での効率的なデータ処理:必要最小限のバーのみを計算
  • Period()関数の活用:現在のチャートの時間軸に適応するロジック
  • 既存リソースの再利用:ゼロからコードを書くのではなく、既存のインジケーターを活用

謎こんにゃく

なるほど!でも、このインジケーターをさらに改良することはできるんでしょうか?

なおと

もちろんです!このインジケーターは非常に柔軟性が高いので、さまざまな方向で改良や拡張が可能です。例えば:

1. 複数の時間軸を同時に表示する機能の追加
2. ユーザーが色や線の太さをカスタマイズできるオプションの実装
3. トレンド転換のアラート機能の追加
4. 他のテクニカル指標(例:移動平均線やRSI)との組み合わせ

これらの改良を加えることで、より強力で使いやすいインジケーターになるでしょう。

今後の学習に向けて

このインジケーターの開発を通じて学んだテクニックは、他の多くのMQL4プロジェクトにも応用できます。特に、既存のリソースを活用する方法や、MT4のチャーティングエンジンを効率的に使用する方法は、高度なインジケーターやEAの開発に大いに役立つでしょう。今回の学びを基礎として、さらに複雑で洗練されたツールの開発にチャレンジしてみてください。

マルチタイムフレーム平均足チャートインジケーターの開発を通じて、MQL4プログラミングの奥深さと可能性を体験しました。このような高度なインジケーターを自分で作れるようになることで、トレードの幅が大きく広がります。ぜひ、ここで学んだ技術を活かして、自分だけのオリジナルインジケーターの開発に挑戦してみてください!