【無料配布】MT5でRSIが70・30をタッチした際にサインとアラートが出るインジケーター

RSIタッチでサイン!インジケーター無料配布 FX・バイナリー攻略

FXやバイナリーオプションで役立つ!

「RSIが70を超えた時」、「RSIが70を超えた後に70を下回った時」、「RSIが30を下回った時」、「「RSIが30を下回った後に30を超えた時」にサインとアラームが鳴るMT5専用インジケーター「RSIタッチの極み」を無料配布しています。

RSIタッチの極みはコードを公開しており、コードは全てコメント付きで理解しやすくなっています。

RSIタッチの極みの使い方

MT5の公式サイト

RSIタッチの極みはMT5専用のインジケーターになるのでまずは、MT5の公式サイトよりダウンロードを行い、インストールを完了させます。

次に「RSIタッチの極み」をダウンロードします。

ダウンロード完了後、MT5を立ち上げ「ファイル」⇒「データフォルダを開く」⇒「MQL5」⇒「Indicators」の中にkiwami-rsi-touch.ex5ファイルを入れます。

MT5再起動後にRSIタッチの極みを立ち上げます。

RSIタッチの極みの画面

RSIタッチの極みを立ち上げると5つのボタンとRSIが表示されます。

RSI70_IN(水色)ボタン

RSI70_INボタンはRSIが70を超えた時に水色のサイン(■)を出します。アラートボタンをオンにしているとアラートも鳴ります。

RSI70_OUT(橙色)ボタン

RSI70_OUTボタンはRSIが70を超えた後、RSIが70を下回った時に橙色(オレンジ色)のサイン(■)を出します。アラートボタンをオンにしているとアラートも鳴ります。

RSI30_IN(桃色)ボタン

RSI30_INボタンはRSIが30を下回った時に桃色(ピンク色)のサイン(■)を出します。アラートボタンをオンにしているとアラートも鳴ります。

RSI30_OUT(黄色)ボタン

RSI30_OUTボタンはRSIが30を下回った後、RSIが30を上回った時に黄色のサイン(■)を出します。アラートボタンをオンにしているとアラートも鳴ります。

アラートボタン

アラートボタンをオンにするとサインが出た際にアラートが鳴ります。

土日対策

RSIタッチの極みの土日対策

RSIタッチの極みは土日(チャートが止まっている場合)はボタンを押してもサインは出てきません。

土日に過去のサインを確認した場合は、インジケーターを立ち上げた際に出てくるポップアップの「インプットタブ」を選び土日対策をtrueに変更して下さい。

綾瀬文也
綾瀬 文也

使い方に関してはボタンを押すだけなので迷う事はありません。

RSIタッチの極みのコードを完全公開

RSIタッチの極み
コードを完全公開

RSIタッチの極みのコードを完全公開しています。全ての項目にコメントを付けていますので、MQL5初心者の方でも理解しやすくなっています。

コードは基本的に初心者向けに作っており、if文の{}などもあえて付けていますので、少しくどい書き方になっています。

コードはそのままコピペで利用できます。このサイトではサイズの都合上見づらいので、テキスト等の張り付けて確認される事をおすすめします。


//+------------------------------------------------------------------+
//|                                                      btn+rsi.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
   #property copyright "ayase tomoya"
   #property link      "https://www.mql5.com"
   #property version   "1.00"
   #property indicator_chart_window
   
   // 使用するバッファ数とプロット数を決める
   #property indicator_buffers 5 // 使用するバッファ数
   #property indicator_plots 4 // 使用するプロット数
   
   // ---- RSI70_INのサイン
   #property indicator_label1  "RSI70_INのサイン" // RSI70_INのサインのラベル名
   #property indicator_type1   DRAW_ARROW // RSI70_INのサイン(矢印)
   #property indicator_color1  clrDeepSkyBlue // RSI70_INの矢印の色(水色)
   #property indicator_width1  2 // RSI70_INの太さ
   
   // ---- RSI70_OUTのサイン
   #property indicator_label2  "RSI70_OUTのサイン" // RSI70_OUTのサインのラベル名
   #property indicator_type2   DRAW_ARROW // RSI70_OUTのサイン(矢印)
   #property indicator_color2  clrOrangeRed // RSI70_OUTの矢印の色(オレンジ)
   #property indicator_width2  2 // RSI70_OUTの太さ
   
   // ---- RSI30_INのサイン
   #property indicator_label3  "RSI30_INのサイン" // RSI30_INのサインのラベル名
   #property indicator_type3   DRAW_ARROW // RSI30_INのサイン(矢印)
   #property indicator_color3  clrPink // RSI30_INの矢印の色(ピンク)
   #property indicator_width3  2 // RSI30_INの太さ
   
   // ---- RSI30_OUTのサイン
   #property indicator_label4  "RSI30_OUTのサイン" // RSI30_OUTのサインのラベル名
   #property indicator_type4   DRAW_ARROW // RSI30_OUTのサイン(矢印)
   #property indicator_color4  clrYellow // RSI30_OUTの矢印の色(黄色)
   #property indicator_width4  2 // RSI30_OUTの太さ    
   
   // ボタンとボタン文字(ラベル)の設定
   string RSI70_IN_Btn_Waku = "RSI70_IN ボタンの枠"; // ボタンの枠のオブジェクト名前
   string RSI70_IN_Btn_Label  = "RSI70_IN ボタンの文字"; // ボタンの文字(ラベル)のオブジェクト名
   string RSI70_OUT_Btn_Waku = "RSI70_OUT ボタンの枠"; // ボタンの枠のオブジェクト名前
   string RSI70_OUT_Btn_Label  = "RSI70_OUT ボタンの文字"; // ボタンの文字(ラベル)のオブジェクト名
   
   string RSI30_IN_Btn_Waku = "RSI30_IN ボタンの枠"; // ボタンの枠のオブジェクト名前
   string RSI30_IN_Btn_Label  = "RSI30_IN ボタンの文字"; // ボタンの文字(ラベル)のオブジェクト名
   string RSI30_OUT_Btn_Waku = "RSI30_OUT ボタンの枠"; // ボタンの枠のオブジェクト名前
   string RSI30_OUT_Btn_Label  = "RSI30_OUT ボタンの文字"; // ボタンの文字(ラベル)のオブジェクト名
   
   string Alert_Btn_Waku = "ABtn_rect";  // ボタンの枠のオブジェクト名前
   string Alert_Btn_Label  = "ABtn_label"; // ボタンの文字(ラベル)のオブジェクト名
   
   // RSIの期間
   input int    RSI_Kikan   = 14; // RSI期間
   double RSI70_IN_Level  = 70.0; // RSI70_INのLevel
   double RSI70_OUT_Level  = 70.0; // RSI70_OUTのLevel
   double RSI30_IN_Level  = 30.0; // RSI30_INのLevel
   double RSI30_OUT_Level  = 30.0; // RSI30_OUTのLevel     
   
   // RSIのハンドル
   int RSI_Handle = INVALID_HANDLE; // RSIハンドル
   
   // バッファ用の配列を作成
   double RSI_Buffer[]; // RSIバッファ用の配列
   double RSI70_IN_Sign_Buffer[]; // RSI70_INのバッファ用の配列
   double RSI70_OUT_Sign_Buffer[]; // RSI70_OUTのバッファ用の配列
   double RSI30_IN_Sign_Buffer[]; // RSI30_INのバッファ用の配列
   double RSI30_OUT_Sign_Buffer[]; // RSI30_OUTのバッファ用の配列
   
   // 真偽値
   bool RSI70_IN_On_Off = false; // 「RSI70_IN」のON/OFFを保持する
   bool RSI70_OUT_On_Off = false; // 「RSI70_OUT」のON/OFFを保持する
   bool RSI30_IN_On_Off = false; // 「RSI30_IN」のON/OFFを保持する
   bool RSI30_OUT_On_Off = false; // 「RSI30_OUT」のON/OFFを保持する
   bool Alert_On_Off   = true; // アラートON/OFF
   input bool ShowRSI = true;   // 起動時にデフォルトRSIを表示するか
   bool All_Keisan = false; // 全部再計算したい時にオンにする合図、初期状態はオフ(false)      

   // 各RSIシグナルで「最後にアラートを出した確定足の時刻」を保持(同一バーでの多重通知防止)。0 は未通知を意味する
   datetime LastAlertTime70IN  = 0;  // RSI70_IN(<=70→>70)の最終アラート時刻
   datetime LastAlertTime70OUT = 0;  // RSI70_OUT(>70→<=70)の最終アラート時刻
   datetime LastAlertTime30IN  = 0;  // RSI30_IN(>=30→<30)の最終アラート時刻
   datetime LastAlertTime30OUT = 0;  // RSI30_OUT(<=30→>30)の最終アラート時刻
   
   // ATRの設定
   double ATR_Buffer[]; // ATRバッファ用の配列
   int ATR_Handle = INVALID_HANDLE; //、「ATRを使うための番号(ハンドル)をまだ持っていない」と設定
   input int ATR_Kikan = 50;   // ATRの期間
   
   input bool Doniti = false;  // 【土日対策】false:平日、true:土日


