FX自動売買基礎と応用

ローソク足を塗りつぶして表示する自動売買の作成方法


実体用とヒゲ用のバッファーを登録


この記事で紹介するのは、ローソク足の塗りつぶし表示についてです。

具体的には、ヒストグラム(HISTOGRAM)という棒グラフを表示するタイプのインジケーターを利用して、ローソク足を塗りつぶします。ローソク足は実体部分とヒゲ部分で太さが違うので、実体用に1ペア、ヒゲ用に1ペアという具合にヒストグラムをペアで使い、四つのバッファーでローソク足を表現します。

まずはファイルの新規作成で「カスタムインディケータ」を選択し、ファイル名を「FlowCandle」とします。パラメーターは追加せず次へ進み、「カスタムインディケータのイベントハンドラ」の画面で「OnTimer」と「OnChartEvent」の両方にチェックを入れましょう。

「カスタムインディケータのイベントハンドラ」の画面で「OnTimer」と「OnChartEvent」の両方にチェック

今回はインジケーター用のバッファー二つを1セットとして1本の線を引くので、「カスタムインディケータの描画プロパティ」画面のプロット欄で、実体用とヒゲ用の計四つのバッファーを登録します。

  • 一つ目は実体用でラベルを「Body0」、タイプを「Histogram」、カラーを「DarkOrange」に、
  • 二つ目は実体用のBody0と対となるペアでラベルを「Body1」、タイプを「Histogram」、カラーを「Magenta」にします。
  • 三つ目はヒゲ用で、ラベルを「Pin0」、タイプを「Histogram」、カラーを「DarkOrange」に、
  • 四つ目はヒゲ用のPin0と対となるペアでラベルを「Pin1」、タイプを「Histogram」、カラーを「Magenta」にして「完了」をクリックすれば、ひな形の完成です。

なお、Histogramをペアで利用する際は、必ず最初のバッファーが偶数となるように定義して下さい(例えばバッファー番号1と2のように、最初のバッファー番号を奇数にしてしまうと、ペアとして認識されません)。

最初のバッファーが偶数となるように定義


アラートとタイマーを宣言


新規ファイルが開いたら、まず上部のプロパティを変更します。「Body0」と「Body1」の太さ(width)のところを「1」から「2」に変えましょう。


#property indicator_label1  "Body0"
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  clrDarkOrange
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2
//--- plot Body1
#property indicator_label2  "Body1"
#property indicator_type2   DRAW_HISTOGRAM
#property indicator_color2  clrFuchsia
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

また、バッファーの名前が長いので「Body0Buffer」→「Body0」という具合に、該当する箇所の「Buffer」を省いて短くしましょう。


double Body0[];
double Body1[];
double Pin0[];
double Pin1[];

同様にOnInit関数内で使われているバッファー名も、次のように変更します。


SetIndexBuffer(0, Body0);
SetIndexBuffer(1, Body1);
SetIndexBuffer(2, Pin0);
SetIndexBuffer(3, Pin1);

今回はアラート条件を作り、それに応じて表示と非表示を切り替えるようにしたいので、「double Pin1[];」の下で「ALERT」を宣言します。


bool ALERT;

また、タイマー関数を使用して塗りつぶした足を移動させたいので、0.01秒ごとに動くタイマーをセットします。

OnInit関数内で「EventSetMillisecondTimer」を宣言しましょう。このEventSetMillisecondTimerは、カッコ内に指定した数字の秒数に従って計算が繰り返される関数です。単位はミリ秒となっており、「10」と入力すると0.01秒間隔となります。


EventSetMillisecondTimer(10);

タイマーを使うので、タイマーを消す処理もセットする必要があります。

「Custom indicator initialization function」の下に「Custom indicator deinitialization function」を設け、OnDeinit関数を用いてタイマーを消す「EventKillTimer」を宣言します。


void OnDeinit(const int reason)
{
   EventKillTimer();
}

