FX自動売買基礎と応用

移動平均線の角度を計算してチャート上に表示する方法


移動平均線用のバッファーを登録


この記事では、移動平均線の角度を計算し、チャート上に表示する方法を解説します。

まずファイルの新規作成で「カスタムインディケータ」を選択後、ファイル名を「MA_angle」とします。今回は「OnChartEvent」を使うので、「カスタムインディケータのイベントハンドラ」画面で「OnChartEvent」にチェックを入れ、次へ進んで移動平均線用にバッファーを登録しましょう。ラベルを「MA」、タイプを「Line」、カラーを「White」として「完了」をクリックすれば、ひな形の完成です。

「カスタムインディケータのイベントハンドラ」画面


オブジェクトを削除するための接頭辞を定義


最初に、バッファーの名前が長いので「MABuffer」から「MA」に変更しましょう。


double         MA[];
SetIndexBuffer(0,MA);

今回はオブジェクトを使用するので、まとめて削除などができるように接頭辞を登録しておきます。ファイル上部のプロパティ「#property indicator_width1 1」の下に「PREFIX」を次のように定義します。


#define  PREFIX "MA_angle_"

また、「Custom indicator initialization function」の下に「Custom indicator deinitialization function」を設けて、OnDeinit関数を用いた次のコードを記述します。


void OnDeinit(const int reason)
{
   ObjectsDeleteAll(0, PREFIX);
}

続いて、OnCalculate関数の配下に移動平均線を描画する式を書いていきます。ここでは仮で描画するので詳細説明は省きますが、for文を使って次のように記述しましょう。


int limit = Bars - prev_calculated - 1;
if (limit < 1) limit = 1;
for (int i = limit; i >= 0; i--)
   MA[i] = iMA(NULL, 0, 20, 0, MODE_SMA, PRICE_CLOSE, i);

これをコンパイルしてチャートにセットすると、白の移動平均線が表示されることが分かります。

白の移動平均線が表示


マウス位置の移動平均線情報を取得


次にマウス位置を検出して、そのマウス位置の移動平均線の角度を計算するプログラムを作ります。

マウス位置を検出するには、「ChartSetInteger」で「CHART_EVENT MOUSE_MOVE」を使う宣言をしておく必要があります。OnInit関数配下の「SetIndexBuffer(0,MA);」の下に次のコードを記述しましょう。


ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);

そして、OnChartEvent関数の配下に、マウスを移動したときの処理を書いていきます。まずは「ChartXYToTimePrice」でマウスのXY座標を、価格と時間情報に変換します。X座標は「lparam」、Y座標は「dparam」です。マウス位置の時間が取得できたら、「iBarShift」でそのときのバーの本数、位置を取得します。


if (id == CHARTEVENT_MOUSE_MOVE) {
   datetime time;
   double price;
   int win = 0;
   ChartXYToTimePrice(0, (int)lparam, (int)dparam, win, time, price);
   int bar = iBarShift(NULL, 0, time);

次に、そのマウス位置の移動平均線の情報を取得します。具体的には、移動平均線の点を2点取得して、そこから角度を導き出します。チャート上の時間と価格をXY座標に変換する「ChartTimePriceToXY」を使います。1点目はバーの位置で、それをx[0]とy[0]に入れます。2点目はそれよりも一つ前の点で、同じようにx[1]とy[1]に入れていきます。


int x[2], y[2];
   ChartTimePriceToXY(0, 0, Time[bar], MA[bar], x[0], y[0]);
   ChartTimePriceToXY(0, 0, Time[bar + 1], MA[bar + 1], x[1], y[1]);

これで2点の取得ができました。


移動平均線の2点の情報から角度を計算


続いて、取得した移動平均線の2点を利用して角度を求めます。角度の計算は、x[0]とx[1]が等しくないときに行うようにします。計算式に関しては次の画像の通りです。

計算式

ここまでに取得した2点が黄色の座標です。角度を算出するためには、まず三角形の底辺Aの長さ、高さBの長さをそれぞれ求めます。そしてtanθがA分のBになるので、「arctan(B/A)」で角度を算出できます。

そこで利用するのが、指定した数値のarctan(アークタンジェント)を返す「MathArctan」という関数です。この関数で求められる答えは「ラジアン」という弧度法の単位なので、度数法の単位「度」に変換するためには180を掛けてπで割る必要があります(MT4ではM_PIがπに相当します)。


double angle = 90;
   if (x[0] != x[1]) angle = MathArctan(double(y[1] - y[0]) / (x[0] - x[1])) * 180 / M_PI;

これで角度を求めることができました。なお、X座標とY座標の引き算で「1」と「0」が逆になっています。これは座標の基準点が左上にあり、Y座標は下に行くほど数値が増えていくためです。


Labelオブジェクトを設定


移動平均線の2点の情報から角度を計算する方法を解説しました。次は、その計算結果をチャートにLabelオブジェクトで表示してみましょう。

Labelオブジェクトのサンプルコードは、MQL4リファレンスからコピーして使います。MQL4リファレンスの目次にある「Constants, Enumerations and Structures」→「Objects Constants」→「Object Types」をクリックするとオブジェクトの一覧が表示されます。

その中から「OBJ_LABEL」を選択し、あらかじめ用意されている「Create a text label」のコードをコピーしてファイル下部に貼り付けます。

まず「//— reset the error value」「ResetLastError();」の2行と、「 Print(__FUNCTION__,」「”: failed to create text label! Error code = “,GetLastError());」の2行は不要なので削除しましょう。

また、表示する数値が変わるように「if(!ObjectCreate(chart_ID,name,OBJ_LABEL,sub_window,0,0)){」の下に「ObjectSetString(chart_ID,name,OBJPROP_TEXT,text);」を、色が変わるように「ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);」を挿入します。


//+------------------------------------------------------------------+ 
//| Create a text label                                              | 
//+------------------------------------------------------------------+ 
bool LabelCreate(const long              chart_ID=0,               // chart's ID 
                 const string            name="Label",             // label name 
                 const int               sub_window=0,             // subwindow index 
                 const int               x=0,                      // X coordinate 
                 const int               y=0,                      // Y coordinate 
                 const ENUM_BASE_CORNER  corner=CORNER_LEFT_UPPER, // chart corner for anchoring 
                 const string            text="Label",             // text 
                 const string            font="Arial",             // font 
                 const int               font_size=10,             // font size 
                 const color             clr=clrRed,               // color 
                 const double            angle=0.0,                // text slope 
                 const ENUM_ANCHOR_POINT anchor=ANCHOR_LEFT_UPPER, // anchor type 
                 const bool              back=false,               // in the background 
                 const bool              selection=false,          // highlight to move 
                 const bool              hidden=true,              // hidden in the object list 
                 const long              z_order=0)                // priority for mouse click 
  { 
//--- create a text label 
   if(!ObjectCreate(chart_ID,name,OBJ_LABEL,sub_window,0,0)){
      ObjectSetString(chart_ID,name,OBJPROP_TEXT,text);
      ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);
      return(false); 
     } 
//--- set label coordinates 
   ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x); 
   ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y); 