//+------------------------------------------------------------------+
//| 初期設定                      |
//+------------------------------------------------------------------+
int OnInit()
  {
  
      // 配列のカウントを0=最新に統一
      ArraySetAsSeries(RSI_Buffer, true); // RSIのバッファを0=最新に統一
      ArraySetAsSeries(RSI70_IN_Sign_Buffer, true); // RSI70_INのバッファを0=最新に統一
      ArraySetAsSeries(RSI70_OUT_Sign_Buffer, true); // RSI70_OUTのバッファを0=最新に統一
      ArraySetAsSeries(RSI30_IN_Sign_Buffer, true); // RSI30_INのバッファを0=最新に統一
      ArraySetAsSeries(RSI30_OUT_Sign_Buffer, true); // RSI30_OUTのバッファを0=最新に統一       
      ArraySetAsSeries(ATR_Buffer, true); // ATRのバッファを0=最新に統一     
   
      //バッファの割り当て
      SetIndexBuffer(0,RSI70_IN_Sign_Buffer,INDICATOR_DATA); // RSI70_INのサイン INDICATOR_DATAは表示用
      SetIndexBuffer(1,RSI70_OUT_Sign_Buffer,INDICATOR_DATA); // RSI70_OUTのサイン INDICATOR_DATAは表示用
      SetIndexBuffer(2,RSI30_IN_Sign_Buffer,INDICATOR_DATA); // RSI30_INのサイン INDICATOR_DATAは表示用
      SetIndexBuffer(3,RSI30_OUT_Sign_Buffer,INDICATOR_DATA); // RSI30_OUTのサイン INDICATOR_DATAは表示用      
      SetIndexBuffer(4,RSI_Buffer,INDICATOR_CALCULATIONS); // RSIバッファ用の配列にバッファを割り当てる INDICATOR_CALCULATIONSは計算用
      
      // 起動直後のゴミ描画防止(残像対策)
      ArrayInitialize(RSI70_IN_Sign_Buffer,  EMPTY_VALUE);
      ArrayInitialize(RSI70_OUT_Sign_Buffer, EMPTY_VALUE);
      ArrayInitialize(RSI30_IN_Sign_Buffer,  EMPTY_VALUE);
      ArrayInitialize(RSI30_OUT_Sign_Buffer, EMPTY_VALUE);      
   
      // プロットの割り当て 
      PlotIndexSetInteger(0, PLOT_ARROW, 110);               // RSI70_IN用のサイン
      PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);  // データがないときに、サインを出ないようにするための処理
      PlotIndexSetInteger(1, PLOT_ARROW, 110);               // RSI70_OUT用のサイン
      PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);  // データがないときに、サインを出ないようにするための処理
      PlotIndexSetInteger(2, PLOT_ARROW, 110);               // RSI30_IN用のサイン
      PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE);  // データがないときに、サインを出ないようにするための処理
      PlotIndexSetInteger(3, PLOT_ARROW, 110);               // RSI30_OUT用のサイン
      PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, EMPTY_VALUE);  // データがないときに、サインを出ないようにするための処理       
      
      // ATRインジケータのハンドルを取得し、失敗時は初期化を中止する処理
      ATR_Handle = iATR(_Symbol, _Period, ATR_Kikan);   // ハンドル番号を変数に入れる
      if(ATR_Handle == INVALID_HANDLE) // もし作成に失敗したら(番号が無効なら)
      {         
         Print("ATRハンドル作成に失敗");         // エラーをメッセージに出してインジケータの初期化を中止する
         return INIT_FAILED; // OnInitを失敗で即時終了させて「未起動のまま」にする処理値
      }
       
      // 現在の銘柄と時間足で指定期間(RSI_Kikan「14」)のRSIを使うためのハンドルを作成し、もし作成に失敗したらエラーメッセージを表示して初期化を中止する処理
      RSI_Handle = iRSI(_Symbol, _Period, RSI_Kikan, PRICE_CLOSE);
      if(RSI_Handle == INVALID_HANDLE)
      {
         Print("RSI ハンドル作成失敗");
         return(INIT_FAILED); //OnInitを失敗で即時終了させて“未起動のまま”にする処理値
      }      
   
      //  ボタン・テキストボックスの生成   
      RSI70_INCreateButton();  // 「RSI70_IN」ボタンを生成
      RSI70_OUTCreateButton();  // 「RSI70_OUT」ボタンを生成
      RSI30_INCreateButton();  // 「RSI30_IN」ボタンを生成
      RSI30_OUTCreateButton();  // 「RSI30_OUT」ボタンを生成
      AlertCreateButton();  // アラートボタンを生成

      // インジケーター起動時にRSIを表示させる      
      if(ShowRSI && RSI_Handle != INVALID_HANDLE)
      {
         ChartIndicatorAdd(0, 1, RSI_Handle);  // サブウィンドウ1へ(最初の0はメインチャート、次の1はサブウィンドウ)追加
      }

      
      // ① 直近で確定した足(インデックス1)の時刻を取得します。0は未確定足、1は直近で確定した足でこれを「既読の基準時刻」として使います。
      // iTimeは、指定したシンボルと時間足における「バー(足)の開始時刻」を返す関数
      datetime _base = iTime(_Symbol, _Period, 1);      
      
      // ② 起動直後に古いバーでアラートが鳴らないよう、各シグナルの「最後にアラートした時刻」を直近確定足に
      LastAlertTime70IN  = _base;  // RSI70_IN の最終アラート時刻
      LastAlertTime70OUT = _base;  // RSI70_OUT の最終アラート時刻
      LastAlertTime30IN  = _base;  // RSI30_IN の最終アラート時刻
      LastAlertTime30OUT = _base;  // RSI30_OUT の最終アラート時刻   
               
      return(INIT_SUCCEEDED);
  }
  
