移動平均線の角度を計算してチャート上に表示する方法
移動平均線用のバッファーを登録
この記事では、移動平均線の角度を計算し、チャート上に表示する方法を解説します。
まずファイルの新規作成で「カスタムインディケータ」を選択後、ファイル名を「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(自動売買)を学びたい方へオススメコンテンツ

OANDAではEA(自動売買)を稼働するプラットフォームMT4/MT5の基本的な使い方について、画像や動画付きで詳しく解説しています。MT4/MT5のインストールからEAの設定方法までを詳しく解説しているので、初心者の方でもスムーズにEA運用を始めることが可能です。またOANDAの口座をお持ちであれば、独自開発したオリジナルインジケーターを無料で利用することもできます。EA運用をお考えであれば、ぜひ口座開設をご検討ください。
本ホームページに掲載されている事項は、投資判断の参考となる情報の提供を目的としたものであり、投資の勧誘を目的としたものではありません。投資方針、投資タイミング等は、ご自身の責任において判断してください。本サービスの情報に基づいて行った取引のいかなる損失についても、当社は一切の責を負いかねますのでご了承ください。また、当社は、当該情報の正確性および完全性を保証または約束するものでなく、今後、予告なしに内容を変更または廃止する場合があります。なお、当該情報の欠落・誤謬等につきましてもその責を負いかねますのでご了承ください。