//--- set the chart's corner, relative to which point coordinates are defined 
   ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,corner); 
//--- set the text 
   ObjectSetString(chart_ID,name,OBJPROP_TEXT,text); 
//--- set text font 
   ObjectSetString(chart_ID,name,OBJPROP_FONT,font); 
//--- set font size 
   ObjectSetInteger(chart_ID,name,OBJPROP_FONTSIZE,font_size); 
//--- set the slope angle of the text 
   ObjectSetDouble(chart_ID,name,OBJPROP_ANGLE,angle); 
//--- set anchor type 
   ObjectSetInteger(chart_ID,name,OBJPROP_ANCHOR,anchor); 
//--- set color 
   ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr); 
//--- display in the foreground (false) or background (true) 
   ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back); 
//--- enable (true) or disable (false) the mode of moving the label by mouse 
   ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection); 
   ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection); 
//--- hide (true) or display (false) graphical object name in the object list 
   ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden); 
//--- set the priority for receiving the event of a mouse click in the chart 
   ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order); 
//--- successful execution 
   return(true); 
  }


角度の大きさによって文字色を変える


設定したLabelCreateは、OnChartEvent関数内に記述します。パラメーターのXは「5」、Yは「15」、テキストは「DoubleToString」の2桁、文字のフォントは「Arial Bold」、サイズは「30」とします。文字のカラー指定に関しては、LabelCreateの上で行います。今回は文字色を白にしましょう。


color clr = clrWhite;
LabelCreate(0, PREFIX +  "angle", 0, 5, 15, CORNER_LEFT_UPPER, DoubleToString(angle, 2), "Arial Bold", 30, clr);  

また、バーの大きさがMA配列のサイズより大きくなり過ぎた場合は、そこで計算を終わりにしたいので、「int bar = iBarShift(NULL, 0, time);」の下に次のif文を追記します。


if (bar > ArraySize(MA) - 2) return;

これでコンパイルすると、マウス位置にある移動平均線の角度がチャートの左上に表示されます。

マウス位置にある移動平均線の角度がチャートの左上に表示

最後に、移動平均線の角度が45度以上だった場合は表示する文字の色を赤に、マイナス45度以下だった場合は表示する文字の色を青に変化するようにしましょう。「color clr = clrWhite;」の下に次のif文を加えればOKです。


if (angle >= 45) clr = clrRed;
   else if (angle <= -45) clr = clrDodgerBlue;

これでコンパイルして、移動平均線の角度が45度以上またはマイナス45度以下のところにマウスを移動させると、角度の文字色が変わることが分かります。

角度の文字色が変わる


ソースコード


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