続いて、チャートをクリックしたときにアラートが入ったり切れたりするようにします。OnChartEvent関数配下に次のコードを記述しましょう。


if (id == CHARTEVENT_CLICK){
   if (!ALERT) ALERT = true; 
   else ALERT = false;
}


OnTimer関数の処理を記述


ローソク足の実体用とヒゲ用のバッファーを登録し、アラートとタイマーを宣言する方法を解説しました。続いて、OnTimer関数の処理を記述していきます。

まず、配列を指定した値で初期化する「ArrayInitialize」を使って、四つのバッファーを毎回リセットするようにします。


ArrayInitialize(Body0, EMPTY_VALUE);
ArrayInitialize(Body1, EMPTY_VALUE);
ArrayInitialize(Pin0, EMPTY_VALUE);
ArrayInitialize(Pin1, EMPTY_VALUE);

そして、アラートがONのときに、表示しているチャートの範囲で処理を実行します。

「CHART_FIRST_VISIBLE_BAR」でチャートの左端のバーを取得し、そこからチャート幅全体の本数を表す「CHART_WIDTH_IN_BARS」を引くことで、右端の値を求めることが可能です。もしその計算結果がマイナスとなってしまった場合は「0」とします。

表示されている幅の本数を「barF - barR」で定義し、塗りつぶす範囲を全体幅の4分の1とします。


