【MT4/MT5】MACDクロスでサイン&アラートのインジケーターを無料配布!コードも完全公開

MACDクロスでサイン&アラートインジケーターを無料配布!コードも完全公開 インジケーター

MACDがクロスした際にサインが出て、アラートとMT4/MT5のアプリにプッシュ通知が届くインジケーター「macd-alert」を無料配布しています。

macd-alertはコード(MT5版)を完全公開していますので、MQL5の学習にも役立ちます。

macd-alertの機能と使い方を紹介

macd-alertのチャート画面

macd-alertは、サブウィンドウに「MACD線」「シグナル線」「ヒストグラム(棒)」を表示し、さらに MACD線とシグナル線のクロス(交差)を●サインで表示します。

また、アラート、プッシュ通知(ON/OFF可能)で知らせる機能を持ち、判定を未確定足と確定足で切り替えることも可能です。

計算に使う「期間」

macd-alertの期間

設定画面で変更できる期間は次の3つになります。

短期EMA期間:12(初期値 12)

長期EMA期間:26(初期値 26)

シグナルEMA期間:9(初期値 9)

これは一般的に「標準MACD」と呼ばれる代表的な組み合わせで、短期と長期の差(勢い)を見て、さらにその差を平均化して判断しやすくするための構成です。

macd-alertは、内部的に次の順番で値を作っています。

手順①:短期EMA(12)を作る

終値を元に、期間12の指数平滑移動平均(EMA)を作ります。

手順②:長期EMA(26)を作る

同じく終値を元に、期間26のEMAを作ります。

手順③:MACD線を作る

MACD線は 短期EMA(12) − 長期EMA(26) です。

意味としては「短期の勢いが長期より強いか弱いか」を数値化したものです。

MACD線が上側に行きやすい:短期の動きが強くなっている

MACD線が下側に行きやすい:短期の動きが弱くなっている

手順④:シグナル線を作る(9)

シグナル線は、MACD線を元にした期間9のEMAです。

MACD線はブレやすいので、シグナル線で「基準」を作って交差を見やすくします。

手順⑤:ヒストグラム(棒)を作る

ヒストグラムは MACD線 − シグナル線 です。

つまり「2本の線の距離」を棒で表しています。

棒が大きい:MACD線とシグナル線の差が大きい(勢いが強い)

棒が小さい:差が小さい(勢いが弱い)

0付近:交差が近い(転換が起きやすい位置)

手順⑥:棒の色を変える(勢いの増減)

このインジケーターは、棒を2色で塗り分けています。

判定は「前の棒より勢いが増えたか/減ったか」を見ています(プラス圏・マイナス圏で比較方向が変わります)。

要するに、勢いが強まっている側/弱まっている側を色で区別する仕組みです。

クロス(交差)の判定方法(GC/DCが出る条件)

macd-alertのサインの交差

クロス判定は「前の足」と「今見ている足」を比較して決めています。

ゴールデンクロス(GC)

前の足では:MACD線がシグナル線以下

今の足では:MACD線がシグナル線より上

になった瞬間をGCとしています。

デッドクロス(DC)

前の足では:MACD線がシグナル線以上

今の足では:MACD線がシグナル線より下

になった瞬間をDCとしています。

サイン(●)は、ONのときにクロス箇所へ出ます(GC用・DC用で別色)。

「確定足/未確定足」モードの意味(どの足で判定するか)

ここが通知のタイミングに直結します。

未確定足モード(Mikakutei)

現在進行中の足(最新の足)でクロスを判定します

早く気付けますが、足が確定するまでに状況が変わり、クロスが消える可能性がありま

確定足モード(Kakutei)

確定した足で判定します

コード上は、確定足モードのとき「最新足ではサインを出さない」制御が入り、通知対象は「ひとつ前の足」になります。

通知(Alert / Push)の条件と重複防止

通知は「クロスが成立」かつ「未確定足or確定足」で発生します。

AlertがON

画面アラートを出す

PushがON

プッシュ通知を送る(MT5側の通知設定が必要)

※同じ時間の足で何度も通知しないように、最後に通知した足の時間を記録して重複を防いでいます。

表示期間の設定(過去をどこまで描くか)

macd-alertの表示期間

このインジケーターは、過去データを無制限に全部描かず、表示期間(日数)を指定できます。

選択肢は: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をダウンロード

macd-alertはMT4/MT5専用のインジケーターになります。MT4/MT5のダウンロード方法や、インジケーターの導入方法は関連記事をご確認下さい。

【簡単】MT4・MT5の導入~インジケーター導入方法を動画で紹介!
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をダウンロード

macd-alertはMT4/MT5専用のインジケーターになります。MT4/MT5のダウンロード方法や、インジケーターの導入方法は関連記事をご確認下さい。

【簡単】MT4・MT5の導入~インジケーター導入方法を動画で紹介!
MT4・MT5の導入方法~インジケーターの導入方法を動画付きで分かりやすく紹介しています。また、導入方以外にも、MT4・MT5の注意点やおすすめの業者も紹介しています。
綾瀬文也
綾瀬 文也

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

    コメント

    【PR】簡単5分で申し込み完了!
    業界最狭水準スプレッド!100円から取引できる
    松井証券FXへ
    【PR】簡単5分で申し込み完了!
    業界最狭水準スプレッド!100円から取引できる
    松井証券FXへ
    タイトルとURLをコピーしました