//+------------------------------------------------------------------+
//|                                                     MA_angle.mq4 |
//|                        Copyright 2021, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
//--- plot MA
#property indicator_label1  "MA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrWhite
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
#define  PREFIX "MA_angle_"
//--- indicator buffers
double         MA[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
   SetIndexBuffer(0, MA);
   ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
//---
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   ObjectsDeleteAll(0, PREFIX);
}
//+------------------------------------------------------------------+
//| 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[])
{
//---
   int limit = Bars - prev_calculated - 1;
   if (limit < 1) limit = 1;
   for (int i = limit; i >= 0; i--)
      MA[i] = iMA(NULL, 0, 20, 0, MODE_SMA, PRICE_CLOSE, i);
//--- return value of prev_calculated for next call
   return(rates_total);
}
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
   if (id == CHARTEVENT_MOUSE_MOVE) {
      datetime time;
      double price;
      int win = 0;
      ChartXYToTimePrice(0, (int)lparam, (int)dparam, win, time, price);
      int bar = iBarShift(NULL, 0, time);
      if (bar > ArraySize(MA) - 2) return;
      int x[2], y[2];
      ChartTimePriceToXY(0, 0, Time[bar], MA[bar], x[0], y[0]);
      ChartTimePriceToXY(0, 0, Time[bar + 1], MA[bar + 1], x[1], y[1]);

      double angle = 90;
      if (x[0] != x[1]) angle = MathArctan(double(y[1] - y[0]) / (x[0] - x[1])) * 180 / M_PI;
      
      color clr = clrWhite;
      if (angle >= 45) clr = clrRed;
      else if (angle <= -45) clr = clrDodgerBlue;
      LabelCreate(0, PREFIX +  "angle", 0, 5, 15, CORNER_LEFT_UPPER, DoubleToString(angle, 2), "Arial Bold", 30, clr);  
   }
}

//+------------------------------------------------------------------+
//| Create a text label                                              |
//+------------------------------------------------------------------+
bool LabelCreate(const long              chart_ID = 0,             // chart's ID
                 const string            name = "Label",           // label name
                 const int               sub_window = 0,           // subwindow index
                 const int               x = 0,                    // X coordinate
                 const int               y = 0,                    // Y coordinate
                 const ENUM_BASE_CORNER  corner = CORNER_LEFT_UPPER, // chart corner for anchoring
                 const string            text = "Label",           // text
                 const string            font = "Arial",           // font
                 const int               font_size = 10,           // font size
                 const color             clr = clrRed,             // color
                 const double            angle = 0.0,              // text slope
                 const ENUM_ANCHOR_POINT anchor = ANCHOR_LEFT_UPPER, // anchor type
                 const bool              back = false,             // in the background
                 const bool              selection = false,        // highlight to move
                 const bool              hidden = true,            // hidden in the object list
                 const long              z_order = 0)              // priority for mouse click
{
//--- create a text label
   if(!ObjectCreate(chart_ID, name, OBJ_LABEL, sub_window, 0, 0)) {
      ObjectSetString(chart_ID, name, OBJPROP_TEXT, text);
      ObjectSetInteger(chart_ID, name, OBJPROP_COLOR, clr);
      return(false);
   }
//--- set label coordinates
   ObjectSetInteger(chart_ID, name, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(chart_ID, name, OBJPROP_YDISTANCE, y);
//--- set the chart's corner, relative to which point coordinates are defined
   ObjectSetInteger(chart_ID, name, OBJPROP_CORNER, corner);
//--- set the text
   ObjectSetString(chart_ID, name, OBJPROP_TEXT, text);
//--- set text font
   ObjectSetString(chart_ID, name, OBJPROP_FONT, font);
//--- set font size
   ObjectSetInteger(chart_ID, name, OBJPROP_FONTSIZE, font_size);
//--- set the slope angle of the text
   ObjectSetDouble(chart_ID, name, OBJPROP_ANGLE, angle);
//--- set anchor type
   ObjectSetInteger(chart_ID, name, OBJPROP_ANCHOR, anchor);
//--- set color
   ObjectSetInteger(chart_ID, name, OBJPROP_COLOR, clr);
//--- display in the foreground (false) or background (true)
   ObjectSetInteger(chart_ID, name, OBJPROP_BACK, back);
//--- enable (true) or disable (false) the mode of moving the label by mouse
   ObjectSetInteger(chart_ID, name, OBJPROP_SELECTABLE, selection);
   ObjectSetInteger(chart_ID, name, OBJPROP_SELECTED, selection);
//--- hide (true) or display (false) graphical object name in the object list
   ObjectSetInteger(chart_ID, name, OBJPROP_HIDDEN, hidden);
//--- set the priority for receiving the event of a mouse click in the chart
   ObjectSetInteger(chart_ID, name, OBJPROP_ZORDER, z_order);
//--- successful execution
   return(true);
}
//+------------------------------------------------------------------+


本記事の監修者・HT FX


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

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

EA運用の注意点

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


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

この記事をシェアする

ホーム » FX自動売買基礎と応用 » 移動平均線の角度を計算してチャート上に表示する方法