/* ===================================================================== 
   ボタン・テキストボックス作成用関数
   ===================================================================== */
   
   // 「RSI70_IN」ボタンの作成
   void RSI70_INCreateButton()
   {
      ObjectDelete(0, RSI70_IN_Btn_Waku); // 既存のラベル削除してから作る
      ObjectCreate(0, RSI70_IN_Btn_Waku, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 四角形ラベルを作成
      ObjectSetInteger(0, RSI70_IN_Btn_Waku, OBJPROP_CORNER,    CORNER_LEFT_UPPER); // 左上に配置
      ObjectSetInteger(0, RSI70_IN_Btn_Waku, OBJPROP_XDISTANCE, 20); // X(横)方向の距離
      ObjectSetInteger(0, RSI70_IN_Btn_Waku, OBJPROP_YDISTANCE, 20); // Y(縦)方向の距離
      ObjectSetInteger(0, RSI70_IN_Btn_Waku, OBJPROP_XSIZE,     160); // 横サイズ
      ObjectSetInteger(0, RSI70_IN_Btn_Waku, OBJPROP_YSIZE,     24); // 縦サイズ
      ObjectSetInteger(0, RSI70_IN_Btn_Waku, OBJPROP_COLOR,     clrBlack); // 枠線の色
      ObjectSetInteger(0, RSI70_IN_Btn_Waku, OBJPROP_BGCOLOR,   RSI70_IN_On_Off ? clrLime : clrWhite); // ON/OFFで背景色変更

      ObjectDelete(0, RSI70_IN_Btn_Label); // 既存のラベル削除してから作る      
      ObjectCreate(0, RSI70_IN_Btn_Label, OBJ_LABEL, 0, 0, 0); // ラベルを作成
      ObjectSetInteger(0, RSI70_IN_Btn_Label, OBJPROP_CORNER,    CORNER_LEFT_UPPER); // 左上に配置
      ObjectSetInteger(0, RSI70_IN_Btn_Label, OBJPROP_XDISTANCE, 45); // X(横)方向の距離
      ObjectSetInteger(0, RSI70_IN_Btn_Label, OBJPROP_YDISTANCE, 25); // Y(縦)方向の距離
      ObjectSetInteger(0, RSI70_IN_Btn_Label, OBJPROP_FONTSIZE,  10); // フォントサイズ
      ObjectSetInteger(0, RSI70_IN_Btn_Label, OBJPROP_COLOR,     clrBlack); // 文字色
      ObjectSetString (0, RSI70_IN_Btn_Label, OBJPROP_TEXT,      RSI70_IN_On_Off ? "RSI70_IN (水色)☑" : "RSI70_IN (水色)"); // テキスト表示
   }
   
   // 「RSI70_OUT」ボタンの作成
   void RSI70_OUTCreateButton() 
   {
      ObjectDelete(0, RSI70_OUT_Btn_Waku); // 既存のラベル削除してから作る
      ObjectCreate(0, RSI70_OUT_Btn_Waku, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 四角形ラベルを作成
      ObjectSetInteger(0, RSI70_OUT_Btn_Waku, OBJPROP_CORNER,    CORNER_LEFT_UPPER); // 左上に配置
      ObjectSetInteger(0, RSI70_OUT_Btn_Waku, OBJPROP_XDISTANCE, 20); // X(横)方向の距離
      ObjectSetInteger(0, RSI70_OUT_Btn_Waku, OBJPROP_YDISTANCE, 50); // Y(縦)方向の距離
      ObjectSetInteger(0, RSI70_OUT_Btn_Waku, OBJPROP_XSIZE,     160); // 横サイズ
      ObjectSetInteger(0, RSI70_OUT_Btn_Waku, OBJPROP_YSIZE,     24); // 縦サイズ
      ObjectSetInteger(0, RSI70_OUT_Btn_Waku, OBJPROP_COLOR,     clrBlack); // 枠線の色
      ObjectSetInteger(0, RSI70_OUT_Btn_Waku, OBJPROP_BGCOLOR,   RSI70_OUT_On_Off ? clrLime : clrWhite); // ON/OFFで背景色変更

      ObjectDelete(0, RSI70_OUT_Btn_Label); // 既存のラベル削除してから作る      
      ObjectCreate(0, RSI70_OUT_Btn_Label, OBJ_LABEL, 0, 0, 0); // ラベルを作成
      ObjectSetInteger(0, RSI70_OUT_Btn_Label, OBJPROP_CORNER,    CORNER_LEFT_UPPER); // 左上に配置
      ObjectSetInteger(0, RSI70_OUT_Btn_Label, OBJPROP_XDISTANCE, 45); // X(横)方向の距離
      ObjectSetInteger(0, RSI70_OUT_Btn_Label, OBJPROP_YDISTANCE, 55); // Y(縦)方向の距離
      ObjectSetInteger(0, RSI70_OUT_Btn_Label, OBJPROP_FONTSIZE,  10); // フォントサイズ
      ObjectSetInteger(0, RSI70_OUT_Btn_Label, OBJPROP_COLOR,     clrBlack); // 文字色
      ObjectSetString (0, RSI70_OUT_Btn_Label, OBJPROP_TEXT,      RSI70_OUT_On_Off ? "RSI70_OUT (橙色)☑" : "RSI70_OUT (橙色)"); // テキスト表示
   }
   
   // 「RSI30_IN」ボタンの作成
   void RSI30_INCreateButton()
   {
      ObjectDelete(0, RSI30_IN_Btn_Waku); // 既存のラベル削除してから作る
      ObjectCreate(0, RSI30_IN_Btn_Waku, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 四角形ラベルを作成
      ObjectSetInteger(0, RSI30_IN_Btn_Waku, OBJPROP_CORNER,    CORNER_LEFT_UPPER); // 左上に配置
      ObjectSetInteger(0, RSI30_IN_Btn_Waku, OBJPROP_XDISTANCE, 20); // X(横)方向の距離
      ObjectSetInteger(0, RSI30_IN_Btn_Waku, OBJPROP_YDISTANCE, 80); // Y(縦)方向の距離
      ObjectSetInteger(0, RSI30_IN_Btn_Waku, OBJPROP_XSIZE,     160); // 横サイズ
      ObjectSetInteger(0, RSI30_IN_Btn_Waku, OBJPROP_YSIZE,     24); // 縦サイズ
      ObjectSetInteger(0, RSI30_IN_Btn_Waku, OBJPROP_COLOR,     clrBlack); // 枠線の色
      ObjectSetInteger(0, RSI30_IN_Btn_Waku, OBJPROP_BGCOLOR,   RSI30_IN_On_Off ? clrLime : clrWhite); // ON/OFFで背景色変更

      ObjectDelete(0, RSI30_IN_Btn_Label); // 既存のラベル削除してから作る      
      ObjectCreate(0, RSI30_IN_Btn_Label, OBJ_LABEL, 0, 0, 0); // ラベルを作成
      ObjectSetInteger(0, RSI30_IN_Btn_Label, OBJPROP_CORNER,    CORNER_LEFT_UPPER); // 左上に配置
      ObjectSetInteger(0, RSI30_IN_Btn_Label, OBJPROP_XDISTANCE, 45); // X(横)方向の距離
      ObjectSetInteger(0, RSI30_IN_Btn_Label, OBJPROP_YDISTANCE, 85); // Y(縦)方向の距離
      ObjectSetInteger(0, RSI30_IN_Btn_Label, OBJPROP_FONTSIZE,  10); // フォントサイズ
      ObjectSetInteger(0, RSI30_IN_Btn_Label, OBJPROP_COLOR,     clrBlack); // 文字色
      ObjectSetString (0, RSI30_IN_Btn_Label, OBJPROP_TEXT,      RSI30_IN_On_Off ? "RSI30_IN (桃色)☑" : "RSI30_IN (桃色)"); // テキスト表示
   }
   
   // 「RSI30_OUT」ボタンの作成
   void RSI30_OUTCreateButton() 
   {
      ObjectDelete(0, RSI30_OUT_Btn_Waku); // 既存のラベル削除してから作る
      ObjectCreate(0, RSI30_OUT_Btn_Waku, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 四角形ラベルを作成
      ObjectSetInteger(0, RSI30_OUT_Btn_Waku, OBJPROP_CORNER,    CORNER_LEFT_UPPER); // 左上に配置
      ObjectSetInteger(0, RSI30_OUT_Btn_Waku, OBJPROP_XDISTANCE, 20); // X(横)方向の距離
      ObjectSetInteger(0, RSI30_OUT_Btn_Waku, OBJPROP_YDISTANCE, 110); // Y(縦)方向の距離
      ObjectSetInteger(0, RSI30_OUT_Btn_Waku, OBJPROP_XSIZE,     160); // 横サイズ
      ObjectSetInteger(0, RSI30_OUT_Btn_Waku, OBJPROP_YSIZE,     24); // 縦サイズ
      ObjectSetInteger(0, RSI30_OUT_Btn_Waku, OBJPROP_COLOR,     clrBlack); // 枠線の色
      ObjectSetInteger(0, RSI30_OUT_Btn_Waku, OBJPROP_BGCOLOR,   RSI30_OUT_On_Off ? clrLime : clrWhite); // ON/OFFで背景色変更

      ObjectDelete(0, RSI30_OUT_Btn_Label); // 既存のラベル削除してから作る      
      ObjectCreate(0, RSI30_OUT_Btn_Label, OBJ_LABEL, 0, 0, 0); // ラベルを作成
      ObjectSetInteger(0, RSI30_OUT_Btn_Label, OBJPROP_CORNER,    CORNER_LEFT_UPPER); // 左上に配置
      ObjectSetInteger(0, RSI30_OUT_Btn_Label, OBJPROP_XDISTANCE, 45); // X(横)方向の距離
      ObjectSetInteger(0, RSI30_OUT_Btn_Label, OBJPROP_YDISTANCE, 115); // Y(縦)方向の距離
      ObjectSetInteger(0, RSI30_OUT_Btn_Label, OBJPROP_FONTSIZE,  10); // フォントサイズ
      ObjectSetInteger(0, RSI30_OUT_Btn_Label, OBJPROP_COLOR,     clrBlack); // 文字色
      ObjectSetString (0, RSI30_OUT_Btn_Label, OBJPROP_TEXT,      RSI30_OUT_On_Off ? "RSI30_OUT (黄色)☑" : "RSI30_OUT (黄色)"); // テキスト表示
   }
   
   // 「アラート」ボタンのON/OFFボタンを作成
   void AlertCreateButton()
   {
      ObjectDelete(0, Alert_Btn_Waku); // 既存削除
      ObjectCreate(0, Alert_Btn_Waku, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 矩形作成
   
      ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_CORNER,    CORNER_LEFT_UPPER);  // 左上に配置
      ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_XDISTANCE, 20); // X(横)方向の距離
      ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_YDISTANCE, 140); // Y(縦)方向の距離
      ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_XSIZE,     90); // 横サイズ
      ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_YSIZE,     24); // 縦サイズ
      ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_COLOR,     clrBlack); // 枠線の色
      ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_BGCOLOR,   Alert_On_Off ? clrLime : clrWhite); // ON/OFFで背景色変更
   
      ObjectDelete(0, Alert_Btn_Label); // 既存ラベル削除
      ObjectCreate(0, Alert_Btn_Label, OBJ_LABEL, 0, 0, 0); // ラベルを作成 
      ObjectSetInteger(0, Alert_Btn_Label, OBJPROP_CORNER,    CORNER_LEFT_UPPER); // 左上に配置
      ObjectSetInteger(0, Alert_Btn_Label, OBJPROP_XDISTANCE, 45); // X(横)方向の距離
      ObjectSetInteger(0, Alert_Btn_Label, OBJPROP_YDISTANCE, 145); // Y(縦)方向の距離
      ObjectSetInteger(0, Alert_Btn_Label, OBJPROP_FONTSIZE,  10); // フォントサイズ
      ObjectSetInteger(0, Alert_Btn_Label, OBJPROP_COLOR,     clrBlack); // 文字色
      ObjectSetString (0, Alert_Btn_Label, OBJPROP_TEXT,      Alert_On_Off ? "アラート☑" : "アラート"); // テキスト表示
   }     
   
   
