MACDがクロスした際にサインが出て、アラートとMT4/MT5のアプリにプッシュ通知が届くインジケーター「macd-alert」を無料配布しています。
macd-alertはコード(MT5版)を完全公開していますので、MQL5の学習にも役立ちます。
macd-alertの機能と使い方を紹介

macd-alertは、サブウィンドウに「MACD線」「シグナル線」「ヒストグラム(棒)」を表示し、さらに MACD線とシグナル線のクロス(交差)を●サインで表示します。
また、アラート、プッシュ通知(ON/OFF可能)で知らせる機能を持ち、判定を未確定足と確定足で切り替えることも可能です。
計算に使う「期間」

設定画面で変更できる期間は次の3つになります。
短期EMA期間:12(初期値 12)
長期EMA期間:26(初期値 26)
シグナルEMA期間:9(初期値 9)
これは一般的に「標準MACD」と呼ばれる代表的な組み合わせで、短期と長期の差(勢い)を見て、さらにその差を平均化して判断しやすくするための構成です。
macd-alertは、内部的に次の順番で値を作っています。
終値を元に、期間12の指数平滑移動平均(EMA)を作ります。
同じく終値を元に、期間26のEMAを作ります。
MACD線は 短期EMA(12) − 長期EMA(26) です。
意味としては「短期の勢いが長期より強いか弱いか」を数値化したものです。
MACD線が上側に行きやすい:短期の動きが強くなっている
MACD線が下側に行きやすい:短期の動きが弱くなっている
シグナル線は、MACD線を元にした期間9のEMAです。
MACD線はブレやすいので、シグナル線で「基準」を作って交差を見やすくします。
ヒストグラムは MACD線 − シグナル線 です。
つまり「2本の線の距離」を棒で表しています。
棒が大きい:MACD線とシグナル線の差が大きい(勢いが強い)
棒が小さい:差が小さい(勢いが弱い)
0付近:交差が近い(転換が起きやすい位置)
このインジケーターは、棒を2色で塗り分けています。
判定は「前の棒より勢いが増えたか/減ったか」を見ています(プラス圏・マイナス圏で比較方向が変わります)。
要するに、勢いが強まっている側/弱まっている側を色で区別する仕組みです。
クロス(交差)の判定方法(GC/DCが出る条件)

クロス判定は「前の足」と「今見ている足」を比較して決めています。
前の足では:MACD線がシグナル線以下
今の足では:MACD線がシグナル線より上
になった瞬間をGCとしています。
前の足では:MACD線がシグナル線以上
今の足では:MACD線がシグナル線より下
になった瞬間をDCとしています。
サイン(●)は、ONのときにクロス箇所へ出ます(GC用・DC用で別色)。
「確定足/未確定足」モードの意味(どの足で判定するか)
ここが通知のタイミングに直結します。
現在進行中の足(最新の足)でクロスを判定します
早く気付けますが、足が確定するまでに状況が変わり、クロスが消える可能性がありま
確定した足で判定します
コード上は、確定足モードのとき「最新足ではサインを出さない」制御が入り、通知対象は「ひとつ前の足」になります。
通知(Alert / Push)の条件と重複防止
通知は「クロスが成立」かつ「未確定足or確定足」で発生します。
画面アラートを出す
プッシュ通知を送る(MT5側の通知設定が必要)
※同じ時間の足で何度も通知しないように、最後に通知した足の時間を記録して重複を防いでいます。
表示期間の設定(過去をどこまで描くか)

このインジケーターは、過去データを無制限に全部描かず、表示期間(日数)を指定できます。
選択肢は:10日 / 180日 / 1年(初期)/ 2年 / 3年 / 4年 / 5年 / 10年
この設定により、
指定期間より古い部分は「描画開始位置」をずらして見えないようにし必要な分のデータは自動的に読み込みを試みるという動作になっています(重さ対策・履歴不足対策のためです)。
操作パネル(ボタン)の役割
Open:パネルの開閉
Alert:画面アラートON/OFF
Push:プッシュ通知ON/OFF
Kakutei:確定足判定ON
Mikakutei:未確定足判定ON
Sign:●サイン表示ON/OFF
ボタンのON/OFF状態は、チャートごと・通貨ペアごとに保存され、次回も引き継がれます。
macd-alertはMT4/MT5専用のインジケーターになります。MT4/MT5のダウンロード方法や、インジケーターの導入方法は関連記事をご確認下さい。