if (ALERT) {
   int barF = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
   int barR = barF - (int)ChartGetInteger(0, CHART_WIDTH_IN_BARS);
   if (barR < 0) barR = 0;
   int width = barF - barR;
   int len = width / 4;


チャートの右から指定本数を塗りつぶす


続いて、for文を用いてチャートの右から指定本数を塗りつぶすようにします。Body0は実体の高値にしたいので始値と終値の高い方を、Body1は実体の安値にしたいので始値と終値の低い方を入れます。同じようにヒゲのPin0に高値を、Pin1に安値を入れておきます。


for (int i = barR; i < barR + len; i++) {
   Body0[i] = MathMax(Open[i], Close[i]);
   Body1[i] = MathMin(Open[i], Close[i]);
   Pin0[i] = High[i];
   Pin1[i] = Low[i];
}

このままではローソク足を塗りつぶすのにタイムラグが生じるので、それが起きないようにfor文を抜けたところにチャートの再描画を行う「ChartRedraw」を加えます。


ChartRedraw();

これでコンパイルしてチャートにセットし、チャート上をクリックすると、右から4分の1の範囲のローソク足がオレンジ色に塗りつぶされることが分かります。また、チャート上をクリックするごとに、塗りつぶしありの状態となしの状態が切り替わります。

塗りつぶしありの状態

塗りつぶしなしの状態


タイマーを使って塗りつぶす足を動かす


今度は、タイマーを利用して、塗りつぶす足を移動させるようにしましょう。

スタートを左端のバーから始まるように、for文を改修します。また、位置をずらすためにカウントするよう変更します。そうすることでローソク足の塗りつぶしが左から右へとずれていきます。

そして「barF + len - count」の値が0より小さくなったときに、カウントをリセットするif文も加えます。その際「i」がマイナスとなってしまうことがあるので、正の場合のみ実行するようにしておきます。


static int count;
for (int i = barF - count; i < barF + len - count; i++) {
   if (i >= 0) {
      Body0[i] = MathMax(Open[i], Close[i]);
      Body1[i] = MathMin(Open[i], Close[i]);
      Pin0[i] = High[i];
      Pin1[i] = Low[i];
   }
}
count++;
if(barF + len - count <=0) count = 0;
ChartRedraw();

これでコンパイルしてチャート上をクリックすると、ローソク足の塗りつぶしが左から右に流れていきます。

ローソク足の塗りつぶしが左から右に流れていきます

ローソク足の塗りつぶしが左から右に流れていきます


色違いの塗りつぶしを追加


続いて、色違いの塗りつぶしをもう1系統表示していきます。

まず「i」から「width + len」の半分の長さを引いた「j」を定義し、それが負の値となってしまった場合はwidth + lenの長さを足すようにします。


int j = i - (width + len) / 2;
if (j < 0) j += width + len;

そして、jを利用して同じように塗りつぶしを表示します。色を変えるために、Body0とBody1の「Max」と「Min」を、Pin0とPin1の「High」と「Low」をそれぞれ入れ替えましょう。この高値と安値を入れ替えると色が変わるのは、MT4に標準で入っている平均足の特性と一緒です。


if (j >= 0) {
   Body0[j] = MathMin(Open[j], Close[j]);
   Body1[j] = MathMax(Open[j], Close[j]);
   Pin0[j] = High[j];
   Pin1[j] = Low[j];
}

これでコンパイルすると、オレンジ色とマゼンタ色の塗りつぶしが、左から右へと追いかけっこをするように動きます。

オレンジ色とマゼンタ色の塗りつぶしが、左から右へと追いかけっこをするように動く


ローソク足の幅に合わせて塗りつぶしを表示


このままではチャートを拡大表示した場合に、塗りつぶしの実体の幅が変化しないので、チャートの拡大に合わせて幅が太くなるように修正しましょう。

塗りつぶしの実体の幅が変化しない

修正を加える箇所は、OnChartEvent関数の配下です。今回はswitch文を使って条件分岐を行います。Switchの後のカッコ内には「条件式」を入れ、その式の値に一致するcaseの箇所に飛んで処理が実行されます。

チャートのスケールをチェックし、その値に応じて塗りつぶしの幅を変化させるようにしましょう。スケールは0~5までの6段階です。スケールが「0」もしくは「1」の場合は幅を「1」に、スケールが「2」の場合は幅を「2」に、スケールが「3」の場合は幅を「3」に、スケールが「4」の場合は幅を「6」に、スケールが「5」の場合は幅を「13」に変更します。


if (id == CHARTEVENT_CHART_CHANGE) {
   int width = 0;
   switch ((int)ChartGetInteger(0, CHART_SCALE)) {
   case 0 :
   case 1 :
      width = 1;
      break;
   case 2 :
      width = 2;
      break;
   case 3 :
      width = 3;
      break;
   case 4 :
      width = 6;
      break;
   case 5 :
      width = 13;
      break;
   }

そして「SetIndexStyle」を用いて、スタイルはそのままに幅だけ変わるように設定します。次のコードを追記することで、0番目と1番目のバッファーを指定できます。


SetIndexStyle(0, DRAW_HISTOGRAM, EMPTY, width);
SetIndexStyle(1, DRAW_HISTOGRAM, EMPTY, width);

これでコンパイルしてチャートを拡大してみましょう。ローソク足の幅に合わせて塗りつぶしの表示も変わることが分かります。

ローソク足の幅に合わせて塗りつぶしの表示も変わる


ソースコード


今回、作成したソースコードは下記の通りです。


//+------------------------------------------------------------------+
//|                                                   FlowCandle.mq4 |
//|                        Copyright 2022, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 4
#property indicator_plots   4
//--- plot Body0
#property indicator_label1  "Body0"
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  clrDarkOrange
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2
//--- plot Body1
#property indicator_label2  "Body1"
#property indicator_type2   DRAW_HISTOGRAM
#property indicator_color2  clrFuchsia
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2
//--- plot Pin0
#property indicator_label3  "Pin0"
#property indicator_type3   DRAW_HISTOGRAM
#property indicator_color3  clrDarkOrange
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1
//--- plot Pin1
#property indicator_label4  "Pin1"
#property indicator_type4   DRAW_HISTOGRAM
#property indicator_color4  clrFuchsia
#property indicator_style4  STYLE_SOLID
#property indicator_width4  1
//--- indicator buffers
double         Body0[];
double         Body1[];
double         Pin0[];
double         Pin1[];
bool ALERT;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
   SetIndexBuffer(0, Body0);
   SetIndexBuffer(1, Body1);
   SetIndexBuffer(2, Pin0);
   SetIndexBuffer(3, Pin1);

   EventSetMillisecondTimer(10);
//---
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   EventKillTimer();
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
{
//---

//--- return value of prev_calculated for next call
   return(rates_total);
}
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
{
//---
   ArrayInitialize(Body0, EMPTY_VALUE);
   ArrayInitialize(Body1, EMPTY_VALUE);
   ArrayInitialize(Pin0, EMPTY_VALUE);
   ArrayInitialize(Pin1, EMPTY_VALUE);

   if (ALERT) {
      int barF = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
      int barR = barF - (int)ChartGetInteger(0, CHART_WIDTH_IN_BARS);
      if (barR < 0) barR = 0;
      int width = barF - barR;
      int len = width / 4;
      
      static int count;
      
      for (int i = barF - count; i < barF + len - count; i++) {
         if (i >= 0) {
            Body0[i] = MathMax(Open[i], Close[i]);
            Body1[i] = MathMin(Open[i], Close[i]);
            Pin0[i] = High[i];
            Pin1[i] = Low[i];
         }
         
         int j = i - (width + len) / 2;
         if (j < 0) j += width + len;
         
         if (j >= 0) {
            Body0[j] = MathMin(Open[j], Close[j]);
            Body1[j] = MathMax(Open[j], Close[j]);
            Pin0[j] = Low[j];
            Pin1[j] = High[j];
         }
      }
      
      count++;
      if(barF + len - count <=0) count = 0;

      ChartRedraw();
   }
}
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
//---
   if (id == CHARTEVENT_CHART_CHANGE) {
      int width = 0;
      switch ((int)ChartGetInteger(0, CHART_SCALE)) {
      case 0 :
      case 1 :
         width = 1;
         break;
      case 2 :
         width = 2;
         break;
      case 3 :
         width = 3;
         break;
      case 4 :
         width = 6;
         break;
      case 5 :
         width = 13;
         break;
      }
   
      SetIndexStyle(0, DRAW_HISTOGRAM, EMPTY, width);
      SetIndexStyle(1, DRAW_HISTOGRAM, EMPTY, width);
   }
   
   if (id == CHARTEVENT_CLICK) {
      if (!ALERT) ALERT = true;
      else ALERT = false;
   }
}


本記事の監修者・HT FX


2013年にFXを開始し、その後専業トレーダーへ。2014年からMT4/MT5のカスタムインジケーターの開発に取り組む。ブログでは100本を超えるインジケーターを無料公開。投資スタイルは自作の秒足インジケーターを利用したスキャルピング。

EA(自動売買)を学びたい方へオススメコンテンツ

EA運用の注意点

OANDAではEA(自動売買)を稼働するプラットフォームMT4/MT5の基本的な使い方について、画像や動画付きで詳しく解説しています。MT4/MT5のインストールからEAの設定方法までを詳しく解説しているので、初心者の方でもスムーズにEA運用を始めることが可能です。またOANDAの口座をお持ちであれば、独自開発したオリジナルインジケーターを無料で利用することもできます。EA運用をお考えであれば、ぜひ口座開設をご検討ください。


本ホームページに掲載されている事項は、投資判断の参考となる情報の提供を目的としたものであり、投資の勧誘を目的としたものではありません。投資方針、投資タイミング等は、ご自身の責任において判断してください。本サービスの情報に基づいて行った取引のいかなる損失についても、当社は一切の責を負いかねますのでご了承ください。また、当社は、当該情報の正確性および完全性を保証または約束するものでなく、今後、予告なしに内容を変更または廃止する場合があります。なお、当該情報の欠落・誤謬等につきましてもその責を負いかねますのでご了承ください。

この記事をシェアする

ホーム » FX自動売買基礎と応用 » ローソク足を塗りつぶして表示する自動売買の作成方法