/* ===================================================================== 
   クリックのイベント処理
   ===================================================================== */ 
void OnChartEvent(const int id, const long &l, const double &d, const string &s) // チャートイベント処理
{
   
   //  「RSI70_IN」ボタンのクリック処理  
   if(id==CHARTEVENT_OBJECT_CLICK && (s==RSI70_IN_Btn_Waku || s==RSI70_IN_Btn_Label)) // ボタンの枠かボタンのラベル(文字)がクリックされたら
   {
      
      RSI70_IN_On_Off = !RSI70_IN_On_Off; // オンオフの切替

      // ボタンを押した後、一度全体を見る為の判定フラグ
      if(RSI70_IN_On_Off)
      {
         All_Keisan = true;
      }
      
      // 見た目を即時反映
      ObjectSetString (0, RSI70_IN_Btn_Label,  OBJPROP_TEXT,  RSI70_IN_On_Off ? "RSI70_IN (水色)☑" : "RSI70_IN (水色)"); // 文字を変更
      ObjectSetInteger(0, RSI70_IN_Btn_Waku, OBJPROP_BGCOLOR, RSI70_IN_On_Off ? clrLime : clrWhite); // ボタンの色を変更
        
      // 土日対策    
      if (Doniti)
      {
         ChartSetSymbolPeriod(0, _Symbol, Period());
      }        
         
      ChartRedraw(); // 再描画
      return;
   }
   
   //  「RSI70_OUT」ボタンのクリック処理  
   if(id==CHARTEVENT_OBJECT_CLICK && (s==RSI70_OUT_Btn_Waku || s==RSI70_OUT_Btn_Label)) // ボタンの枠かボタンのラベル(文字)がクリックされたら
   {
      
      RSI70_OUT_On_Off = !RSI70_OUT_On_Off; // オンオフの切替
      
      // ボタンを押した後、一度全体を見る為の判定フラグ      
      if(RSI70_OUT_On_Off)
      {
      All_Keisan = true;
      }      
            
      // 見た目を即時反映
      ObjectSetString (0, RSI70_OUT_Btn_Label,  OBJPROP_TEXT,  RSI70_OUT_On_Off ? "RSI70_OUT (橙色)☑" : "RSI70_OUT (橙色)"); // 文字を変更
      ObjectSetInteger(0, RSI70_OUT_Btn_Waku, OBJPROP_BGCOLOR, RSI70_OUT_On_Off ? clrLime : clrWhite); // ボタンの色を変更
         
      // 土日対策    
      if (Doniti)
      {
         ChartSetSymbolPeriod(0, _Symbol, Period());
      }   

      ChartRedraw(); // 再描画
      return;
   }
   
   //  「RSI30_IN」ボタンのクリック処理  
   if(id==CHARTEVENT_OBJECT_CLICK && (s==RSI30_IN_Btn_Waku || s==RSI30_IN_Btn_Label)) // ボタンの枠かボタンのラベル(文字)がクリックされたら
   {
      
      RSI30_IN_On_Off = !RSI30_IN_On_Off; // オンオフの切替
      
      // ボタンを押した後、一度全体を見る為の判定フラグ
      if(RSI30_IN_On_Off){
      All_Keisan = true;
      }
      
      // 見た目を即時反映
      ObjectSetString (0, RSI30_IN_Btn_Label,  OBJPROP_TEXT,  RSI30_IN_On_Off ? "RSI30_IN (桃色)☑" : "RSI30_IN (桃色)"); // 文字を変更
      ObjectSetInteger(0, RSI30_IN_Btn_Waku, OBJPROP_BGCOLOR, RSI30_IN_On_Off ? clrLime : clrWhite); // ボタンの色を変更

      // 土日対策    
      if (Doniti)
      {
         ChartSetSymbolPeriod(0, _Symbol, Period());
      }            
   
      ChartRedraw(); // 再描画
      return;
   }
   
   //  「RSI30_OUT」ボタンのクリック処理  
   if(id==CHARTEVENT_OBJECT_CLICK && (s==RSI30_OUT_Btn_Waku || s==RSI30_OUT_Btn_Label)) // ボタンの枠かボタンのラベル(文字)がクリックされたら
   {
      
      RSI30_OUT_On_Off = !RSI30_OUT_On_Off; // オンオフの切替
      
      // ボタンを押した後、一度全体を見る為の判定フラグ
      if(RSI30_OUT_On_Off)
      {
         All_Keisan = true;
      }   
      
      // 見た目を即時反映
      ObjectSetString (0, RSI30_OUT_Btn_Label,  OBJPROP_TEXT,  RSI30_OUT_On_Off ? "RSI30_OUT (黄色)☑" : "RSI30_OUT (黄色)"); // 文字を変更
      ObjectSetInteger(0, RSI30_OUT_Btn_Waku, OBJPROP_BGCOLOR, RSI30_OUT_On_Off ? clrLime : clrWhite); // ボタンの色を変更
         
      // 土日対策    
      if (Doniti)
      {
         ChartSetSymbolPeriod(0, _Symbol, Period());
      }            
           
      ChartRedraw(); // 再描画
      return;
   }
   
   // 「アラート」ボタンのクリック処理
   if(id==CHARTEVENT_OBJECT_CLICK && (s==Alert_Btn_Waku || s==Alert_Btn_Label))
   {
      Alert_On_Off = !Alert_On_Off; // オンオフの切り替え
   
      // 見た目を即時反映
      ObjectSetString (0, Alert_Btn_Label,  OBJPROP_TEXT,    Alert_On_Off ? "アラート☑" : "アラート");
      ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_BGCOLOR, Alert_On_Off ? clrLime : clrWhite);
   
      // 直近で「確定」しているバー(インデックス1)の開始時刻を取得します。
      // iTimeは指定シンボル・時間足のバー開始時刻を返します。0は未確定、1が直近確定バーです。
      datetime Last_Close = iTime(_Symbol, _Period, 1);
      
      // 有効な時刻が取得できた場合のみ、各アラートの「最後に通知した時刻」を揃えます。こうすることで、起動直後やボタン切替直後に「過去のバー」でアラートが連発するのを防ぎます(既読化)。
      if(Last_Close > 0){
         LastAlertTime70IN  = Last_Close;  // RSI70_IN の最終通知時刻を直近確定バーに更新
         LastAlertTime70OUT = Last_Close;  // RSI70_OUT の最終通知時刻を直近確定バーに更新
         LastAlertTime30IN  = Last_Close;  // RSI30_IN の最終通知時刻を直近確定バーに更新
         LastAlertTime30OUT = Last_Close;  // RSI30_OUT の最終通知時刻を直近確定バーに更新
      }
   
      ChartRedraw();
      return;
   }         
        
}