※二次配布や販売、自作発言は禁止です。
※本ツールによる損害の責任は負いかねます。
※学習用としてご活用下さい。
macd-alertのコードを完全公開
macd-alertのコードを完全コメント付きで公開しています。コードはプログラム初心者向けに、なるべく直感的に見やすくしていますので整理されたきれいなコードではありません。
#property copyright "pokyun.com"
#property link "https://pokyun.com/"
#property description "※二次配布や販売、自作発言は禁止です。"
#property description "※本ツールによる損害の責任は負いかねます。"
#property description "※学習用としてご活用下さい。"
#property version "1.10"
#property indicator_separate_window // インジケーターをサブウィンドウに表示する設定
#property indicator_buffers 6 // インジケーターで使用するバッファ(配列)の数を6に設定
#property indicator_plots 5 // グラフ描画(プロット)する数を5に設定
//--- プロパティ設定
#property indicator_label1 "MACD Line" // 1番目のラインラベル名を「MACD Line」に設定
#property indicator_type1 DRAW_LINE // 1番目の描画タイプを「線」に設定
#property indicator_color1 clrDodgerBlue // 1番目の線の色を「ドジャーブルー」に設定
#property indicator_width1 2 // 1番目の線の太さを2に設定
#property indicator_label2 "Signal EMA Line" // 2番目のラインラベル名を「Signal EMA Line」に設定
#property indicator_type2 DRAW_LINE // 2番目の描画タイプを「線」に設定
#property indicator_color2 clrRed // 2番目の線の色を「赤」に設定
#property indicator_width2 2 // 2番目の線の太さを2に設定
#property indicator_label3 "Histogram" // 3番目のラベル名を「Histogram」に設定
#property indicator_type3 DRAW_COLOR_HISTOGRAM // 3番目の描画タイプを「カラーヒストグラム」に設定
#property indicator_width3 2 // 3番目のヒストグラムの太さを2に設定
#property indicator_color3 clrLightSkyBlue, clrSteelBlue // 3番目のヒストグラムの色を2色(上昇時・下降時用)設定
#property indicator_label4 "GC_Sign" // 4番目のラベル名を「GC_Sign」に設定
#property indicator_type4 DRAW_ARROW // 4番目の描画タイプを「矢印」に設定
#property indicator_color4 clrTomato // 4番目の矢印の色を「トマト色」に設定
#property indicator_width4 4 // 4番目の矢印のサイズを4に設定
#property indicator_label5 "DC_Sign" // 5番目のラベル名を「DC_Sign」に設定
#property indicator_type5 DRAW_ARROW // 5番目の描画タイプを「矢印」に設定
#property indicator_color5 clrAqua // 5番目の矢印の色を「アクア」に設定
#property indicator_width5 4 // 5番目の矢印のサイズを4に設定
// オブジェクト名の接頭辞(プレフィックス)
#define Button_Prefix "【MACD_SYS】" // ボタンなどのオブジェクト名の頭に付ける識別子を定義
// --- オブジェクト名(ボタンの枠と文字の名前)の定義 ---
string Open_Btn_Waku = Button_Prefix + "Btn_OPEN_Frame"; // OPENボタンの枠のオブジェクト名を定義
string Open_Btn_Label = Button_Prefix + "Btn_OPEN_Text"; // OPENボタンの文字ラベルのオブジェクト名を定義
string Alert_Btn_Waku = Button_Prefix + "Btn_Alert_Frame"; // Alertボタンの枠のオブジェクト名を定義
string Alert_Btn_Label = Button_Prefix + "Btn_Alert_Text"; // Alertボタンの文字ラベルのオブジェクト名を定義
string Push_Btn_Waku = Button_Prefix + "Btn_Push_Frame"; // Pushボタンの枠のオブジェクト名を定義
string Push_Btn_Label = Button_Prefix + "Btn_Push_Text"; // Pushボタンの文字ラベルのオブジェクト名を定義
string Kakutei_Btn_Waku = Button_Prefix + "Btn_KAKUTEI_Frame"; // 確定足ボタンの枠のオブジェクト名を定義
string Kakutei_Btn_Label = Button_Prefix + "Btn_KAKUTEI_Text"; // 確定足ボタンの文字ラベルのオブジェクト名を定義
string Mikakutei_Btn_Waku = Button_Prefix + "Btn_MIKAKUTEI_Frame"; // 未確定足ボタンの枠のオブジェクト名を定義
string Mikakutei_Btn_Label= Button_Prefix + "Btn_MIKAKUTEI_Text"; // 未確定足ボタンの文字ラベルのオブジェクト名を定義
string Sign_Btn_Waku = Button_Prefix + "Btn_SIGN_Frame"; // サインボタンの枠のオブジェクト名を定義
string Sign_Btn_Label = Button_Prefix + "Btn_SIGN_Text"; // サインボタンの文字ラベルのオブジェクト名を定義
// --- プルダウン用 enum定義(表示期間設定) ---
enum ENUM_HYOUJI_KIKAN // 表示期間を選択するための列挙型を定義
{
KIKAN_DAYS_10 = 10, // 選択肢:10日
KIKAN_DAYS_180 = 180, // 選択肢:180日
KIKAN_YEAR_1 = 365, // 選択肢:1年
KIKAN_YEAR_2 = 730, // 選択肢:2年
KIKAN_YEAR_3 = 1095, // 選択肢:3年
KIKAN_YEAR_4 = 1460, // 選択肢:4年
KIKAN_YEAR_5 = 1825, // 選択肢:5年
KIKAN_YEAR_10 = 3650 // 選択肢:10年
};
//--- 入力パラメータ(設定画面で変える数値)
input int FastEMA_Kikan = 12; // 短期EMA期間を設定(デフォルト12)
input int SlowEMA_Kikan = 26; // 長期EMA期間を設定(デフォルト26)
input int SignalEMA_Kikan = 9; // シグナルEMA期間を設定(デフォルト9)
input ENUM_HYOUJI_KIKAN Hyouji_Kikan = KIKAN_YEAR_1; // 表示期間を設定(デフォルト1年)
// --- UI カラー設定 ---
input color Btn_Off_Color = clrWhite; // ボタンがOFFの時の背景色を設定(白)
input color Text_Color = clrBlack; // ボタンの文字色を設定(黒)
input color Waku_Color = clrSteelBlue; // ボタンの枠線の色を設定(スチールブルー)
input color Open_On_Color = clrDeepSkyBlue; // OpenボタンがONの時の色を設定(ディープスカイブルー)
input color Alert_On_Color = clrRed; // AlertボタンがONの時の色を設定(赤)
input color Push_On_Color = clrSpringGreen; // PushボタンがONの時の色を設定(スプリンググリーン)
input color Kakutei_On_Color = clrAqua; // KakuteiボタンがONの時の色を設定(アクア)
input color Mikakutei_On_Color= clrAqua; // MikakuteiボタンがONの時の色を設定(アクア)
input color Sign_On_Color = clrGold; // SignボタンがONの時の色を設定(ゴールド)
// --- サイン色設定 ---
input color Sign_Color_GC = clrTomato; // ゴールデンクロス(GC)のサイン色を設定(トマト)
input color Sign_Color_DC = clrAqua; // デッドクロス(DC)のサイン色を設定(アクア)
//--- バッファ(計算結果を入れる箱)
double MACD_Buffer[]; // MACDラインの値を格納する動的配列
double Signal_Buffer[]; // シグナルラインの値を格納する動的配列
double Histogram_Buffer[]; // ヒストグラムの値を格納する動的配列
double Color_Buffer[]; // ヒストグラムの色情報を格納する動的配列
double GC_Sign_Buffer[]; // ゴールデンクロス(GC)のサイン位置を格納する動的配列
double DC_Sign_Buffer[]; // デッドクロス(DC)のサイン位置を格納する動的配列
//--- 内部変数・ハンドル
int FastEMA_Handle = INVALID_HANDLE; // FastEMAの指標ハンドルを格納する変数(初期値は無効)
int SlowEMA_Handle = INVALID_HANDLE; // SlowEMAの指標ハンドルを格納する変数(初期値は無効)
bool All_Keisan = false; // 全てのバーを再計算するかどうかのフラグ(初期値はfalse)
int SubWindow_No = -1; // インジケーターが表示されているサブウィンドウ番号(初期値は-1)
datetime LastAlert_Time = 0; // 最後にアラートを出した時間を記録する変数(初期値は0)
// ショートネーム定義
string Indicator_Name = "MACD-ALERT"; // インジケーターの短縮名を定義
// --- スイッチ状態(ON/OFF) ---
bool Panel_On_Off = true; // パネルの開閉状態を保持する変数(初期値ON)
bool Alert_On_Off = false; // アラート機能のON/OFF状態を保持する変数(初期値OFF)
bool Push_On_Off = false; // プッシュ通知機能のON/OFF状態を保持する変数(初期値OFF)
bool Kakutei_On_Off = false; // 確定足判定モードのON/OFF状態を保持する変数(初期値OFF)
bool Mikakutei_On_Off = true; // 未確定足判定モードのON/OFF状態を保持する変数(初期値ON)
bool Sign_On_Off = true; // サイン表示のON/OFF状態を保持する変数(初期値ON)
//+------------------------------------------------------------------+
//| グローバル変数(GV)操作用のキー名を作成
//時間足を切り替えてもボタンのON/OFF状態がリセットされないよう、設定をMT5内に保存しておく役割です。
//どのチャートの設定かわかるように名札(チャートID)をつけて保存することで、他のチャートと混ざらないようにしています。 |
//+------------------------------------------------------------------+
// ------------------------------------------------------------------
// 保存場所の名前(キー名)を作る関数
// 他のチャートと混ざらないように「チャートID」や「通貨ペア」を名前に含めます
// ------------------------------------------------------------------
string Get_GV_Key(const string name)
{
// 「MACD_Sys:チャートID:通貨ペア名:保存したい名前」という長い名前を作って返します
return("MACD_Sys:" + (string)ChartID() + ":" + _Symbol + ":" + name);
}
// ------------------------------------------------------------------
// スイッチの状態(ON/OFF)をMT5本体に保存する関数
// ------------------------------------------------------------------
void Set_GV_Bool(const string name, const bool v)
{
// ONなら1.0、OFFなら0.0という数字に変換して保存します(グローバル変数は数字しか保存できないため)
GlobalVariableSet(Get_GV_Key(name), v ? 1.0 : 0.0);
}
// ------------------------------------------------------------------
// MT5本体に保存してあるスイッチの状態(ON/OFF)を読み込む関数
// ------------------------------------------------------------------
bool Get_GV_Bool(const string name, const bool def)
{
string k = Get_GV_Key(name); // 読み込むための名前(キー名)を取得します
// 保存データが存在するか確認し、あればその値を(0.5より大きければONとみなして)返します
// 保存データがなければ、指定された初期値(def)を返します
return GlobalVariableCheck(k) ? (GlobalVariableGet(k) > 0.5) : def;
}
//+------------------------------------------------------------------+
//| UI設定の保存・読み込み |
//+------------------------------------------------------------------+
void Save_UI_State() // 現在のUIの状態(ボタンのON/OFF)を保存する関数
{
Set_GV_Bool("Panel_On_Off", Panel_On_Off); // パネル開閉状態を保存
Set_GV_Bool("Alert_On_Off", Alert_On_Off); // アラート状態を保存
Set_GV_Bool("Push_On_Off", Push_On_Off); // プッシュ通知状態を保存
Set_GV_Bool("Kakutei_On_Off", Kakutei_On_Off); // 確定足モード状態を保存
Set_GV_Bool("Mikakutei_On_Off", Mikakutei_On_Off); // 未確定足モード状態を保存
Set_GV_Bool("Sign_On_Off", Sign_On_Off); // サイン表示状態を保存
}
bool Load_UI_State() // 保存されたUIの状態を読み込む関数
{
if(!GlobalVariableCheck(Get_GV_Key("Mikakutei_On_Off"))) // 保存データ(未確定足設定)が存在しない場合
{
return(false); // falseを返す
}
Panel_On_Off = Get_GV_Bool("Panel_On_Off", true); // パネル開閉状態を読み込み(デフォルトtrue)
Alert_On_Off = Get_GV_Bool("Alert_On_Off", false); // アラート状態を読み込み(デフォルトfalse)
Push_On_Off = Get_GV_Bool("Push_On_Off", false); // プッシュ通知状態を読み込み(デフォルトfalse)
Kakutei_On_Off = Get_GV_Bool("Kakutei_On_Off", false); // 確定足モード状態を読み込み(デフォルトfalse)
Mikakutei_On_Off = Get_GV_Bool("Mikakutei_On_Off", true); // 未確定足モード状態を読み込み(デフォルトtrue)
Sign_On_Off = Get_GV_Bool("Sign_On_Off", true); // サイン表示状態を読み込み(デフォルトtrue)
return(true); // 読み込み成功としてtrueを返す
}
//+------------------------------------------------------------------+
//| UI部品(ボタン)作成関数 |
//+------------------------------------------------------------------+
// ボタンの表示更新
void Update_Button_Disp() // ボタンの表示状態を更新する関数
{
// サブウィンドウ番号の取得
int subwin = SubWindow_No; // 現在のサブウィンドウ番号を変数に入れる
if(subwin < 0) // 無効なら
{
subwin = ChartWindowFind(0, Indicator_Name); // 名前からウィンドウ番号を再検索
}
if(subwin < 0) // それでも見つからなければ
{
subwin = 0; // 0とする
}
// --- Openボタンの設定 ---
ObjectDelete(0, Open_Btn_Waku); // 既存のOpenボタン枠を削除
ObjectDelete(0, Open_Btn_Label); // 既存のOpenボタンラベルを削除
ObjectCreate(0, Open_Btn_Waku, OBJ_RECTANGLE_LABEL, subwin, 0, 0); // Openボタンの枠を作成
ObjectSetInteger(0, Open_Btn_Waku, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, Open_Btn_Waku, OBJPROP_XDISTANCE, 5); // X座標5
ObjectSetInteger(0, Open_Btn_Waku, OBJPROP_YDISTANCE, 20); // Y座標20
ObjectSetInteger(0, Open_Btn_Waku, OBJPROP_XSIZE, 75); // 幅75
ObjectSetInteger(0, Open_Btn_Waku, OBJPROP_YSIZE, 18); // 高さ18
ObjectSetInteger(0, Open_Btn_Waku, OBJPROP_COLOR, Waku_Color); // 枠色設定
ObjectSetInteger(0, Open_Btn_Waku, OBJPROP_BORDER_TYPE, BORDER_FLAT); // フラット枠
ObjectSetInteger(0, Open_Btn_Waku, OBJPROP_BGCOLOR, Panel_On_Off ? Open_On_Color : Btn_Off_Color); // ON/OFFに応じて背景色を設定
ObjectSetInteger(0, Open_Btn_Waku, OBJPROP_ZORDER, 1); // 重なり順1
ObjectCreate(0, Open_Btn_Label, OBJ_LABEL, subwin, 0, 0); // Openボタンの文字を作成
ObjectSetInteger(0, Open_Btn_Label, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, Open_Btn_Label, OBJPROP_ANCHOR, ANCHOR_CENTER); // 中央揃え
ObjectSetInteger(0, Open_Btn_Label, OBJPROP_XDISTANCE, 42); // X位置調整
ObjectSetInteger(0, Open_Btn_Label, OBJPROP_YDISTANCE, 28); // Y位置調整
ObjectSetInteger(0, Open_Btn_Label, OBJPROP_FONTSIZE, 8); // フォントサイズ8
ObjectSetInteger(0, Open_Btn_Label, OBJPROP_COLOR, Text_Color); // 文字色設定
ObjectSetString (0, Open_Btn_Label, OBJPROP_TEXT, "Open"); // テキストを"Open"に設定
ObjectSetInteger(0, Open_Btn_Label, OBJPROP_ZORDER, 2); // 重なり順2
if(!Panel_On_Off) // パネルがOFF(閉じている)場合
{
ObjectDelete(0, Alert_Btn_Waku); ObjectDelete(0, Alert_Btn_Label); // Alertボタンを削除
ObjectDelete(0, Push_Btn_Waku); ObjectDelete(0, Push_Btn_Label); // Pushボタンを削除
ObjectDelete(0, Kakutei_Btn_Waku); ObjectDelete(0, Kakutei_Btn_Label); // 確定足ボタンを削除
ObjectDelete(0, Mikakutei_Btn_Waku); ObjectDelete(0, Mikakutei_Btn_Label); // 未確定足ボタンを削除
ObjectDelete(0, Sign_Btn_Waku); ObjectDelete(0, Sign_Btn_Label); // サインボタンを削除
ChartRedraw(); // チャートを再描画
return; // 処理を終了
}
// --- Alertボタンの設定 ---
ObjectDelete(0, Alert_Btn_Waku); // 既存のAlertボタン枠を削除
ObjectDelete(0, Alert_Btn_Label); // 既存のAlertボタンラベルを削除
ObjectCreate(0, Alert_Btn_Waku, OBJ_RECTANGLE_LABEL, subwin, 0, 0); // Alertボタンの枠を作成
ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_XDISTANCE, 5); // X座標5
ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_YDISTANCE, 40); // Y座標40
ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_XSIZE, 75); // 幅75
ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_YSIZE, 18); // 高さ18
ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_COLOR, Waku_Color); // 枠色設定
ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_BORDER_TYPE, BORDER_FLAT); // フラット枠
ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_BGCOLOR, Alert_On_Off ? Alert_On_Color : Btn_Off_Color); // ON/OFFで背景色切替
ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_ZORDER, 1); // 重なり順1
ObjectCreate(0, Alert_Btn_Label, OBJ_LABEL, subwin, 0, 0); // Alertボタンの文字を作成
ObjectSetInteger(0, Alert_Btn_Label, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, Alert_Btn_Label, OBJPROP_ANCHOR, ANCHOR_CENTER); // 中央揃え
ObjectSetInteger(0, Alert_Btn_Label, OBJPROP_XDISTANCE, 42); // X位置調整
ObjectSetInteger(0, Alert_Btn_Label, OBJPROP_YDISTANCE, 48); // Y位置調整
ObjectSetInteger(0, Alert_Btn_Label, OBJPROP_FONTSIZE, 8); // フォントサイズ8
ObjectSetInteger(0, Alert_Btn_Label, OBJPROP_COLOR, Text_Color); // 文字色設定
ObjectSetString (0, Alert_Btn_Label, OBJPROP_TEXT, "Alert"); // テキストを"Alert"に設定
ObjectSetInteger(0, Alert_Btn_Label, OBJPROP_ZORDER, 2); // 重なり順2
// --- Pushボタンの設定 ---
ObjectDelete(0, Push_Btn_Waku); // 既存のPushボタン枠を削除
ObjectDelete(0, Push_Btn_Label); // 既存のPushボタンラベルを削除
ObjectCreate(0, Push_Btn_Waku, OBJ_RECTANGLE_LABEL, subwin, 0, 0); // Pushボタンの枠を作成
ObjectSetInteger(0, Push_Btn_Waku, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, Push_Btn_Waku, OBJPROP_XDISTANCE, 5); // X座標5
ObjectSetInteger(0, Push_Btn_Waku, OBJPROP_YDISTANCE, 60); // Y座標60
ObjectSetInteger(0, Push_Btn_Waku, OBJPROP_XSIZE, 75); // 幅75
ObjectSetInteger(0, Push_Btn_Waku, OBJPROP_YSIZE, 18); // 高さ18
ObjectSetInteger(0, Push_Btn_Waku, OBJPROP_COLOR, Waku_Color); // 枠色設定
ObjectSetInteger(0, Push_Btn_Waku, OBJPROP_BORDER_TYPE, BORDER_FLAT); // フラット枠
ObjectSetInteger(0, Push_Btn_Waku, OBJPROP_BGCOLOR, Push_On_Off ? Push_On_Color : Btn_Off_Color); // ON/OFFで背景色切替
ObjectSetInteger(0, Push_Btn_Waku, OBJPROP_ZORDER, 1); // 重なり順1
ObjectCreate(0, Push_Btn_Label, OBJ_LABEL, subwin, 0, 0); // Pushボタンの文字を作成
ObjectSetInteger(0, Push_Btn_Label, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, Push_Btn_Label, OBJPROP_ANCHOR, ANCHOR_CENTER); // 中央揃え
ObjectSetInteger(0, Push_Btn_Label, OBJPROP_XDISTANCE, 42); // X位置調整
ObjectSetInteger(0, Push_Btn_Label, OBJPROP_YDISTANCE, 68); // Y位置調整
ObjectSetInteger(0, Push_Btn_Label, OBJPROP_FONTSIZE, 8); // フォントサイズ8
ObjectSetInteger(0, Push_Btn_Label, OBJPROP_COLOR, Text_Color); // 文字色設定
ObjectSetString (0, Push_Btn_Label, OBJPROP_TEXT, "Push"); // テキストを"Push"に設定
ObjectSetInteger(0, Push_Btn_Label, OBJPROP_ZORDER, 2); // 重なり順2
// --- Kakuteiボタンの設定 ---
ObjectDelete(0, Kakutei_Btn_Waku); // 既存のKakuteiボタン枠を削除
ObjectDelete(0, Kakutei_Btn_Label); // 既存のKakuteiボタンラベルを削除
ObjectCreate(0, Kakutei_Btn_Waku, OBJ_RECTANGLE_LABEL, subwin, 0, 0); // Kakuteiボタンの枠を作成
ObjectSetInteger(0, Kakutei_Btn_Waku, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, Kakutei_Btn_Waku, OBJPROP_XDISTANCE, 5); // X座標5
ObjectSetInteger(0, Kakutei_Btn_Waku, OBJPROP_YDISTANCE, 80); // Y座標80
ObjectSetInteger(0, Kakutei_Btn_Waku, OBJPROP_XSIZE, 75); // 幅75
ObjectSetInteger(0, Kakutei_Btn_Waku, OBJPROP_YSIZE, 18); // 高さ18
ObjectSetInteger(0, Kakutei_Btn_Waku, OBJPROP_COLOR, Waku_Color); // 枠色設定
ObjectSetInteger(0, Kakutei_Btn_Waku, OBJPROP_BORDER_TYPE, BORDER_FLAT); // フラット枠
ObjectSetInteger(0, Kakutei_Btn_Waku, OBJPROP_BGCOLOR, Kakutei_On_Off ? Kakutei_On_Color : Btn_Off_Color); // ON/OFFで背景色切替
ObjectSetInteger(0, Kakutei_Btn_Waku, OBJPROP_ZORDER, 1); // 重なり順1
ObjectCreate(0, Kakutei_Btn_Label, OBJ_LABEL, subwin, 0, 0); // Kakuteiボタンの文字を作成
ObjectSetInteger(0, Kakutei_Btn_Label, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, Kakutei_Btn_Label, OBJPROP_ANCHOR, ANCHOR_CENTER); // 中央揃え
ObjectSetInteger(0, Kakutei_Btn_Label, OBJPROP_XDISTANCE, 42); // X位置調整
ObjectSetInteger(0, Kakutei_Btn_Label, OBJPROP_YDISTANCE, 88); // Y位置調整
ObjectSetInteger(0, Kakutei_Btn_Label, OBJPROP_FONTSIZE, 8); // フォントサイズ8
ObjectSetInteger(0, Kakutei_Btn_Label, OBJPROP_COLOR, Text_Color); // 文字色設定
ObjectSetString (0, Kakutei_Btn_Label, OBJPROP_TEXT, "Kakutei"); // テキストを"Kakutei"に設定
ObjectSetInteger(0, Kakutei_Btn_Label, OBJPROP_ZORDER, 2); // 重なり順2
// --- Mikakuteiボタンの設定 ---
ObjectDelete(0, Mikakutei_Btn_Waku); // 既存のMikakuteiボタン枠を削除
ObjectDelete(0, Mikakutei_Btn_Label); // 既存のMikakuteiボタンラベルを削除
ObjectCreate(0, Mikakutei_Btn_Waku, OBJ_RECTANGLE_LABEL, subwin, 0, 0); // Mikakuteiボタンの枠を作成
ObjectSetInteger(0, Mikakutei_Btn_Waku, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, Mikakutei_Btn_Waku, OBJPROP_XDISTANCE, 5); // X座標5
ObjectSetInteger(0, Mikakutei_Btn_Waku, OBJPROP_YDISTANCE, 100); // Y座標100
ObjectSetInteger(0, Mikakutei_Btn_Waku, OBJPROP_XSIZE, 75); // 幅75
ObjectSetInteger(0, Mikakutei_Btn_Waku, OBJPROP_YSIZE, 18); // 高さ18
ObjectSetInteger(0, Mikakutei_Btn_Waku, OBJPROP_COLOR, Waku_Color); // 枠色設定
ObjectSetInteger(0, Mikakutei_Btn_Waku, OBJPROP_BORDER_TYPE, BORDER_FLAT); // フラット枠
ObjectSetInteger(0, Mikakutei_Btn_Waku, OBJPROP_BGCOLOR, Mikakutei_On_Off ? Mikakutei_On_Color : Btn_Off_Color); // ON/OFFで背景色切替
ObjectSetInteger(0, Mikakutei_Btn_Waku, OBJPROP_ZORDER, 1); // 重なり順1
ObjectCreate(0, Mikakutei_Btn_Label, OBJ_LABEL, subwin, 0, 0); // Mikakuteiボタンの文字を作成
ObjectSetInteger(0, Mikakutei_Btn_Label, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, Mikakutei_Btn_Label, OBJPROP_ANCHOR, ANCHOR_CENTER); // 中央揃え
ObjectSetInteger(0, Mikakutei_Btn_Label, OBJPROP_XDISTANCE, 42); // X位置調整
ObjectSetInteger(0, Mikakutei_Btn_Label, OBJPROP_YDISTANCE, 108); // Y位置調整
ObjectSetInteger(0, Mikakutei_Btn_Label, OBJPROP_FONTSIZE, 8); // フォントサイズ8
ObjectSetInteger(0, Mikakutei_Btn_Label, OBJPROP_COLOR, Text_Color); // 文字色設定
ObjectSetString (0, Mikakutei_Btn_Label, OBJPROP_TEXT, "Mikakutei"); // テキストを"Mikakutei"に設定
ObjectSetInteger(0, Mikakutei_Btn_Label, OBJPROP_ZORDER, 2); // 重なり順2
// --- Signボタンの設定 ---
ObjectDelete(0, Sign_Btn_Waku); // 既存のSignボタン枠を削除
ObjectDelete(0, Sign_Btn_Label); // 既存のSignボタンラベルを削除
ObjectCreate(0, Sign_Btn_Waku, OBJ_RECTANGLE_LABEL, subwin, 0, 0); // Signボタンの枠を作成
ObjectSetInteger(0, Sign_Btn_Waku, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, Sign_Btn_Waku, OBJPROP_XDISTANCE, 5); // X座標5
ObjectSetInteger(0, Sign_Btn_Waku, OBJPROP_YDISTANCE, 120); // Y座標120
ObjectSetInteger(0, Sign_Btn_Waku, OBJPROP_XSIZE, 75); // 幅75
ObjectSetInteger(0, Sign_Btn_Waku, OBJPROP_YSIZE, 18); // 高さ18
ObjectSetInteger(0, Sign_Btn_Waku, OBJPROP_COLOR, Waku_Color); // 枠色設定
ObjectSetInteger(0, Sign_Btn_Waku, OBJPROP_BORDER_TYPE, BORDER_FLAT); // フラット枠
ObjectSetInteger(0, Sign_Btn_Waku, OBJPROP_BGCOLOR, Sign_On_Off ? Sign_On_Color : Btn_Off_Color); // ON/OFFで背景色切替
ObjectSetInteger(0, Sign_Btn_Waku, OBJPROP_ZORDER, 1); // 重なり順1
ObjectCreate(0, Sign_Btn_Label, OBJ_LABEL, subwin, 0, 0); // Signボタンの文字を作成
ObjectSetInteger(0, Sign_Btn_Label, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, Sign_Btn_Label, OBJPROP_ANCHOR, ANCHOR_CENTER); // 中央揃え
ObjectSetInteger(0, Sign_Btn_Label, OBJPROP_XDISTANCE, 42); // X位置調整
ObjectSetInteger(0, Sign_Btn_Label, OBJPROP_YDISTANCE, 128); // Y位置調整
ObjectSetInteger(0, Sign_Btn_Label, OBJPROP_FONTSIZE, 8); // フォントサイズ8
ObjectSetInteger(0, Sign_Btn_Label, OBJPROP_COLOR, Text_Color); // 文字色設定
ObjectSetString (0, Sign_Btn_Label, OBJPROP_TEXT, "Sign"); // テキストを"Sign"に設定
ObjectSetInteger(0, Sign_Btn_Label, OBJPROP_ZORDER, 2); // 重なり順2
ChartRedraw(); // チャートを再描画する
}
// UI削除関数
void Delete_All_Button() // 全てのボタンオブジェクトを削除する関数
{
int windows = (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL); // チャートのウィンドウ総数を取得
if(windows < 1) // ウィンドウ数が1未満なら
{
windows = 1; // 1にする
}
for(int w = 0; w < windows; w++) // 全ウィンドウに対してループ
{
int total = ObjectsTotal(0, w, -1); // そのウィンドウにあるオブジェクト総数を取得
for(int i = total - 1; i >= 0; i--) // オブジェクトを後ろから順に走査
{
string name = ObjectName(0, i, w, -1); // オブジェクト名を取得
if(StringFind(name, Button_Prefix) == 0) // 名前がボタンの接頭辞で始まる場合
{
ObjectDelete(0, name); // そのオブジェクトを削除する
}
}
}
}
//+------------------------------------------------------------------+
//| 初期化関数(インジケーターを入れた時に最初に動く) |
//+------------------------------------------------------------------+
int OnInit() // インジケーター初期化時に実行される関数
{
// ショートネーム設定
IndicatorSetString(INDICATOR_SHORTNAME, Indicator_Name); // インジケーターの短縮名を設定
Delete_All_Button(); // 既存のボタンを全て削除(重複防止)
// バッファ設定
SetIndexBuffer(0, MACD_Buffer, INDICATOR_DATA); // 0番バッファにMACD配列を割り当て
SetIndexBuffer(1, Signal_Buffer, INDICATOR_DATA); // 1番バッファにシグナル配列を割り当て
SetIndexBuffer(2, Histogram_Buffer, INDICATOR_DATA); // 2番バッファにヒストグラム配列を割り当て
SetIndexBuffer(3, Color_Buffer, INDICATOR_COLOR_INDEX); // 3番バッファに色管理配列を割り当て
SetIndexBuffer(4, GC_Sign_Buffer, INDICATOR_DATA); // 4番バッファにGCサイン配列を割り当て
SetIndexBuffer(5, DC_Sign_Buffer, INDICATOR_DATA); // 5番バッファにDCサイン配列を割り当て
// プロット設定
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); // 0番プロットの空値を設定
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE); // 1番プロットの空値を設定
PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE); // 2番プロットの空値を設定
PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, EMPTY_VALUE); // 3番プロットの空値を設定
PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, EMPTY_VALUE); // 4番プロットの空値を設定
// サイン形状設定(● = 159)
PlotIndexSetInteger(3, PLOT_ARROW, 159); // 3番プロット(GC)の矢印形状を設定
PlotIndexSetInteger(4, PLOT_ARROW, 159); // 4番プロット(DC)の矢印形状を設定
// サインの色設定を適用
PlotIndexSetInteger(3, PLOT_LINE_COLOR, Sign_Color_GC); // GCサインの色を適用
PlotIndexSetInteger(4, PLOT_LINE_COLOR, Sign_Color_DC); // DCサインの色を適用
// 時系列設定(0=最新)
ArraySetAsSeries(MACD_Buffer, true); // MACD配列を時系列逆順(0が最新)に設定
ArraySetAsSeries(Signal_Buffer, true); // シグナル配列を時系列逆順に設定
ArraySetAsSeries(Histogram_Buffer, true); // ヒストグラム配列を時系列逆順に設定
ArraySetAsSeries(Color_Buffer, true); // 色管理配列を時系列逆順に設定
ArraySetAsSeries(GC_Sign_Buffer, true); // GCサイン配列を時系列逆順に設定
ArraySetAsSeries(DC_Sign_Buffer, true); // DCサイン配列を時系列逆順に設定
// UIロード
if(!Load_UI_State()) // 保存されたUI状態の読み込みを試みる
{
Alert_On_Off = false; // 読み込み失敗時はアラートをOFF
Push_On_Off = false; // 読み込み失敗時はPushをOFF
Kakutei_On_Off = false; // 確定足OFF
Mikakutei_On_Off = true; // 未確定足ON
Panel_On_Off = true; // パネルON
}
// EMA ハンドル作成
FastEMA_Handle = iMA(NULL, 0, FastEMA_Kikan, 0, MODE_EMA, PRICE_CLOSE); // FastEMAのハンドルを作成
if(FastEMA_Handle == INVALID_HANDLE) // ハンドル作成に失敗した場合
{
Print("Fast EMA handle creation failed."); // エラーログを出力
return INIT_FAILED; // 初期化失敗を返す
}
SlowEMA_Handle = iMA(NULL, 0, SlowEMA_Kikan, 0, MODE_EMA, PRICE_CLOSE); // SlowEMAのハンドルを作成
if(SlowEMA_Handle == INVALID_HANDLE) // ハンドル作成に失敗した場合
{
Print("Slow EMA handle creation failed."); // エラーログを出力
if(FastEMA_Handle != INVALID_HANDLE) // FastEMAハンドルが有効なら
{
IndicatorRelease(FastEMA_Handle); // 解放する
FastEMA_Handle = INVALID_HANDLE; // 無効化する
}
return INIT_FAILED; // 初期化失敗を返す
}
return INIT_SUCCEEDED; // 初期化成功を返す
}
//+------------------------------------------------------------------+
//| 終了関数(インジケーターを消した時に動く) |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) // インジケーター終了時に実行される関数
{
Save_UI_State(); // 現在のUI状態を保存する
Delete_All_Button(); // 全てのボタンを削除する
if(FastEMA_Handle != INVALID_HANDLE) // FastEMAハンドルが有効なら
{
IndicatorRelease(FastEMA_Handle); // 解放する
}
if(SlowEMA_Handle != INVALID_HANDLE) // SlowEMAハンドルが有効なら
{
IndicatorRelease(SlowEMA_Handle); // 解放する
}
}
//+------------------------------------------------------------------+
//| イベントハンドラ(ボタンクリック時の処理) |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &l, const double &d, const string &s) // チャートイベントが発生した時に実行される関数
{
if(id == CHARTEVENT_OBJECT_CLICK) // クリックイベントの場合
{
bool Changed = false; // 設定変更があったかどうかを判定するフラグ
if(s == Open_Btn_Waku || s == Open_Btn_Label) // Openボタンがクリックされた場合
{
Panel_On_Off = !Panel_On_Off; // パネル開閉状態を反転
Update_Button_Disp(); // ボタン表示を更新
Save_UI_State(); // UI状態を保存
return; // 処理を終了
}
if(!Panel_On_Off) // パネルが閉じている場合
{
return; // 他のボタン処理をしない
}
if(s == Alert_Btn_Waku || s == Alert_Btn_Label) // Alertボタンがクリックされた場合
{
Alert_On_Off = !Alert_On_Off;
Changed = true; // アラート状態を反転し変更フラグを立てる
}
else if(s == Push_Btn_Waku || s == Push_Btn_Label) // Pushボタンがクリックされた場合
{
Push_On_Off = !Push_On_Off;
Changed = true; // Push通知状態を反転し変更フラグを立てる
}
else if(s == Kakutei_Btn_Waku || s == Kakutei_Btn_Label) // Kakuteiボタンがクリックされた場合
{
if(!Kakutei_On_Off) // 現在OFFなら
{
Kakutei_On_Off = true; Mikakutei_On_Off = false; Changed = true; // 確定足ON・未確定足OFFにして変更フラグを立てる
}
}
else if(s == Mikakutei_Btn_Waku || s == Mikakutei_Btn_Label) // Mikakuteiボタンがクリックされた場合
{
if(!Mikakutei_On_Off) // 現在OFFなら
{
Mikakutei_On_Off = true;
Kakutei_On_Off = false;
Changed = true; // 未確定足ON・確定足OFFにして変更フラグを立てる
}
}
else if(s == Sign_Btn_Waku || s == Sign_Btn_Label) // Signボタンがクリックされた場合
{
Sign_On_Off = !Sign_On_Off;
Changed = true; // サイン表示状態を反転し変更フラグを立てる
}
if(Changed) // 設定に変更があった場合
{
Update_Button_Disp(); // ボタン表示を更新
Save_UI_State(); // UI状態を保存
All_Keisan = true; // 再計算フラグを立てる
ChartRedraw(); // チャートを再描画
}
}
}
//+------------------------------------------------------------------+
//| バー本数取得(設定した表示期間の日数分) |
//+------------------------------------------------------------------+
int Get_Kako_Data_Count(const datetime &time[], const int rates_total)
{
// --- 1. データがあるか確認 ---
if(rates_total == 0)
{
return 0;
}
// --- 2. 基準となる時間を決める ---
datetime latest = time[0]; // 現在の時間
// --- 3. 設定値の読み込み ---
// ここで初期設定の「1年(365)」などが読み込まれます
int days = (int)Hyouji_Kikan;
// 設定値が0以下などでおかしい場合の安全装置
if(days <= 0)
{
days = 10;
}
// --- 4. 目標の日時を計算 ---
// 「今日から365日前」の日時を計算します
datetime cutoff = latest - (datetime)(days * 86400);
// --- 5. 場所の検索 ---
// 「その365日前の日付は、何番目のローソク足か?」を探します
int idx = iBarShift(_Symbol, _Period, cutoff, false);
// =========================================================
// ★【重要】ヒストリ対策(データ不足による事故防止)
// 例:1年分(2000本)欲しいのに、手元に1週間分(100本)しかない場合
// =========================================================
// 場所が見つからなかった場合の念のための処理
if(idx < 0)
{
idx = rates_total - 1;
}
// ★ここが最重要
// 計算の結果「2000番目の足(1年前)」が必要だと分かりましたが、
// 実際のデータ(rates_total)は「100本」しかありません。
// このまま「2000番目」を参照すると、存在しない場所なのでエラーで止まります。
if(idx > rates_total - 1)
{
// なので、強制的に「今ある限界の場所(100番目)」に書き換えます。
// これにより、1年分なくてもエラー落ちせず、ある分だけで動くようになります。
idx = rates_total - 1;
}
// --- 7. 結果を返す ---
// 0番から始まるので、本数にするため+1して返します
return(idx + 1);
}
//+------------------------------------------------------------------+
//| 計算関数(メイン処理) |
//+------------------------------------------------------------------+
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[]) // スプレッド配列
{
// --- 【ヒストリ対策 1】 最低限のデータ量チェック ---
// 例:長期EMAの設定が「26本分の平均」なのに、手元のデータが「5本」しかない場合。
// 計算しようとすると「データが足りない!」とエラーになるので、何もせず終了させます。
if(rates_total < MathMax(FastEMA_Kikan, SlowEMA_Kikan))
{
return 0; // データ不足のため計算中止
}
// --- 【ヒストリ対策 2】 過去データの自動ダウンロード機能 ---
// 目的:手元のデータが足りない時に、サーバーから強制的にデータを取り寄せる処理
// 「今」から「設定した期間(例:1年)」引いて、「1年前の日付」を計算します
datetime Target_Date = TimeCurrent() - ((int)Hyouji_Kikan * 86400);
// ★現状のチェック
// 「手元にある一番古いデータ」が、「1年前」より新しい場合(=1年前のデータが無い状態)。
// 例:1年前を見たいのに、手元には「先週まで」しかデータがない状態です。
if(rates_total > 0 && iTime(_Symbol, _Period, rates_total - 1) > Target_Date)
{
double Check_Array[]; // 読み込み用の空箱
// ★ダウンロードの誘発(ここが重要)
// MT5は「手元に無いデータ」をプログラムから要求すると、
// 自動的にサーバーへ取りに行ってダウンロードを始める性質があります。
// ここで「1年前のデータをくれ(CopyClose)」と命令することで、裏でダウンロードを開始させます。
if(CopyClose(_Symbol, _Period, Target_Date, 1, Check_Array) <= 0)
{
/* 取得に失敗しても、ダウンロードの「きっかけ」を作ることが目的なので、処理は止めずに続けます */
}
}
// サブウィンドウ取得
SubWindow_No = ChartWindowFind(0, Indicator_Name); // このインジケーターのサブウィンドウ番号を取得
static bool First_Run = true; // 初回実行フラグ(静的変数)
if(First_Run) // 初回実行時のみ
{
Update_Button_Disp(); // ボタン表示を更新
First_Run = false; // 初回フラグを下ろす
}
// ※このコードでは「時間(time)」しか直接使わないので、timeだけ設定しています。
if(!ArrayGetAsSeries(time)) // もし、まだ設定されていなければ
{
ArraySetAsSeries(time, true); // timeを「新しい順(Series)」にする
}
int Start_Bar_Index; // 計算開始位置のインデックス変数
if(All_Keisan) // 全再計算フラグが立っている場合
{
ArrayInitialize(MACD_Buffer, 0.0); // MACDバッファを初期化
ArrayInitialize(Signal_Buffer, 0.0); // シグナルバッファを初期化
ArrayInitialize(Histogram_Buffer, 0.0); // ヒストグラムバッファを初期化
ArrayInitialize(Color_Buffer, 0.0); // 色バッファを初期化
ArrayInitialize(GC_Sign_Buffer, EMPTY_VALUE); // GCサインバッファを空値で初期化
ArrayInitialize(DC_Sign_Buffer, EMPTY_VALUE); // DCサインバッファを空値で初期化
Start_Bar_Index = rates_total - MathMax(FastEMA_Kikan, SlowEMA_Kikan) - 1; // 計算可能な最古の位置を開始位置に設定
All_Keisan = false; // 再計算フラグを下ろす
} else { // 通常更新の場合
Start_Bar_Index = rates_total - prev_calculated; // 前回計算分から差分を開始位置に設定
if(Start_Bar_Index < 0) // 負の値なら
{
Start_Bar_Index = 0; // 0に補正
}
}
int Max_Bars = Get_Kako_Data_Count(time, rates_total); // 表示期間に応じたバー数を取得
int Limit_Bars = (Max_Bars > 0 ? MathMin(Max_Bars, rates_total) : rates_total); // バー数を全体の数以下に制限
int Display_Begin_Index = rates_total - Limit_Bars; // 表示開始インデックスを計算
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, Display_Begin_Index); // 0番プロットの描画開始位置を設定
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, Display_Begin_Index); // 1番プロットの描画開始位置を設定
PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, Display_Begin_Index); // 2番プロットの描画開始位置を設定
PlotIndexSetInteger(3, PLOT_DRAW_BEGIN, Display_Begin_Index); // 3番プロットの描画開始位置を設定
PlotIndexSetInteger(4, PLOT_DRAW_BEGIN, Display_Begin_Index); // 4番プロットの描画開始位置を設定
static int Zenkai_Begin = -1; // 前回の表示開始位置を保持する静的変数
// ---------------------------------------------------------
// ★【重要】過去データの「穴埋め」が必要かチェックする処理
// ---------------------------------------------------------
// 「1年表示」→「3年表示」に変更した時など、
// 今まで計算していなかった「古い過去(左側)」がいきなり必要になったかを判定します。
// ※「Display_Begin_Index」は「描画の開始地点」です。
// この数字が前回より小さくなった(<)ということは、
// スタート地点が「より古い過去(左側)」に移動したことを意味します。
bool Left_Kakudai = (Zenkai_Begin >= 0 && Display_Begin_Index < Zenkai_Begin);
// もし、計算していない過去のエリアが表示範囲に入ってきたら
// (=数値が0のまま放置されているエリアを表示することになったら)
if(Left_Kakudai)
{
// 1. 「全部計算し直して!」という旗を立てます
// 部分的な計算では穴埋めが難しいので、最初から計算し直して数値を埋めます。
All_Keisan = true;
// 2. 「ここからスタートするよ」と新しい位置を記憶します
// これを忘れると、毎回「位置が変わった!」と勘違いして無限ループになります。
Zenkai_Begin = Display_Begin_Index;
// 3. いったん処理を中断して、次の瞬間に「全計算(All_Keisan)」をスタートさせます
return prev_calculated;
}
// (変化がなければ)現在の開始位置を記憶して、次回のチェックに使います
Zenkai_Begin = Display_Begin_Index;
// ---------------------------------------------------------
// ★EMAの計算精度を保つための「助走区間(余白)」の計算
// ---------------------------------------------------------
// EMA(指数平滑移動平均)は「過去のデータ」の影響を受け続ける計算式です。
// そのため、表示したい期間のギリギリから計算を始めると、最初の頃の数値がズレてしまいます。
// 正しい数値を出すために、表示期間よりさらに昔から計算を始めるための「助走距離」を決めます。
int Limit_Index = Limit_Bars - 1; // 表示したい期間の端っこ(インデックス)
// 「期間×3倍」くらい余分に計算しておけば、誤差がなくなって数値が安定します(+100は念のため)
int Yohaku_Margin = MathMax(FastEMA_Kikan, SlowEMA_Kikan) * 3 + 100;
int Bar_Limit_Index = Limit_Index + Yohaku_Margin; // ここまで昔から計算スタートする(限界地点)
// 【ヒストリ対策】
// 計算しようとした場所が、チャート全体のデータ数(rates_total)より昔だった場合、
// エラーになるので「一番古い足」に強制的に合わせます。
if(Bar_Limit_Index >= rates_total)
{
Bar_Limit_Index = rates_total - 1; // 補正する
}
// 計算開始位置(Start_Bar_Index)が、決めた限界より昔にならないように調整
if(Start_Bar_Index > Bar_Limit_Index)
{
Start_Bar_Index = Bar_Limit_Index; // 補正する
}
// 【ヒストリ対策】
// 計算開始位置が「最新の足(データ不足)」すぎてもエラーの元なので、最低限の位置に補正
if(Start_Bar_Index > rates_total - 2)
{
Start_Bar_Index = rates_total - 2; // 補正する
}
double FastEMA_Array[],SlowEMA_Array[]; // 計算に使うための「空箱」を用意
// 配列の並び順を「0=最新」に設定(これをしないと計算が逆になる)
ArraySetAsSeries(FastEMA_Array, true);
ArraySetAsSeries(SlowEMA_Array, true);
// MT5本体からデータをコピーしてくる数(少し多めに確保)
int Copy_Count = Start_Bar_Index + 100;
// 【ヒストリ対策】
// 「1000本コピーして」と言ったのに「500本」しかデータがない場合のエラー防止
if(Copy_Count > rates_total)
{
Copy_Count = rates_total; // あるだけ(全部)に設定変更
}
// --- 計算準備ができているか確認 ---
// インジケーターを入れた直後は、MT5の裏側で計算が追いついていないことがあります。
int BC_Fast = BarsCalculated(FastEMA_Handle); // FastEMAの準備完了数
int BC_Slow = BarsCalculated(SlowEMA_Handle); // SlowEMAの準備完了数
// 必要な分だけ計算が終わっているかチェック
if(BC_Fast < Copy_Count || BC_Slow < Copy_Count)
{
return prev_calculated; // まだ準備中なので、今回は計算をあきらめて次回に回す
}
// ---------------------------------------------------------
// ★【重要】裏で計算されたデータを「自分の配列」に持ってくる
// ---------------------------------------------------------
// MT5の本体(Handle)が裏で計算し続けているEMAの数値を、
// このプログラム内で計算に使えるように「FastEMA_Array(箱)」へコピーします。
// ※これをしないと、手元にデータが無いので何も計算できません。
// ■短期EMA(Fast)のデータをコピー
// 「FastEMA_Handle(裏の計算機)」から「FastEMA_Array(自分の箱)」へ、
// 「Copy_Count(必要な個数)」だけデータを移し替えます。
// もしコピーに失敗したり、データが0個だったりした場合(<= 0)
if(CopyBuffer(FastEMA_Handle, 0, 0, Copy_Count, FastEMA_Array) <= 0)
{
return 0; // データが取れなかったので、今回は計算を諦めて終了します(エラー回避)
}
// ■長期EMA(Slow)のデータをコピー
// 同じように、長期線のデータも裏から持ってきます。
if(CopyBuffer(SlowEMA_Handle, 0, 0, Copy_Count, SlowEMA_Array) <= 0)
{
return 0; // 失敗なら終了
}
// シグナル線を計算するための係数(公式通りの数値)
double Alpha = 2.0 / (SignalEMA_Kikan + 1);
// ---------------------------------------------------------
// ★メイン計算ループ(ここが効率化のポイント!)
// ---------------------------------------------------------
// 「過去(Start_Bar_Index)」から「現在(0)」に向かって、1本ずつ計算を進めます。
// i-- なので、数字が減っていくカウントダウン方式です。
//
// 【重要:なぜPCが重くならないの?】
// 一見すると毎回大量に計算しているように見えますが、実は違います。
// この「Start_Bar_Index(開始位置)」という数字が、状況によって自動で変わるからです。
//
// ■初回だけ: Start_Bar_Index = 2000(例) → 2000回ループ(全部計算)
// ■2回目以降: Start_Bar_Index = 1 または 0 → 1回だけループ(増えた分だけ計算)
//
// つまり、「前回計算した続き(差分)」だけを処理しているので、
// チャートが動くたびに計算し直すような無駄がなく、非常に高速に動きます。
for(int i = Start_Bar_Index; i >= 0; i--)
{
// --- 表示範囲外の処理 ---
// 「計算限界(Bar_Limit_Index)」より古いデータは、表示しないので計算しません。
if(i > Bar_Limit_Index)
{
MACD_Buffer[i] = 0.0; // 数値が入っていると変な線が出るので「0」で消す
Signal_Buffer[i] = 0.0;
Histogram_Buffer[i] = 0.0;
GC_Sign_Buffer[i] = EMPTY_VALUE; // サインも消す
DC_Sign_Buffer[i] = EMPTY_VALUE;
continue; // 次のループ(1つ新しい足)へ進む
}
// --- MACDライン(メインの線)の計算と描画 ---
// 短期EMA - 長期EMA = MACD値
double MACD_Value = FastEMA_Array[i] - SlowEMA_Array[i];
// 計算結果をMACDバッファに代入
// (※この「=」で代入した瞬間に、チャート上に青いメイン線が描画されます)
MACD_Buffer[i] = MACD_Value;
// --- シグナル線の計算準備(1つ前の値を取得) ---
double Prev_Signal;
// 計算のスタート地点(一番古い過去)かどうかを判定
// EMAは「前回の値」が必要ですが、一番最初だけは「前回」が存在しません。
// なので、特例として「その時のMACD値」をそのままスタート値として使います。
if(i >= Bar_Limit_Index - 1 || i >= rates_total - MathMax(FastEMA_Kikan,SlowEMA_Kikan) - 5)
{
Prev_Signal = MACD_Value; // スタート地点用の初期化
} else {
// それ以外(2本目以降)は、1つ昔(i+1)のシグナル値を読み込みます
Prev_Signal = Signal_Buffer[i + 1];
}
// --- シグナルライン(基準線)の計算と描画 ---
// シグナルEMAの計算式(前回の値 + 平滑化定数 × 差分)
// 計算結果をシグナルバッファに代入
// (※ここに数値を入れることで、チャートに赤い点線が描画されます)
Signal_Buffer[i] = Prev_Signal + Alpha * (MACD_Value - Prev_Signal);
// --- ヒストグラム(棒グラフ)の計算と描画 ---
// MACD - シグナル = ヒストグラム(2本の線の隙間の広さ)
double Histogram_Value = MACD_Value - Signal_Buffer[i];
// 計算結果をヒストグラムバッファに代入
// (※ここに数値を入れることで、チャートに棒グラフが描画されます)
Histogram_Buffer[i] = Histogram_Value;
// --- ヒストグラムの色分け ---
// 「今回の値」と「1つ前の値」を比べて、増えているか減っているかで色を変えます
double Prev_Histogram_Value = (i < rates_total - 1) ? Histogram_Buffer[i + 1] : 0;
if(Histogram_Value >= 0) // プラス圏の場合
{
Color_Buffer[i] = (Histogram_Value >= Prev_Histogram_Value) ? 0 : 1;
} else { // マイナス圏の場合
Color_Buffer[i] = (Histogram_Value <= Prev_Histogram_Value) ? 1 : 0;
}
// --- サイン(矢印)の判定 ---
GC_Sign_Buffer[i] = EMPTY_VALUE; // いったんサイン無しにする
DC_Sign_Buffer[i] = EMPTY_VALUE;
if(i < rates_total - 2) // データ端でなければ判定開始
{
double Prev_MACD = MACD_Buffer[i+1]; // 1つ前のMACD
double Prev_Sig = Signal_Buffer[i+1]; // 1つ前のシグナル
// ゴールデンクロス(GC):前回は下だったMACDが、今回上抜けた
bool Is_GC = (Prev_MACD <= Prev_Sig && MACD_Value > Signal_Buffer[i]);
// デッドクロス(DC):前回は上だったMACDが、今回下抜けた
bool Is_DC = (Prev_MACD >= Prev_Sig && MACD_Value < Signal_Buffer[i]);
// 「確定足モード」の制限
// まだ動いている最中の足(i==0)では、サインを出しません
if(Kakutei_On_Off && i == 0)
{
Is_GC = false;
Is_DC = false;
}
// 条件を満たし、かつスイッチがONならサインを表示(バッファに値を入れる)
if(Is_GC && Sign_On_Off)
{
GC_Sign_Buffer[i] = Signal_Buffer[i];
}
if(Is_DC && Sign_On_Off)
{
DC_Sign_Buffer[i] = Signal_Buffer[i];
}
// --- アラート・通知の判定 ---
bool Trigger = false; // 通知するかどうかのフラグ
string Sign_Type = ""; // 通知のメッセージ
// パターンA:未確定足モード(0番の足で、動いている最中に通知)
if(Mikakutei_On_Off && i == 0)
{
if(Is_GC) { Trigger = true; Sign_Type = "Golden Cross (Mikakutei)"; }
if(Is_DC) { Trigger = true; Sign_Type = "Dead Cross (Mikakutei)"; }
}
// パターンB:確定足モード(1番の足=確定した直後の足で通知)
else if(Kakutei_On_Off && i == 1)
{
if(Is_GC) { Trigger = true; Sign_Type = "Golden Cross (kakutei)"; }
if(Is_DC) { Trigger = true; Sign_Type = "Dead Cross (kakutei)"; }
}
// 通知トリガーがONなら
if(Trigger)
{
// 同じ足で何度も通知しないように、最後に通知した時間をチェック
if(LastAlert_Time != time[i])
{
string Msg = StringFormat("[%s] MACD %s", _Symbol, Sign_Type); // メッセージ作成
if(Alert_On_Off) Alert(Msg); // アラート音
if(Push_On_Off) SendNotification(Msg); // スマホ通知
LastAlert_Time = time[i]; // 「この時間は通知済み」と記録する
}
}
}
}
return rates_total; // 「ここまで計算しました」とMT5に報告
}
//+------------------------------------------------------------------+
macd-alertはMT4/MT5専用のインジケーターになります。MT4/MT5のダウンロード方法や、インジケーターの導入方法は関連記事をご確認下さい。


※二次配布や販売、自作発言は禁止です。
※本ツールによる損害の責任は負いかねます。
※学習用としてご活用下さい。