/* ===================================================================== 
   関数
   ===================================================================== */ 

   // 追加:5年境界を求める小関数(series=0が最新前提)※うるう年までは見ていないので厳密には約5年
   int GetFive(const datetime &time[], const int rates_total)     // 過去5年分の有効バー数を求める関数(series=0が最新前提)
   {
      datetime latest  = time[0];                                 // 最新バー(インデックス0)の時間を取得
      datetime cutoff  = latest - (datetime)(86400 * 365 * 5);    // 最新から5年分(約365日×5)の秒数を引いて基準時刻を計算
      int idx = iBarShift(_Symbol, _Period, cutoff, false);       // 指定時刻(cutoff)に最も近いバーのインデックスを取得
      if(idx < 0)               idx = rates_total - 1;            // 取得失敗または範囲外なら末尾(最古バー)に補正
      if(idx > rates_total - 1) idx = rates_total - 1;            // 範囲外上限なら同じく末尾に補正
      return(idx + 1);                                            // 0〜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[])
  {

   // time[]~spread[]は通常0=最新でtrueとされていますが、環境によって異なる可能性があるので
   // 念のためifで確認を0=最新であるかを確認しています。
   
   if(!ArrayGetAsSeries(time))
   {
      ArraySetAsSeries(time, true); //0=最新でない時はArraySetAsSeries(true)で0=最新に
   }
   if(!ArrayGetAsSeries(open))
   {
      ArraySetAsSeries(open, true); //0=最新でない時はArraySetAsSeries(true)で0=最新に
   }
   if(!ArrayGetAsSeries(high))
   {
      ArraySetAsSeries(high, true); //0=最新でない時はArraySetAsSeries(true)で0=最新に
   }
   if(!ArrayGetAsSeries(low))
   {
      ArraySetAsSeries(low, true); //0=最新でない時はArraySetAsSeries(true)で0=最新に
   }
   if(!ArrayGetAsSeries(close))
   {
      ArraySetAsSeries(close, true); //0=最新でない時はArraySetAsSeries(true)で0=最新に
   }
   if(!ArrayGetAsSeries(tick_volume))
   {
      ArraySetAsSeries(tick_volume, true); //0=最新でない時はArraySetAsSeries(true)で0=最新に
   }
   if(!ArrayGetAsSeries(volume))
   {
      ArraySetAsSeries(volume, true); //0=最新でない時はArraySetAsSeries(true)で0=最新に
   }
   if(!ArrayGetAsSeries(spread))
   {
      ArraySetAsSeries(spread, true); //0=最新でない時はArraySetAsSeries(true)で0=最新に
   }   


   //ローソク足は最低2本以上必要なので、本数が不足している時は終了する。※RSIのサインの判定は前の足と今の足で行っている為   
   if(rates_total < (int)MathMax(RSI_Kikan, ATR_Kikan) + 2)
   {
      return(0); // まだ計算していない時はreturn(0)で返す。   
   }

   // RSI70_INがオフの時サインを消す(ArrayInitialize=配列のすべての要素を一度に同じ値へそろえるための関数)
   if(!RSI70_IN_On_Off)
   {
      ArrayInitialize(RSI70_IN_Sign_Buffer, EMPTY_VALUE); // RSI70_IN_Sign_Buffer全体を「非表示値(EMPTY_VALUE)」で一括初期化してサインを消す
   }

   // RSI70_OUTがオフの時サインを消す   
   if(!RSI70_OUT_On_Off)
   {
      ArrayInitialize(RSI70_OUT_Sign_Buffer, EMPTY_VALUE); // RSI70_OUT_Sign_Buffer全体を「非表示値(EMPTY_VALUE)」で一括初期化してサインを消す 
   }
   
   // RSI30_INがオフの時サインを消す
   if(!RSI30_IN_On_Off)
   {
      ArrayInitialize(RSI30_IN_Sign_Buffer, EMPTY_VALUE); // RSI30_IN_Sign_Buffer全体を「非表示値(EMPTY_VALUE)」で一括初期化してサインを消す
   }

   // RSI30_OUTがオフの時サインを消す   
   if(!RSI30_OUT_On_Off)
   {
      ArrayInitialize(RSI30_OUT_Sign_Buffer, EMPTY_VALUE); // RSI30_OUT_Sign_Buffer全体を「非表示値(EMPTY_VALUE)」で一括初期化してサインを消す 
   }   

   // RSI70 RSI30 全てのボタンがオフの時全てのサインを消す
   if(!RSI70_IN_On_Off && !RSI70_OUT_On_Off && !RSI30_IN_On_Off && !RSI30_OUT_On_Off)
   {
      ArrayInitialize(RSI70_IN_Sign_Buffer, EMPTY_VALUE); // RSI70_IN_Sign_Buffer全体を「非表示値(EMPTY_VALUE)」で一括初期化してサインを消す   
      ArrayInitialize(RSI70_OUT_Sign_Buffer, EMPTY_VALUE); // RSI70_OUT_Sign_Buffer全体を「非表示値(EMPTY_VALUE)」で一括初期化してサインを消す   
      ArrayInitialize(RSI30_IN_Sign_Buffer, EMPTY_VALUE); // RSI30_IN_Sign_Buffer全体を「非表示値(EMPTY_VALUE)」で一括初期化してサインを消す
      ArrayInitialize(RSI30_OUT_Sign_Buffer, EMPTY_VALUE); // RSI30_OUT_Sign_Buffer全体を「非表示値(EMPTY_VALUE)」で一括初期化してサインを消す
      return(rates_total);  
   }


   // 過去5年分の本数をGetFive()より取得
   int Get5Bars  = GetFive(time, rates_total);
   
   // 5年分もしくは5年分以下の場合は全てのローソク足の本数をBars5_Or_Totalに代入。ローソク足は必ず5年分あるとは限らない為                               
   int Bars5_Or_Total   = (Get5Bars > 0 ? MathMin(Get5Bars, rates_total) : rates_total);  

   //RSIの値を5年(もしくはtotal)分コピー   
   int RSI_Copy= CopyBuffer(RSI_Handle, 0, 0, Bars5_Or_Total, RSI_Buffer);

   //RSIの値が無ければ終了   
   if(RSI_Copy <= 0)
   {
      return(rates_total);
   }
     
   // ATRも5年(もしくはtotal)分コピー
   int ATR_Copy = CopyBuffer(ATR_Handle, 0, 0, Bars5_Or_Total, ATR_Buffer); // 5年分コピー
   
   // ATRの値が無ければ終了
   if(ATR_Copy <= 0)
   {
      return(rates_total);
   }
   
   // 未確定バーは毎回クリア(確定足でサインを出す為 0=未確定足)
   RSI70_IN_Sign_Buffer[0] = EMPTY_VALUE; // RSI70_IN_Sign_Buffer[0]が未確定足(0番目)の時はサインを消す
   RSI70_OUT_Sign_Buffer[0] = EMPTY_VALUE; // RSI70_OUT_Sign_Buffer[0]が未確定足(0番目)の時はサインを消す
   RSI30_IN_Sign_Buffer[0] = EMPTY_VALUE; // RSI30_IN_Sign_Buffer[0]が未確定足(0番目)の時はサインを消す
   RSI30_OUT_Sign_Buffer[0] = EMPTY_VALUE; // RSI30_OUT_Sign_Buffer[0]が未確定足(0番目)の時はサインを消す      
   

   // RSI・ATR・ローソク足(5年分)の「どれにもはみ出さない」ように、計算できる最後の位置(最大インデックス)を一番短いデータに合わせて決めている処理
   // MathMin(x, y) は小さいほうを返します。ここでは 3つの候補 のうち最も小さい値を選んでいます。
   // RSI_Copy - 2 は、ループ内で RSI_Buffer[i+1]を使用するので1つ余分に。
   // ATR_Copy - 1 は、ATR_Buffer[i] を参照するだけ。-1は配列の番号が0から始まる為
   // Bars5_Or_Total - 1 は、ローソク足(5年分または総本数)。-1は配列の番号が0から始まる為。
   // 【例】RSI_Copy=100, ATR_Copy=120, Bars5_Or_Total=90 なら min(98, 119, 89)=89 となり、i=1~89 の範囲が安全に計算できます。
   
   int Min_End = MathMin(MathMin(RSI_Copy - 2, ATR_Copy - 1), Bars5_Or_Total - 1);
   
   // 最低限の計算可能本数を Min_End で判定後、2本以上あるか確認「i+1」をRSIで使っているので2本は必要になる。
   if(Min_End < 1)
   {
      return(rates_total); // 2本無ければ終了。
   }


   // 「今回どれだけ再計算するか」を求めており、初回(prev_calculated==0)なら処理可能範囲の全体 Min_End を
   // 2回目以降なら現在本数 rates_total から前回計算済み本数 prev_calculated を引いた増分(マイナスなら0)を MinEnd_Or_Zouka に入れます
   int MinEnd_Or_Zouka = (prev_calculated==0) ? Min_End : MathMax(0, rates_total - prev_calculated); // 初回は全体もしくは今回増えた本数
   
   // 再計算するときに安全のためもう1本分だけ余裕を足して計算することを示す数値(予備の1本)
   int Yobi   = 1;
   
   // 初回の計算(prev_calculated==0)か「全体を計算し直す合図(All_Keisan)」が出ているときに true となり、全面再計算を行うかどうかを決めるフラグ
   bool Do_Full = (prev_calculated==0) || All_Keisan;                                      // 隣足(i+1)参照の安全マージン
   
   int MainLoopStart = 1;                                   // 直近確定足から
   
   // ※ここが一番需要なの長文での説明
   // この1行は、「メインの for ループをどこまで回すか(最後のインデックス)」を決めています。
   // 平たく言えば、全部やるのか、それとも必要なぶんだけ(+安全のために少し余分に)やるのか。
   // まず、「条件演算子 ? :」の外側の考え方です。
   // Do_Full ? Min_End : ……
   // Do_Full が true(初回計算や「全再計算」の合図が出ている)なら、最後まで計算します。最後の位置は Min_End(安全に計算できる最大インデックス)です。
   // Do_Full が false(差分だけ計算すればよい)なら、右側の式(MathMin(MainLoopStart + MathMax(1, MinEnd_Or_Zouka + Yobi) - 1, Min_End))で必要なぶんだけの終点を出します。
   // MathMin( A , Min_End ) 終点を必ずMin_Endを超えないように上限でクリップしています。
   // A の中身はこうなっています。 MainLoopStart + MathMax(1, MinEnd_Or_Zouka + Yobi) - 1
   // MainLoopStart … ループの開始位置です。ここでは 1(=直近の「確定足」)に固定されています。
   // MinEnd_Or_Zouka … 「今回どれだけ増えたか(再計算が必要な本数)」です。
   // 初回は全体本数、2回目以降は「現在本数 − 前回計算済本数」で求めた増分になります(マイナスは0に切り上げ済み)。
   // Yobi … 安全マージンの1本です。インジ計算では「隣の足(i+1)を参照する」ことがあり、境目の再計算漏れを防ぐために1本だけ余分に回します。
   // MathMax(1, MinEnd_Or_Zouka + Yobi) … たとえ増分が0でも、最低1本は回すようにしています(「全く回らず更新が反映されない」を避けます)。
   // - 1 … 「開始位置に「本数」を足して終点インデックスを出す」都合上、インデックスは0始まりなので最後に1を引いて整えています。
   // 例:開始が1で「3本回す」なら終点は 1 + 3 - 1 = 3(1,2,3 の3本分)
   
   int MainLoopEnd   = Do_Full ? Min_End : MathMin(MainLoopStart + MathMax(1, MinEnd_Or_Zouka + Yobi) - 1, Min_End);

   // メインのループ処理   変数 i を MainLoopStart から MainLoopEnd まで 1 ずつ増やしながら、その範囲の各インデックスに対する処理を順番に繰り返す
   for(int i = MainLoopStart; i <= MainLoopEnd; i++)
   {
      if(All_Keisan)
      {
         All_Keisan = false; // 全体集計フラグを解除
      }
      
      //RSI判定用の処理
            
      double RSI70_IN_Now  = RSI_Buffer[i]; // RSIの計算済の値を配列から取り出す(確定足)
      double RSI70_IN_Prev = RSI_Buffer[i+1]; // RSIの計算済の値を配列から取り出す(確定足の前(1つ古い足))
      
      double RSI70_OUT_Now  = RSI_Buffer[i]; // RSIの計算済の値を配列から取り出す(確定足)
      double RSI70_OUT_Prev = RSI_Buffer[i+1]; // RSIの計算済の値を配列から取り出す(確定足の前(1つ古い足))
      
      double RSI30_IN_Now  = RSI_Buffer[i]; // RSIの計算済の値を配列から取り出す(確定足)
      double RSI30_IN_Prev = RSI_Buffer[i+1]; // RSIの計算済の値を配列から取り出す(確定足の前(1つ古い足))
      
      double RSI30_OUT_Now  = RSI_Buffer[i]; // RSIの計算済の値を配列から取り出す(確定足)
      double RSI30_OUT_Prev = RSI_Buffer[i+1]; // RSIの計算済の値を配列から取り出す(確定足の前(1つ古い足))             

      // 上抜け(<=70 → >70)でサイン      
      bool RSI70_IN_Hantei = RSI70_IN_Now > RSI70_IN_Level && RSI70_IN_Prev <= RSI70_IN_Level;
      
      // 下降抜け(>70 → <=70)でサイン
      bool RSI70_OUT_Hantei = (RSI70_OUT_Now < RSI70_OUT_Level && RSI70_OUT_Prev >= RSI70_OUT_Level);
      
      // 下抜け(>=30 → <30)でサイン 
      bool RSI30_IN_Hantei  = (RSI30_IN_Now  < RSI30_IN_Level  && RSI30_IN_Prev  >= RSI30_IN_Level);   // 下抜け(>=30 → <30)
      
      // 上抜け(<=30 → >30)でサイン
      bool RSI30_OUT_Hantei = (RSI30_OUT_Now > RSI30_OUT_Level && RSI30_OUT_Prev <= RSI30_OUT_Level);  // 上抜け(<=30 → >30)      

      // 矢印の幅をATRの0.4倍に設定
      double ATR_Haba = ATR_Buffer[i] * 0.4;
         
      // RSI70_IN_HanteiがTrueでRSI70_INのボタンがONの時   
      if(RSI70_IN_Hantei && RSI70_IN_On_Off) // 上抜け(<=70 → >70)した場合
      {
         RSI70_IN_Sign_Buffer[i] = high[i] + ATR_Haba; // 高値の上(ATR_Haba)にサインを出す
      }

      // RSI70_OUT_HanteiがTrueでRSI70_OUTのボタンがONの時   
      if(RSI70_OUT_Hantei && RSI70_OUT_On_Off) // 下降抜け(>70 → <=70)した場合
      {
         RSI70_OUT_Sign_Buffer[i] = high[i] + ATR_Haba; // 高値の上(ATR_Haba)にサインを出す
      }
      
      // RSI30_IN_HanteiがTrueでRSI30_INのボタンがONの時  
      if(RSI30_IN_Hantei && RSI30_IN_On_Off)
      {
         RSI30_IN_Sign_Buffer[i] = low[i] - ATR_Haba; // 安値の下(ATR_Haba)にサインを出す
      }
      
      // RSI30_OUT_HanteiがTrueでRSI30_OUTのボタンがONの時
      if(RSI30_OUT_Hantei && RSI30_OUT_On_Off)
      {
         RSI30_OUT_Sign_Buffer[i] = low[i] - ATR_Haba; // 安値の下(ATR_Haba)にサインを出す
      }
      
      // アラート処理(RSI70_IN)
      if(i == 1 && Alert_On_Off && RSI70_IN_On_Off)  // i==1:直近で確定した足だけを対象にする・全体アラートON・このシグナルもONのとき
      {
         const bool plotted = (RSI70_IN_Sign_Buffer[i] != EMPTY_VALUE); // このバーにRSI70_INの矢印が実際に描かれているか(EMPTY_VALUEでなければ描画あり)
         if(plotted && (LastAlertTime70IN == 0 || time[i] > LastAlertTime70IN)) // 描画があり、かつ最後に通知したバーより新しい(同一バーでの多重通知を防止)
         {
            Alert(StringFormat("[%s] RSI70_IN %s (TF:%s)", // メッセージの雛形を作ってアラート表示
                  _Symbol,                                  // %s(1つ目): 銘柄名
                  TimeToString(time[i], TIME_DATE | TIME_MINUTES), // %s(2つ目): 該当バーの日時(分まで)
                  EnumToString((ENUM_TIMEFRAMES)_Period)));       // %s(3つ目): 現在の時間足を文字列で
            LastAlertTime70IN = time[i];                   // このバーを「通知済み」として時刻を記録
         }
      }
      
      // アラート処理(RSI70_OUT)
      if(i == 1 && Alert_On_Off && RSI70_OUT_On_Off) // i==1のみ対象・全体アラートON・このシグナルもONのとき
      {
         const bool plotted = (RSI70_OUT_Sign_Buffer[i] != EMPTY_VALUE); // このバーにRSI70_OUTの矢印が描かれているか
         if(plotted && (LastAlertTime70OUT == 0 || time[i] > LastAlertTime70OUT)) // 既に同じバーで通知していないかをチェック
         {
            Alert(StringFormat("[%s] RSI70_OUT %s (TF:%s)", // アラート文を組み立てて表示
                  _Symbol,                                   // 銘柄名
                  TimeToString(time[i], TIME_DATE | TIME_MINUTES), // バーの日時
                  EnumToString((ENUM_TIMEFRAMES)_Period)));       // 時間足
            LastAlertTime70OUT = time[i];                  // 最終通知時刻をこのバーに更新
         }
      }
      
      // アラート処理(RSI30_IN)
      if(i == 1 && Alert_On_Off && RSI30_IN_On_Off) // i==1のみ対象・全体アラートON・このシグナルもONのとき
      {
         const bool plotted = (RSI30_IN_Sign_Buffer[i] != EMPTY_VALUE); // このバーにRSI30_INの矢印が描かれているか
         if(plotted && (LastAlertTime30IN == 0 || time[i] > LastAlertTime30IN)) // 同一バーでの重複通知を避ける
         {
            Alert(StringFormat("[%s] RSI30_IN %s (TF:%s)", // アラート文を表示
                  _Symbol,                                   // 銘柄名
                  TimeToString(time[i], TIME_DATE | TIME_MINUTES), // バーの日時
                  EnumToString((ENUM_TIMEFRAMES)_Period)));       // 時間足
            LastAlertTime30IN = time[i];                  // このバーを通知済みに更新
         }
      }
      
      // アラート処理(RSI30_OUT)
      if(i == 1 && Alert_On_Off && RSI30_OUT_On_Off) // i==1のみ対象・全体アラートON・このシグナルもONのとき
      {
         const bool plotted = (RSI30_OUT_Sign_Buffer[i] != EMPTY_VALUE); // このバーにRSI30_OUTの矢印が描かれているか
         if(plotted && (LastAlertTime30OUT == 0 || time[i] > LastAlertTime30OUT)) // 同一バーの重複通知を防止
         {
            Alert(StringFormat("[%s] RSI30_OUT %s (TF:%s)", // アラート文を表示
                  _Symbol,                                    // 銘柄名
                  TimeToString(time[i], TIME_DATE | TIME_MINUTES), // バーの日時
                  EnumToString((ENUM_TIMEFRAMES)_Period)));       // 時間足
            LastAlertTime30OUT = time[i];                 // このバーを最後に通知した時刻として記録
         }
      }
    
                      
   }    
  
   return(rates_total);
  }


//+------------------------------------------------------------------+
//| 削除処理                    |
//+------------------------------------------------------------------+

   // 終了時の削除・解放
void OnDeinit(const int reason)
{
   // 1) 先にハンドルは必ず解放
   if(ATR_Handle != INVALID_HANDLE)
   {
      IndicatorRelease(ATR_Handle); ATR_Handle = INVALID_HANDLE;
   }
   
   if(RSI_Handle  != INVALID_HANDLE)
   {
      IndicatorRelease(RSI_Handle ); RSI_Handle  = INVALID_HANDLE;
   }

   // 2) チャート変更/パラメータ変更/再コンパイル時はUIを残す
   if(reason==REASON_CHARTCHANGE || reason==REASON_PARAMETERS || reason==REASON_RECOMPILE)
   {
      // 任意:サインだけは消しておく(残像防止)
      ArrayInitialize(RSI70_IN_Sign_Buffer,  EMPTY_VALUE);
      ArrayInitialize(RSI70_OUT_Sign_Buffer, EMPTY_VALUE);
      ArrayInitialize(RSI30_IN_Sign_Buffer,  EMPTY_VALUE);
      ArrayInitialize(RSI30_OUT_Sign_Buffer, EMPTY_VALUE);
      ChartRedraw();
      return; // ★ここではUIオブジェクトを削除しない
   }

   // 3) 本当に終了する場合のみ、UIを削除
   ObjectDelete(0, RSI70_IN_Btn_Waku);
   ObjectDelete(0, RSI70_IN_Btn_Label);
   ObjectDelete(0, RSI70_OUT_Btn_Waku);
   ObjectDelete(0, RSI70_OUT_Btn_Label);
   ObjectDelete(0, RSI30_IN_Btn_Waku);
   ObjectDelete(0, RSI30_IN_Btn_Label);
   ObjectDelete(0, RSI30_OUT_Btn_Waku);
   ObjectDelete(0, RSI30_OUT_Btn_Label);
   ObjectDelete(0, Alert_Btn_Waku);
   ObjectDelete(0, Alert_Btn_Label);

   // 任意:サイン配列クリア
   ArrayInitialize(RSI70_IN_Sign_Buffer,  EMPTY_VALUE);
   ArrayInitialize(RSI70_OUT_Sign_Buffer, EMPTY_VALUE);
   ArrayInitialize(RSI30_IN_Sign_Buffer,  EMPTY_VALUE);
   ArrayInitialize(RSI30_OUT_Sign_Buffer, EMPTY_VALUE);

   ChartRedraw();
}

RSIタッチの極みのコードを解説

特に重要な点を
詳しく解説

RSIタッチの極みのコードで、MQL5初心者の方が迷いやすい点を抜粋して紹介しています。

バッファ

バッファとは、プログラムが処理中のデータを一時的に保存しておく「入れ物(メモリ上の置き場)」で、MQL5のインジケータでは描画や計算結果をチャートに渡すための配列を指します。

難しく考えても時間の無駄なので、「バッファに値が入ったらサインがでる」とだけ覚えておきましょう。

バッファが出来るまでの流れ

まずは、バッファ用の配列を用意します。

double TestBuffer[];

この時点ではただの配列です。

次にSetIndexBuffer()にTestBuffer配列をセットしてバッファとして認識させます。SetIndexBuffer(0,TestBuffer,INDICATOR_DATA);

※この際に番号を振り当ています。
※SetIndexBuffer()の番号は0から始まります。

TestBuffer[]はこの時点でどの様なサインを出すのかまだ決まっていません。

その為、バファに値が入った瞬間にどの様なサイン(色や形)が入るか事前に決めておく必要があります。

基本的にグローバルでサインの情報(ラベル名・矢印の種類・矢印の色・矢印の太さ)を決めます。

#property indicator_label1 “RSI70_INのサイン” // RSI70_INのサインのラベル名
#property indicator_type1 DRAW_ARROW // RSI70_INのサイン(矢印)
#property indicator_color1 clrDeepSkyBlue // RSI70_INの矢印の色(水色)
#property indicator_width1 2 // RSI70_INの太さ

矢印の種類(↑や↓矢印)はint OnInit()内で設定します。

PlotIndexSetInteger(0, PLOT_ARROW, 110); // RSI70_IN用のサイン

PlotIndexSetIntegerで番号を変更する事で矢印の種類を変更する事が出来ます。今回は■マークを使用していますが、■は110になります。

バッファと番号について

SetIndexBuffer(0,TestBuffer,INDICATOR_DATA); は0番目となります。

SetIndexBufferは0から始まるので、一番初めに作ったバッファは0をセットしますが、この0番目のバッファにサインの情報を入れるには、同じ番号を当てはめる必要があります。

#propertyも番号をセットする事ができ「バッファの番号と#propertyの番号が同じ」同じであればバッファに#propertyの情報をセットする事が出来ます。

#property indicator_label1 の最後に付いている「1」が番号になります。

#property indicator_label1 “RSI70_INのサイン” // RSI70_INのサインのラベル名
#property indicator_type1 DRAW_ARROW // RSI70_INのサイン(矢印)
#property indicator_color1 clrDeepSkyBlue // RSI70_INの矢印の色(水色)
#property indicator_width1 2 // RSI70_INの太さ

※ここでややこしくなるのが#propertyの番号です。#propertyは1から始まるのでバッファの「0」=propertyの「1」となります。

※更にややこしくしているのが、PlotIndexSetInteger(0, PLOT_ARROW, 110); です。

PlotIndexSetIntegerは0から始まります。

即ちSetIndexBuffer(0,TestBuffer,INDICATOR_DATA); に矢印の情報を入れるには

#propertyは1、PlotIndexSetIntegerは0にする必要があるのです。

綾瀬文也
綾瀬 文也

#propertyだけ1から、それ以外は0ととりあえず覚えておきましょう。

後は「RSI70_IN_Sign_Buffer[i] = high[i]」の様にバッファに値(どこに配置するか)ローソクの上(high)にサインが表示されます。

ハンドル

ハンドルは、MT5の中にある「計算データの入れ物」を指し示すための番号です。RSIなどを作ると番号が返り、その番号で「中身を読む・表示する・片付ける」を指定します。

番号があるおかげで、同じ種類でも銘柄や時間足・期間が違うものを取り違えずに扱えます(例:USDJPYのRSI用の番号と、EURUSDのRSI用の番号は別々に返ってきます)。

番号が無効(INVALID_HANDLE)なら入れ物がまだ無い状態なので、使う前に作成できたか確認し、使い終わったら解放します。

int RSI_Handle = INVALID_HANDLE;の場合

RSI_Handle はRSIインジケータ用のハンドル番号を格納する整数変数で、INVALID_HANDLE を入れて「まだ有効なハンドルを取得していない初期状態」であることを明示しています。

ArraySetAsSeries

バッファ(配列)を作った場合、配列に入るiはi番目のローソク足の意味となります。

仮にiが0だったします。

このi(0)が意外とややこしく、ローソク足の最古の0なのか最新の0なのか判断が難しくなっています。※環境によって異なる可能性がある

そこでバッファ(配列)を作った場合は、ArraySetAsSeries()でtrueにする事で、0=最新足にしています。

【最重要】インジケーターを軽くする

ローソク足の本数は時間足によって大きく異なるのですが、1分足の場合、1年で約50万本、10年で約500万本とかなり膨大になります。

相場は常に細かく変動しており、その都度MT5がチャート値を取得しますが、処理を誤るとチャートが動くたびに過去の何百万本ものバーを読み込んでしまう恐れがあります。

間違った処理を行ってしまうと、画面の動きが極端に重たくなり、場合によっては遅延を招いてしまいます。

そこで、重要になるのが「初回:全体を見る」「2回目以降:増えた足だけ見る」と言う考え方です。

また、全体もある程度年数を決めた方が良いでしょう。

年数を決めずに「ローソク足全体」にすると1971年まで見てしまいます。

決まりはないのですが、5年なり、10年なり決めて、それ以降は表示させない様にした方が良いでしょう。

今回紹介しているコードは最大5年分と決めています。初動に関しては5年分を見るのでやや重くなりますが、それ以降は新しい足のみ確認をしていますので、動作が軽くなります。

サインツールを作る際、最も面倒でややこしくなるのがインジケーターを軽くする処理です。

逆に言えばそれ以外は、難しい事はほとんど無いので、インジケーターを軽くする処理についてはじっくり理解される事をおすすめします。

RSIタッチの極みの流れ、軽くするまでの流れ
  • 「Get5Bars」に5年分の本数を入れる
  • 「Bars5_Or_Total」にGet5Bars(5年分)もしくはローソク足のトータルを入れる(ローソク足のトータルが5年以上あるとは限らない:少ない方に合わせる)
  • 「RSI_Copy」にRSIの値を(Bars5_Or_Total:5年分)入れる
  • 「ATR_Copy」にATRの値を(Bars5_Or_Total:5年分)入れる ※ATRは矢印の位置を決める為に使用
  • 「Min_End」にRSI(5年分)・ATR(5年分)・ローソク足のトータルの中から一番小さな値を入れる(計算がはみ出さない様にする為)
  • 「MinEnd_Or_Zouka」に初回はMin_End(5年分)、2回目以降は増加分(rates_total – prev_calculated)を入れる
  • 「Do_Full」は真偽値:初回(prev_calculated==0)で全体を計算し直す合図(All_Keisan)」が出ているときはtrue
  • 「MainLoopStart」は1
  • 「MainLoopEnd」はDo_Fullがtrueの時「Min_End(5年分)」:falseの時に「増加分」に「安全のための1本」を足して開始位置からどこまで処理するかを決め、なおかつ安全に見られる最大位置(Min_End)を絶対に超えないよう上限で止める

メインのforは基本的に1~5年分もしくは1~増加分となります。

タイトルとURLをコピーしました