FXやバイナリーオプション(ザオプション)で役立つ!
MT5で使えるボリンジャーバンドで±2σと±3σにタッチした際にサインとアラートが鳴るインジケーター「らくらくボリンジャーバンド君」を無料で配布しています。
らくらくボリンジャーバンド君のコードは完全公開しています。全項目にコメントを付けていますので、MQL5のサインツールの学習に役立ちます。
らくらくボリンジャーバンド君の使い方

らくらくボリンジャーバンド君はMT5専用のインジケーターになりますので、MT5をダウンロードする必要があります。
MT5のダウンロード方法やインジケーターの導入方法は関連記事にまとめていますので、まずは関連記事をご確認下さい。


らくらくボリンジャーバンド君を立ち上げると画面左上に4つのボタンが表示されます。
- ±2σ touch
- ±3σ touch
- Repeat
- Alert
±2σ touchボタン

±2σ touchボタンをクリックすると±2σ touch [OFF]⇒±2σ touch [ON]に切り替わります。
±2σ touch [ON]の時、+2σのバンド(水色)と-2σのバンド(水色)とミドルバンド(緑色)が表示され、±2σのバンドにタッチした足には■(水色)のサインが表示されます。
ボリンジャーバンドとサインは過去5年分まで表示されます。
±3σ touchボタン

±3σ touchボタンをクリックすると±3σ touch [OFF]⇒±3σ touch [ON]に切り替わります。
±3σ touch [ON]の時、+3σのバンド(ピンク)と-3σのバンド(ピンク)とミドルバンド(緑色)が表示され、±3σのバンドにタッチした足には■(ピンク)のサインが表示されます。
ボリンジャーバンドとサインは過去5年分まで表示されます。
Repeatボタン

RepeatボタンをONにすると±2σもしくは±3σにタッチした全ての足にサインが出ます。都度確認したい場合は、RepeatボタンをONにします。
RepeatボタンをOFFにすると±2σもしくは±3σにタッチした際に一度サインを出して、連続で出た場合はサインが出ない様にしています。
仮に+2σを超えてサインがでた場合は、+2σを下回り、再度+2σを上回るまでサインが出ない様になっています。
Alartボタン

AlartボタンをONにするとサインが出た時に、アラートが鳴ります。
詳細設定について

らくらくボリンジャーバンド君を起動するとポップアップが表示されます。詳細設定は、そのポップアップの「インプット」タブから行えます。
ボリンジャーバンドの期間のおすすめは20なので、デフォルトの20が推奨となります。
適用価格はデフォルトのClose priceが推奨となります。
ATRは、サインをローソク足からどれだけ離すか(オフセット)を決めるために使用します。間隔を point 固定でも指定できますが、時間足が変わると見え方の差が大きく、安定しません。
そこで、過去○○本のローソク足から算出した ATR(平均)を用いることで、時間軸に左右されにくく、サインとローソク足の間隔を一定に保てます。
なお、参照本数を増やしすぎると処理が重くなるため、目安としては50本程度を推奨します。
ATRは過去50本の平均値を基準にしますが、その値をそのままオフセットにするとサインがローソク足から離れすぎます。そこで、実際にはATRに0.○を乗じた値(ATR×0.○)で縮小し、サイン位置を調整します。
ボタンの処理はチャートが動いていることが前提になります。土日はチャートが止まるのでボタンを押しても反応しません。
土日対策をtrueにすると、ボタンを押した際に「ChartSetSymbolPeriod(0, _Symbol, Period());」で読み込みます。
ChartSetSymbolPeriod(0, _Symbol, Period());で読み込むと土日でもボタンが反応するのですが、処理がやや重いので土日のみ土日処理をtrueにされる事をおすすめします。
「らくらくボリンジャーバンド君」は、初期設定では未確定足(0番目)でサインを表示します。設定項目「未確定足でもサイン/アラートを出す」を false にすると、未確定足では表示せず、確定足(1番目)でサインとアラートを出すようになります。
らくらくボリンジャーバンド君のコードを完全公開
コードを完全公開
らくらくボリンジャーバンド君のコードを完全公開しています。コードの意味は全てコメントに残していますので、コメントを見るだけで理解することができます。
初心者の方が理解しやすいように、書いていますので、スマートな書き方ではないことだけご理解下さい。
※はみ出している箇所はキーボードの「←」と「→」で動かして見る事が出来ます。
//+------------------------------------------------------------------+
//| btn+bb_touch.mq5 |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
/*--------------------------------------------------------------------
#property は、インジケータ(EA/スクリプト)の著作権やリンクなどのメタ情報と、
表示方式・色・本数などの初期設定をコンパイル時に宣言して固定するための記述で、
実行中には変更できません。
--------------------------------------------------------------------*/
#property copyright "watase akira"
#property link "https://www.mql5.com"
#property version "1.00"
#property indicator_chart_window
// 使用するバッファ数とプロット数を決める
#property indicator_buffers 10
#property indicator_plots 10
// ---- 「+2σタッチ」のサイン(矢印)
#property indicator_label1 "+2σバンドタッチのサイン" // ラベル名
#property indicator_type1 DRAW_ARROW // 表示形式:矢印
#property indicator_color1 clrDeepSkyBlue // 矢印色:水色
#property indicator_width1 2 // 太さ
// ---- 「+3σタッチ」のサイン(矢印)
#property indicator_label2 "+3σバンドタッチのサイン" // ラベル名
#property indicator_type2 DRAW_ARROW // 表示形式:矢印
#property indicator_color2 clrPink // 線色:桃色
#property indicator_width2 2 // 太さ
// ---- 「-2σタッチ」のサイン(矢印)
#property indicator_label3 "-2σバンドタッチのサイン" // ラベル名
#property indicator_type3 DRAW_ARROW // 表示形式:矢印
#property indicator_color3 clrDeepSkyBlue // 矢印色:水色
#property indicator_width3 2 // 太さ
// ---- 「-3σタッチ」のサイン(矢印)
#property indicator_label4 "-3σバンドタッチのサイン" // ラベル名
#property indicator_type4 DRAW_ARROW // 表示形式:矢印
#property indicator_color4 clrPink // 線色:桃色
#property indicator_width4 2 // 太さ
// ---- +2σのバンド表示(バンド)
#property indicator_label5 "+2σバンド" // +2σバンド
#property indicator_type5 DRAW_LINE // 表示形式:ライン
#property indicator_color5 clrDeepSkyBlue // 線色:水色
#property indicator_width5 1 // 太さ
// ---- -2σのバンド表示(バンド)
#property indicator_label6 "-2σバンド" // -2σバンド
#property indicator_type6 DRAW_LINE // 表示形式:ライン
#property indicator_color6 clrDeepSkyBlue // 線色:水色
#property indicator_width6 1 // 太さ
// ---- ±2σのミドルバンド表示(バンド)
#property indicator_label7 "±2ミドルバンド" // ±2σのミドルバンド
#property indicator_type7 DRAW_LINE // 表示形式:ライン
#property indicator_color7 clrGreen // 線色:緑
#property indicator_width7 1 // 太さ
// ---- +3σのバンド表示(バンド)
#property indicator_label8 "+3σバンド" // +3σのバンド
#property indicator_type8 DRAW_LINE // 表示形式:ライン
#property indicator_color8 clrPink // 線色:桃色
#property indicator_width8 1 // 太さ
// ---- -3σのバンド表示(バンド)
#property indicator_label9 "-3σバンド" // -3σのバンド
#property indicator_type9 DRAW_LINE // 表示形式:ライン
#property indicator_color9 clrPink // 線色:桃色
#property indicator_width9 1 // 太さ
// ---- ±3σのミドルバンド表示(バンド)
#property indicator_label10 "±3σミドルバンド" // ±3σのミドルバンド
#property indicator_type10 DRAW_LINE // 表示形式:ライン
#property indicator_color10 clrGreen // 線色:緑
#property indicator_width10 1 // 太さ
// ボタン用オブジェクト名(枠と文字ラベル)
string BB2_Btn_Waku = "BB2 ボタンの枠"; // 「±2σ touch」枠のオブジェクト名
string BB2_Btn_Label = "BB2 ボタンの文字"; // 「±2σ touch」文字のオブジェクト名
string BB3_Btn_Waku = "BB3 ボタンの枠"; // 「±3σ touch」枠のオブジェクト名
string BB3_Btn_Label = "BB3 ボタンの文字"; // 「±3σ touch」文字のオブジェクト名
string Alert_Btn_Waku = "Alert ボタンの枠"; // 「Alert」枠のオブジェクト名
string Alert_Btn_Label = "Alert ボタンの文字"; // 「Alert」文字のオブジェクト名
string Repeat_Btn_Waku = "Repeatボタンの枠"; // 「Repeat」枠のオブジェクト名
string Repeat_Btn_Label = "Repeatボタンの文字"; // 「Repeat」文字のオブジェクト名
// 入力パラメータ(BB と ATR、土日対策)
input int BB_Kikan = 20; // ボリンジャーバンドの期間
// ENUM_APPLIED_PRICE は、インジケータ等の計算に用いる
// 価格の種類(終値・始値・高値・安値・中央値・典型価格・加重終値 など)を指定するための列挙型。
input ENUM_APPLIED_PRICE BB_Price = PRICE_CLOSE; // 適用価格 「価格」は、ローソク足の高値・安値・終値といった価格系列の総称
input int ATR_Kikan = 50; // ATRの期間 デフォルトは50本の平均
input double Haba = 0.4; // ローソク足とサインの幅 デフォルトはATR(50)×0.4(Haba)
input bool Doniti = false; // 【土日対策】false=平日、true=土日
// インジケータのハンドル(作成失敗時は INVALID_HANDLE)
int BB2_Handle = INVALID_HANDLE; // 偏差2.0のBBのハンドル
int BB3_Handle = INVALID_HANDLE; // 偏差3.0のBBのハンドル
int ATR_Handle = INVALID_HANDLE; // ATR用のハンドル
// バッファ用の配列を作成
double U_BB2_Buffer[]; // iBands(偏差2.0)の上バンド値を保持(CopyBufferで取得/0=最新)
double L_BB2_Buffer[]; // iBands(偏差2.0)の下バンド値を保持(CopyBufferで取得/0=最新)
double M_BB2_Buffer[]; // iBands(偏差2.0)のミドル(基準線)値を保持(CopyBufferで取得/0=最新)
double U_BB3_Buffer[]; // iBands(偏差3.0)の上バンド値を保持(CopyBufferで取得/0=最新)
double L_BB3_Buffer[]; // iBands(偏差3.0)の下バンド値を保持(CopyBufferで取得/0=最新)
double M_BB3_Buffer[]; // iBands(偏差3.0)のミドル(基準線)値を保持(CopyBufferで取得/0=最新)
double U_BB2_Line_Buffer[]; // 表示用ライン配列:+2σ上バンドを描画(ON時にU_BB2_Bufferを代入/OFFや非表示はEMPTY_VALUE)
double L_BB2_Line_Buffer[]; // 表示用ライン配列:-2σ下バンドを描画(ON時にL_BB2_Bufferを代入/OFFや非表示はEMPTY_VALUE)
double M_BB2_Line_Buffer[]; // 表示用ライン配列:±2σミドルを描画(ON時にM_BB2_Bufferを代入/OFFや非表示はEMPTY_VALUE)
double U_BB3_Line_Buffer[]; // 表示用ライン配列:+3σ上バンドを描画(ON時にU_BB3_Bufferを代入/OFFや非表示はEMPTY_VALUE)
double L_BB3_Line_Buffer[]; // 表示用ライン配列:-3σ下バンドを描画(ON時にL_BB3_Bufferを代入/OFFや非表示はEMPTY_VALUE)
double M_BB3_Line_Buffer[]; // 表示用ライン配列:±3σミドルを描画(ON時にM_BB3_Bufferを代入/OFFや非表示はEMPTY_VALUE)
double U_BB2_Sign_Buffer[]; // サイン描画用(+2σ上タッチ時):高値+ATR*0.4 を格納/非該当は EMPTY_VALUE
double U_BB3_Sign_Buffer[]; // サイン描画用(+3σ上タッチ時):高値+ATR*0.4 を格納/非該当は EMPTY_VALUE
double L_BB2_Sign_Buffer[]; // サイン描画用(-2σ下タッチ時):安値-ATR*0.4 を格納/非該当は EMPTY_VALUE
double L_BB3_Sign_Buffer[]; // サイン描画用(-3σ下タッチ時):安値-ATR*0.4 を格納/非該当は EMPTY_VALUE
double ATR_Buffer[]; // iATRのATR値を保持(CopyBufferで取得/0=最新)。サインの上下オフセット計算(ATR*0.4)に使用
// 真偽値
bool BB2_On_Off = false; // ±2σボタン用の真偽値(オン・オフ)
bool BB3_On_Off = false; // ±3σボタン用の真偽値(オン・オフ)
bool Alert_On_Off = false; // Alertボタン用の真偽値(オン・オフ)
bool Repeat_On_Off = false; // Repeatボタンの真偽値(オン・オフ)
bool All_Keisan = false; // 全範囲再計算用の真偽値
// 同一バーでの多重通知防止のため、最後に通知した確定足の時刻を保持
datetime LastAlertTime_U_BB2 = 0; // +2σ(多重通知防止)
datetime LastAlertTime_U_BB3 = 0; // +3σ(多重通知防止)
datetime LastAlertTime_L_BB2 = 0; // -2σ(多重通知防止)
datetime LastAlertTime_L_BB3 = 0; // -3σ(多重通知防止)
// サインを出す条件
input bool Zero_Signal = true; // 未確定足でもサイン/アラートを出す(true=出す, false=確定足のみ)
//+------------------------------------------------------------------+
//| 初期設定 |
//+------------------------------------------------------------------+
int OnInit()
{
/*-----------------------------------------------------------------
ArraySetAsSeries は、配列の並び順を「0番目=最新バー(現在足)」にするか
「0番目=最古バー」にするかを切り替えるスイッチです(true で“0=最新”、false で“0=最古”)
------------------------------------------------------------------*/
// 計算・描画で扱う全バッファを「0=最新」の時系列に統一(参照方向の不一致を防止)
ArraySetAsSeries(ATR_Buffer, true); // ATR値(サイン位置のオフセット計算に使用)
ArraySetAsSeries(U_BB2_Buffer, true); // +2σバンドの値
ArraySetAsSeries(M_BB2_Buffer, true); // ±2σミドルバンドの値
ArraySetAsSeries(L_BB2_Buffer, true); // -2σバンドの値
ArraySetAsSeries(U_BB3_Buffer, true); // +3σバンドの値
ArraySetAsSeries(M_BB3_Buffer, true); // ±3σミドルバンドの値
ArraySetAsSeries(L_BB3_Buffer, true); // -3σバンドの値
ArraySetAsSeries(U_BB2_Line_Buffer, true); // 描画用ライン配列:+2σ
ArraySetAsSeries(L_BB2_Line_Buffer, true); // 描画用ライン配列:-2σ
ArraySetAsSeries(M_BB2_Line_Buffer, true); // 描画用ライン配列:±2σ
ArraySetAsSeries(U_BB3_Line_Buffer, true); // 描画用ライン配列:+3σ
ArraySetAsSeries(L_BB3_Line_Buffer, true); // 描画用ライン配列:-3σ
ArraySetAsSeries(M_BB3_Line_Buffer, true); // 描画用ライン配列:±3σ
ArraySetAsSeries(U_BB2_Sign_Buffer, true); // 矢印サイン用:+2σ
ArraySetAsSeries(U_BB3_Sign_Buffer, true); // 矢印サイン用:+3σ
ArraySetAsSeries(L_BB2_Sign_Buffer, true); // 矢印サイン用:-2σ
ArraySetAsSeries(L_BB3_Sign_Buffer, true); // 矢印サイン用:-3σ
/*-----------------------------------------------------------------
SetIndexBuffer は、インジケータの「描画対象(線や矢印=プロット)」に対して
「この配列を使って描いてください」と紐づける関数で
(第1引数=プロット番号、第2引数=配列、第3引数=役割〈通常は INDICATOR_DATA〉)。
------------------------------------------------------------------*/
// インジケータ出力バッファの割り当て(プロット番号 ⇔ 配列を対応付け)
SetIndexBuffer(0, U_BB2_Sign_Buffer, INDICATOR_DATA); // プロット0:+2σ(上タッチサイン)
SetIndexBuffer(1, U_BB3_Sign_Buffer, INDICATOR_DATA); // プロット1:+3σ(上タッチサイン)
SetIndexBuffer(2, L_BB2_Sign_Buffer, INDICATOR_DATA); // プロット2:-2σ(下タッチサイン)
SetIndexBuffer(3, L_BB3_Sign_Buffer, INDICATOR_DATA); // プロット3:-3σ(下タッチサイン)
SetIndexBuffer(4, U_BB2_Line_Buffer, INDICATOR_DATA); // プロット4:+2σ(上バンドライン)
SetIndexBuffer(5, L_BB2_Line_Buffer, INDICATOR_DATA); // プロット5:-2σ(下バンドライン)
SetIndexBuffer(6, M_BB2_Line_Buffer, INDICATOR_DATA); // プロット6:±2σ(ミドルライン)
SetIndexBuffer(7, U_BB3_Line_Buffer, INDICATOR_DATA); // プロット7:+3σ(上バンドライン)
SetIndexBuffer(8, L_BB3_Line_Buffer, INDICATOR_DATA); // プロット8:-3σ(下バンドライン)
SetIndexBuffer(9, M_BB3_Line_Buffer, INDICATOR_DATA); // プロット9:±3σ(ミドルライン)
/*-----------------------------------------------------------------
ArrayInitialize は、指定した配列の全要素を一度に同じ値で埋める(初期化する)ための関数で、
インジケータでは起動直後や表示を消したいときに描画用バッファを EMPTY_VALUE で一括クリア
配列の向き(Series の真偽)は変わらず、
要素数ぶん処理するため大きな配列では負荷がかかる点に注意が必要
------------------------------------------------------------------*/
// 残像防止:起動直後はサイン/ラインをすべて非表示(EMPTY_VALUE で初期化)
ArrayInitialize(U_BB2_Sign_Buffer, EMPTY_VALUE); // +2σ 上タッチサイン 非表示
ArrayInitialize(U_BB3_Sign_Buffer, EMPTY_VALUE); // +3σ 上タッチサイン 非表示
ArrayInitialize(L_BB2_Sign_Buffer, EMPTY_VALUE); // -2σ 下タッチサイン 非表示
ArrayInitialize(L_BB3_Sign_Buffer, EMPTY_VALUE); // -3σ 下タッチサイン 非表示
ArrayInitialize(U_BB2_Line_Buffer, EMPTY_VALUE); // +2σ 上バンドライン 非表示
ArrayInitialize(L_BB2_Line_Buffer, EMPTY_VALUE); // -2σ 下バンドライン 非表示
ArrayInitialize(M_BB2_Line_Buffer, EMPTY_VALUE); // ±2σ ミドルライン 非表示
ArrayInitialize(U_BB3_Line_Buffer, EMPTY_VALUE); // +3σ 上バンドライン 非表示
ArrayInitialize(L_BB3_Line_Buffer, EMPTY_VALUE); // -3σ 下バンドライン 非表示
ArrayInitialize(M_BB3_Line_Buffer, EMPTY_VALUE); // ±3σ ミドルライン 非表示
/*-----------------------------------------------------------------
PlotIndexSetInteger は、特定のプロット(線・矢印など)の整数で指定する設定を行う関数です。
引数は「①プロット番号 ②プロパティID(例:PLOT_ARROW/PLOT_LINE_STYLE/PLOT_LINE_WIDTH/PLOT_DRAW_BEGIN/PLOT_SHIFT/PLOT_SHOW_DATA)③設定値」。
矢印の形や線の種類・太さ、描画開始位置や右シフトなどの見た目・挙動を変えるときに使い、
初期の設定は通常 OnInit でまとめて行います。
なお、小数を指定する項目は PlotIndexSetDouble、文字列は PlotIndexSetString を使います。
------------------------------------------------------------------*/
// プロットの設定(矢印はPLOT_ARROW/ラインは空値時に非表示)
PlotIndexSetInteger(0, PLOT_ARROW, 110); // BB(+2σ)のサインの種類(■)
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); // データがないときに、サインを出ないようにするための処理(+2σ サイン)
PlotIndexSetInteger(1, PLOT_ARROW, 110); // BB(+3σ)のサインの種類(■)
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE); // データがないときに、サインを出ないようにするための処理(+3σ サイン)
PlotIndexSetInteger(2, PLOT_ARROW, 110); // BB(-2σ)のサインの種類(■)
PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE); // データがないときに、サインを出ないようにするための処理(-2σ サイン)
PlotIndexSetInteger(3, PLOT_ARROW, 110); // BB(-3σ)のサインの種類(■)
PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, EMPTY_VALUE); // データがないときに、サインを出ないようにするための処理(-3σ サイン)
PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, EMPTY_VALUE); // データがないときに、サインを出ないようにするための処理(+2σ ライン)
PlotIndexSetDouble(5, PLOT_EMPTY_VALUE, EMPTY_VALUE); // データがないときに、サインを出ないようにするための処理(-2σ ライン)
PlotIndexSetDouble(6, PLOT_EMPTY_VALUE, EMPTY_VALUE); // データがないときに、サインを出ないようにするための処理(±2σ ミドルライン)
PlotIndexSetDouble(7, PLOT_EMPTY_VALUE, EMPTY_VALUE); // データがないときに、サインを出ないようにするための処理(+3σ ライン)
PlotIndexSetDouble(8, PLOT_EMPTY_VALUE, EMPTY_VALUE); // データがないときに、サインを出ないようにするための処理(-3σ ライン)
PlotIndexSetDouble(9, PLOT_EMPTY_VALUE, EMPTY_VALUE); // データがないときに、サインを出ないようにするための処理(±3σ ミドルライン)
/*-----------------------------------------------------------------
iATR は、チャートの「1本ごとの値幅」の平均(ATR)を計算する内蔵インジケータを作成し、そのハンドルを返す関数です。
使い方は、銘柄(_Symbol)・時間足(_Period)・期間(例:14)を指定してハンドルを受け取り、CopyBuffer で ATR の数値を取り出し、
使い終えたら IndicatorRelease で解放します(失敗時は INVALID_HANDLE)。
------------------------------------------------------------------*/
// ATRインジケータのハンドル取得(失敗時は初期化中止)
ATR_Handle = iATR(_Symbol, _Period, ATR_Kikan);
if(ATR_Handle == INVALID_HANDLE)
{
Print("ATRハンドル作成に失敗");
return INIT_FAILED;
}
/*-----------------------------------------------------------------
iBands は、「価格の平均線(ミドル)と、その上下に広がるボリンジャーバンド(ばらつきの幅)」を
計算する内蔵インジケータを作成し、そのハンドルを返す関数です。
銘柄(_Symbol)・時間足(_Period)・期間(MAの本数)・偏差(例:2.0/3.0)・バンドシフト(通常0)・適用価格(ENUM_APPLIED_PRICE)を
指定してハンドルを取得し、値は CopyBuffer で読み出します(バッファ番号は 0=ミドル、1=上バンド、2=下バンド)。
使い終わったら IndicatorRelease で解放し、失敗時は INVALID_HANDLE が返ります。
------------------------------------------------------------------*/
// ボリンジャーバンド(偏差2.0)のハンドル取得(失敗時は初期化中止)
BB2_Handle = iBands(_Symbol, _Period, BB_Kikan, 0, 2.0, BB_Price);
if(BB2_Handle == INVALID_HANDLE)
{
Print("BB(2σ) ハンドル作成失敗");
return INIT_FAILED;
}
// ボリンジャーバンド(偏差3.0)のハンドル取得(失敗時は初期化中止)
BB3_Handle = iBands(_Symbol, _Period, BB_Kikan, 0, 3.0, BB_Price);
if(BB3_Handle == INVALID_HANDLE)
{
Print("BB(3σ) ハンドル作成失敗");
return INIT_FAILED;
}
// ボタン(UI)の生成
BB2_CreateButton(); // ±2σ touchボタンを作成
BB3_CreateButton(); // ±3σ touchボタンを作成
Alert_CreateButton(); // Alertボタンを作成
Repeat_CreateButton(); // Repeatボタンを作成
// 直近で確定した足(インデックス1)の開始時刻で既読化(多重通知防止)
datetime Kidoku_Time = iTime(_Symbol, _Period, 1);
LastAlertTime_U_BB2 = Kidoku_Time;
LastAlertTime_U_BB3 = Kidoku_Time;
LastAlertTime_L_BB2 = Kidoku_Time;
LastAlertTime_L_BB3 = Kidoku_Time;
return(INIT_SUCCEEDED);
}
/* =====================================================================
ボタン・テキストボックス作成用関数
===================================================================== */
// ±2σ touchボタンの作成用関数
void BB2_CreateButton()
{
ObjectDelete(0, BB2_Btn_Waku); // 既存の枠を削除してから作成
ObjectCreate(0, BB2_Btn_Waku, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 矩形ラベル
ObjectSetInteger(0, BB2_Btn_Waku, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上に配置
ObjectSetInteger(0, BB2_Btn_Waku, OBJPROP_XDISTANCE, 20); // X方向距離(横方向)
ObjectSetInteger(0, BB2_Btn_Waku, OBJPROP_YDISTANCE, 20); // Y方向距離(縦方向)
ObjectSetInteger(0, BB2_Btn_Waku, OBJPROP_XSIZE, 125); // 横サイズ
ObjectSetInteger(0, BB2_Btn_Waku, OBJPROP_YSIZE, 24); // 縦サイズ
ObjectSetInteger(0, BB2_Btn_Waku, OBJPROP_COLOR, clrBlack); // 枠線色
ObjectSetInteger(0, BB2_Btn_Waku, OBJPROP_BGCOLOR, BB2_On_Off ? clrLime : clrWhite); // ON/OFFで背景色変更
ObjectDelete(0, BB2_Btn_Label); // 既存の文字ラベル削除
ObjectCreate(0, BB2_Btn_Label, OBJ_LABEL, 0, 0, 0); // 文字ラベル
ObjectSetInteger(0, BB2_Btn_Label, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上に配置
ObjectSetInteger(0, BB2_Btn_Label, OBJPROP_XDISTANCE, 35); // X方向距離(横方向)
ObjectSetInteger(0, BB2_Btn_Label, OBJPROP_YDISTANCE, 25); // Y方向距離(縦方向)
ObjectSetInteger(0, BB2_Btn_Label, OBJPROP_FONTSIZE, 10); // 文字サイズ
ObjectSetInteger(0, BB2_Btn_Label, OBJPROP_COLOR, clrBlack); // 文字色
ObjectSetString (0, BB2_Btn_Label, OBJPROP_TEXT, BB2_On_Off ? "±2σ touch [ON]" : "±2σ touch [OFF]"); // テキスト
}
// ±3σ touchボタンの作成用関数
void BB3_CreateButton()
{
ObjectDelete(0, BB3_Btn_Waku); // 既存の枠を削除してから作成
ObjectCreate(0, BB3_Btn_Waku, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 矩形ラベル
ObjectSetInteger(0, BB3_Btn_Waku, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上に配置
ObjectSetInteger(0, BB3_Btn_Waku, OBJPROP_XDISTANCE, 20); // X方向距離(横方向)
ObjectSetInteger(0, BB3_Btn_Waku, OBJPROP_YDISTANCE, 50); // Y方向距離(縦方向)
ObjectSetInteger(0, BB3_Btn_Waku, OBJPROP_XSIZE, 125); // 横サイズ
ObjectSetInteger(0, BB3_Btn_Waku, OBJPROP_YSIZE, 24); // 縦サイズ
ObjectSetInteger(0, BB3_Btn_Waku, OBJPROP_COLOR, clrBlack); // 枠線色
ObjectSetInteger(0, BB3_Btn_Waku, OBJPROP_BGCOLOR, BB3_On_Off ? clrLime : clrWhite); // ON/OFFで背景色変更
ObjectDelete(0, BB3_Btn_Label); // 既存の文字ラベル削除
ObjectCreate(0, BB3_Btn_Label, OBJ_LABEL, 0, 0, 0); // 文字ラベル
ObjectSetInteger(0, BB3_Btn_Label, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上に配置
ObjectSetInteger(0, BB3_Btn_Label, OBJPROP_XDISTANCE, 35); // X方向距離(横方向)
ObjectSetInteger(0, BB3_Btn_Label, OBJPROP_YDISTANCE, 55); // Y方向距離(縦方向)
ObjectSetInteger(0, BB3_Btn_Label, OBJPROP_FONTSIZE, 10); // 文字サイズ
ObjectSetInteger(0, BB3_Btn_Label, OBJPROP_COLOR, clrBlack); // 文字色
ObjectSetString (0, BB3_Btn_Label, OBJPROP_TEXT, BB3_On_Off ? "±3σ touch [ON]" : "±3σ touch [OFF]"); // テキスト
}
// Alertボタンの作成用関数
void Alert_CreateButton()
{
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, 110); // Y方向距離(縦方向)
ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_XSIZE, 125); // 横サイズ
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, 35); // X方向距離(横方向)
ObjectSetInteger(0, Alert_Btn_Label, OBJPROP_YDISTANCE, 115); // 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 ? "Alert [ON]" : "Alert [OFF]"); // テキスト
}
// Repeatボタンの作成用関数
void Repeat_CreateButton()
{
ObjectDelete(0, Repeat_Btn_Waku); // 既存の枠を削除してから作成
ObjectCreate(0, Repeat_Btn_Waku, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 矩形ラベル
ObjectSetInteger(0, Repeat_Btn_Waku, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上に配置
ObjectSetInteger(0, Repeat_Btn_Waku, OBJPROP_XDISTANCE, 20); // X方向距離(横方向)
ObjectSetInteger(0, Repeat_Btn_Waku, OBJPROP_YDISTANCE, 80); // Y方向距離(縦方向)
ObjectSetInteger(0, Repeat_Btn_Waku, OBJPROP_XSIZE, 125); // 横サイズ
ObjectSetInteger(0, Repeat_Btn_Waku, OBJPROP_YSIZE, 24); // 縦サイズ
ObjectSetInteger(0, Repeat_Btn_Waku, OBJPROP_COLOR, clrBlack); // 枠線色
ObjectSetInteger(0, Repeat_Btn_Waku, OBJPROP_BGCOLOR, Repeat_On_Off ? clrLime : clrWhite); // ON/OFFで背景色変更
ObjectDelete(0, Repeat_Btn_Label); // 既存の文字ラベル削除
ObjectCreate(0, Repeat_Btn_Label, OBJ_LABEL, 0, 0, 0); // 文字ラベル
ObjectSetInteger(0, Repeat_Btn_Label, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上に配置
ObjectSetInteger(0, Repeat_Btn_Label, OBJPROP_XDISTANCE, 35); // X方向距離(横方向)
ObjectSetInteger(0, Repeat_Btn_Label, OBJPROP_YDISTANCE, 85); // Y方向距離(縦方向)
ObjectSetInteger(0, Repeat_Btn_Label, OBJPROP_FONTSIZE, 10); // 文字サイズ
ObjectSetInteger(0, Repeat_Btn_Label, OBJPROP_COLOR, clrBlack); // 文字色
ObjectSetString (0, Repeat_Btn_Label, OBJPROP_TEXT, Repeat_On_Off ? "Repeat [ON]" : "Repeat [OFF]"); // テキスト
}
/* =====================================================================
クリックのイベント処理
===================================================================== */
void OnChartEvent(const int id, const long &l, const double &d, const string &s)
{
// 「+2σ touch」ボタンのクリック処理
if(id==CHARTEVENT_OBJECT_CLICK && (s==BB2_Btn_Waku || s==BB2_Btn_Label)) // ボタンの枠かラベルをクリックした時
{
BB2_On_Off = !BB2_On_Off; // ON/OFF切替
if(BB2_On_Off) // ボタンがONの時
{
All_Keisan = true; // 全体再計算フラグをONに
}
ObjectSetString (0, BB2_Btn_Label, OBJPROP_TEXT, BB2_On_Off ? "±2σ touch [ON]" : "±2σ touch [OFF]"); // ラベルの反映
ObjectSetInteger(0, BB2_Btn_Waku, OBJPROP_BGCOLOR, BB2_On_Off ? clrLime : clrWhite); // 背景色の反映
if(Doniti) // 土日対策
{
ChartSetSymbolPeriod(0, _Symbol, Period()); // 土日対策(再読込)
}
ChartRedraw(); return;
}
// 「+3σ touch」ボタンのクリック処理
if(id==CHARTEVENT_OBJECT_CLICK && (s==BB3_Btn_Waku || s==BB3_Btn_Label)) // ボタンの枠かラベルをクリックした時
{
BB3_On_Off = !BB3_On_Off; // ON/OFF切替
if(BB3_On_Off) // ボタンがONの時
{
All_Keisan = true; // 全体再計算フラグをONに
}
ObjectSetString (0, BB3_Btn_Label, OBJPROP_TEXT, BB3_On_Off ? "±3σ touch [ON]" : "±3σ touch [OFF]"); // ラベルの反映
ObjectSetInteger(0, BB3_Btn_Waku, OBJPROP_BGCOLOR, BB3_On_Off ? clrLime : clrWhite); // 背景色の反映
if(Doniti) // 土日対策
{
ChartSetSymbolPeriod(0, _Symbol, Period()); // 土日対策(再読込)
}
ChartRedraw(); return;
}
// 「Alert」ボタンのクリック処理
if(id==CHARTEVENT_OBJECT_CLICK && (s==Alert_Btn_Waku || s==Alert_Btn_Label)) // ボタンの枠かラベルをクリックした時
{
Alert_On_Off = !Alert_On_Off; // ON/OFF切替
ObjectSetString (0, Alert_Btn_Label, OBJPROP_TEXT, Alert_On_Off ? "Alert [ON]" : "Alert [OFF]"); // 見た目反映
ObjectSetInteger(0, Alert_Btn_Waku, OBJPROP_BGCOLOR, Alert_On_Off ? clrLime : clrWhite); // 背景色の反映
// 直近の確定足(1本前)の開始時刻を取得します。
// 過去バーでアラートが連発しないよう、この時刻を各アラートの「最後に通知した時刻」にそろえます。
datetime Last_Close_Time = iTime(_Symbol, _Period, 1);
if(Last_Close_Time > 0)
{
LastAlertTime_U_BB2 = Last_Close_Time; // 「+2σバンドにタッチ」の最終通知時刻として設定
LastAlertTime_U_BB3 = Last_Close_Time; // 「+3σバンドにタッチ」の最終通知時刻として設定
LastAlertTime_L_BB2 = Last_Close_Time; // 「-2σバンドにタッチ」の最終通知時刻として設定
LastAlertTime_L_BB3 = Last_Close_Time; // 「-3σバンドにタッチ」の最終通知時刻として設定
}
if(Doniti) // 土日対策
{
ChartSetSymbolPeriod(0, _Symbol, Period()); // 土日対策(再読込)
}
ChartRedraw(); return;
}
// 「Repeat」ボタンのクリック処理
if(id==CHARTEVENT_OBJECT_CLICK && (s==Repeat_Btn_Waku || s==Repeat_Btn_Label)) // ボタンの枠かラベルをクリックした時
{
Repeat_On_Off = !Repeat_On_Off; // ON/OFF切替
All_Keisan = true; // 全体再計算フラグをONに
ObjectSetString (0, Repeat_Btn_Label, OBJPROP_TEXT, Repeat_On_Off ? "Repeat [ON]" : "Repeat [OFF]"); // 見た目反映
ObjectSetInteger(0, Repeat_Btn_Waku, OBJPROP_BGCOLOR, Repeat_On_Off ? clrLime : clrWhite); // 背景色の反映
if(Doniti) // 土日対策
{
ChartSetSymbolPeriod(0, _Symbol, Period()); // 土日対策(再読込)
}
ChartRedraw(); return;
}
}
/* =====================================================================
関数
===================================================================== */
// 過去5年分の有効バー数を求める(series=0が最新前提)
// 最新バーの開始時刻から約5年分(うるう年は考慮せず)さかのぼった基準時刻に最も近いバー位置を取得し、0〜その位置までを有効本数として返す
int GetFive(const datetime &time[], const int rates_total)
{
datetime latest = time[0]; // 最新バーの開始時刻(もっとも新しい足)
datetime cutoff = latest - (datetime)(86400*365*5); // 約5年ぶん秒数を引いた基準時刻(うるう年は考慮しない簡易計算)
// 基準時刻に「いちばん近い」バーのインデックスを取得(exact=false:ぴったりでなく近い方を返す)
int idx = iBarShift(_Symbol, _Period, cutoff, false);
if(idx < 0) idx = rates_total - 1; // 取得失敗などで負なら、最古バーを指すように補正
if(idx > rates_total - 1) idx = rates_total - 1; // 念のため、上限(最古バー)でクリップ
return(idx + 1); // インデックス0〜idx までの本数= 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は、ループ内で「今の足(i)」と「1本前の足(i+1)」を同時に参照するため、配列の範囲外アクセスを防ぐ目的で、
BBやATRの必要本数にさらに2本を上乗せして最低限のデータ数を確保
デフォルトでATR50 BB20なので少なくとも52本はローソク足が必要
52本以下になることは基本的にないのですが、0ではないので念のため配列の範囲外アクセスを防ぐ必要があります。
------------------------------------------------------------------*/
if(rates_total < (int)MathMax(BB_Kikan, ATR_Kikan) + 2)
{
return(0);
}
// ±2σtouchボタンがオフの時
if(!BB2_On_Off)
{
ArrayInitialize(U_BB2_Sign_Buffer, EMPTY_VALUE); // +2σのサインを全消去
ArrayInitialize(L_BB2_Sign_Buffer, EMPTY_VALUE); // -2σのサインを全消去
ArrayInitialize(U_BB2_Line_Buffer, EMPTY_VALUE); // +2σのラインを全消去
ArrayInitialize(L_BB2_Line_Buffer, EMPTY_VALUE); // -2σのラインを全消去
ArrayInitialize(M_BB2_Line_Buffer, EMPTY_VALUE); // ±2σのミドルラインを全消去
}
// ±3σtouchボタンがオフの時
if(!BB3_On_Off)
{
ArrayInitialize(U_BB3_Sign_Buffer, EMPTY_VALUE); // +3σのサインを全消去
ArrayInitialize(L_BB3_Sign_Buffer, EMPTY_VALUE); // -3σのサインを全消去
ArrayInitialize(U_BB3_Line_Buffer, EMPTY_VALUE); // +3σのラインを全消去
ArrayInitialize(L_BB3_Line_Buffer, EMPTY_VALUE); // -3σのラインを全消去
ArrayInitialize(M_BB3_Line_Buffer, EMPTY_VALUE); // ±3σのミドルラインを全消去
}
// 「±2σ touch」「±3σ touch」2つのボタンがOFFなら何も描かないで終了
if(!BB2_On_Off && !BB3_On_Off)
{
return(rates_total);
}
/*-----------------------------------------------------------------
Zero_Signalでは、未確定足[0]でもサインとアラート出すか出さないかを決めています。
input bool Zero_Signal = true; // 未確定足でもインサ/Alertを出す(true=出す, false=確定足のみ)
trueの時は0(未確定足)でも「サインは出す」「アラートは出す」「バンドは出す」
falseの時は0(未確定足)時は、「サインは出さない」「アラートは出さない」「バンドは出す」
------------------------------------------------------------------*/
// Zero_Signalがfalse(確定足のみ)の時、0(未確定足)の±2σと±3σのサインを消す
if(!Zero_Signal)
{
U_BB2_Sign_Buffer[0] = EMPTY_VALUE;
U_BB3_Sign_Buffer[0] = EMPTY_VALUE;
L_BB2_Sign_Buffer[0] = EMPTY_VALUE;
L_BB3_Sign_Buffer[0] = EMPTY_VALUE;
}
// 過去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);
/*-----------------------------------------------------------------
PlotIndexSetInteger(p, PLOT_DRAW_BEGIN, begin) は「プロット p の描画開始位置を指定する」呼び出しで、
PLOT_DRAW_BEGIN に入れた本数だけ古いバーを描かずにスキップします(データ自体は消えません)。
// 具体例:全体30,000本のうち、直近5,000本だけ描画したい
int rates_total = 30000;
int Bars5_Or_Total = 5000;
int begin = MathMax(0, rates_total - Bars5_Or_Total);
25,000(左=古い側の25,000本は描かない
for(int p=0; p<10; ++p)
PlotIndexSetInteger(p, PLOT_DRAW_BEGIN, begin);
各プロットの表示開始を「25,001本目」に揃える
結果:古い25,000本は非表示、直近5,000本だけが描画される(隠すのは表示だけ。計算結果はそのまま)
------------------------------------------------------------------*/
// 5年以降は全プロット非表示
int begin = MathMax(0, rates_total - Bars5_Or_Total);
for(int p=0; p<10; ++p)
{
PlotIndexSetInteger(p, PLOT_DRAW_BEGIN, begin);
}
/*-----------------------------------------------------------------
「Zenkai_Taisyougai」は、前回どこから描き始めたか(begin の位置)を覚えておくための数字です。
今回の begin が前回より小さくなったとき=表示範囲が過去側へ広がったときだけ、
その新しく見えるようになった部分を一度消して(EMPTY_VALUE にして)から描き直します。
その合図として All_Keisan を true にします。
処理が終わったら、次回の比較のために今回の begin を「Zenkai_Taisyougai」に保存します。
初期値の -1 は「前回の値はまだ無い」という印で、begin は 0 以上なので初回は判定が動かず、余計な消去処理が起きません。
------------------------------------------------------------------*/
static int Zenkai_Taisyougai = -1;
// 今回の begin(begin = MathMax(0, rates_total - Bars5_Or_Total);) が小さくなった=描画範囲が左側へ拡大したか
bool Left_Kakudai = (Zenkai_Taisyougai >= 0 && begin < Zenkai_Taisyougai);
/*-----------------------------------------------------------------
用語の確認
begin … 左の古いバーを「何本」隠すか(= 表示を始める位置)。0 以上の整数。
旧begin … 前回の begin の値(= Zenkai_Taisyougai)
新begin … 今回の begin の値
series 配列 … 0 が最新バー(右端)。通常の並びのインデックス k は、seriesでは s = (rates_total-1) - k に変換する
▼何をしているか
新begin < 旧begin なら、表示範囲が左(過去)へ広がりました。
「新しく見えるようになった通常インデックス範囲」は [新begin .. 旧begin-1] です。
その範囲だけ一度 EMPTY_VALUE を入れて“表示を消し”、次の計算で描き直します。
------------------------------------------------------------------*/
// 「過去側に表示が広がった」ときだけ処理する
if (Left_Kakudai) // (判定)新begin < 旧begin のときだけ実行
{
// 「[新begin .. 旧begin-1]」を series の範囲に直したもの
int s_from = rates_total - Zenkai_Taisyougai; // = 旧begin-1 を series に直した左端(5000 のような値)
int s_to = rates_total - 1 - begin; // = 新begin を series に直した右端(5099 のような値)
if (s_from <= s_to)
{
// 上で求めた「新しく見える部分」だけ一時的に非表示にする(計算データ自体は消さない)
for (int i = s_from; i <= s_to; ++i)
{
U_BB2_Sign_Buffer[i]=EMPTY_VALUE;
U_BB3_Sign_Buffer[i]=EMPTY_VALUE;
L_BB2_Sign_Buffer[i]=EMPTY_VALUE;
L_BB3_Sign_Buffer[i]=EMPTY_VALUE;
U_BB2_Line_Buffer[i]=EMPTY_VALUE;
L_BB2_Line_Buffer[i]=EMPTY_VALUE;
M_BB2_Line_Buffer[i]=EMPTY_VALUE;
U_BB3_Line_Buffer[i]=EMPTY_VALUE;
L_BB3_Line_Buffer[i]=EMPTY_VALUE;
M_BB3_Line_Buffer[i]=EMPTY_VALUE;
}
}
All_Keisan = true; // 次の計算で今消した範囲を描き直す合図
}
// 「10本あるプロット(矢印・ラインなど)すべての「描き始め位置」を同じ begin にそろえ、古いバーを左側からまとめて非表示にする」処理
for(int p=0; p<10; ++p)
{
PlotIndexSetInteger(p, PLOT_DRAW_BEGIN, begin);
}
// 今回の描画開始位置(begin)を保存する。次回の処理では、この値を「前回の begin」として比較に使う。
Zenkai_Taisyougai = begin;
// CopyBuffer を呼ぶ前に、「今回は初回か/全体を計算し直すべきか」を判定してフラグに入れる
// true なら全体(直近5年分など)をコピー・計算、false なら増分だけ処理する
bool Syokai_Or_AllKeisan = (prev_calculated==0) || All_Keisan;
// inc は前回計算以降に増えたバー本数で、初回は0 とし、負になり得る場合は0に切り上げます。
int inc = (prev_calculated==0) ? 0 : MathMax(0, rates_total - prev_calculated);
// need は初回/全再計算なら Bars5_Or_Total、それ以外は(inc+2)を下限2本・上限 Bars5_Or_Total で挟んだ本数とします。
int need = Syokai_Or_AllKeisan ? Bars5_Or_Total : MathMax(2, MathMin(Bars5_Or_Total, inc + 2));
// ATRの値を、バッファ0(最新起点)から need 本だけ ATR_Bufferに読み出し、その取得本数をATR_Copyに受け取ります。
int ATR_Copy = CopyBuffer(ATR_Handle, 0, 0, need, ATR_Buffer);
// ボリンジャーバンドのバッファ番号は 0=ミドル、1=上バンド、2=下バンド です。
int M_BB2_Copy = CopyBuffer(BB2_Handle, 0, 0, need, M_BB2_Buffer); // BB(2σ)のミドル線を最新からneed本読み込み、その取得本数をM_BB2_Copyに受け取ります。
int U_BB2_Copy = CopyBuffer(BB2_Handle, 1, 0, need, U_BB2_Buffer); // BB(2σ)の上バンドを最新からneed本読み込み、その取得本数をU_BB2_Copyに受け取ります。
int L_BB2_Copy = CopyBuffer(BB2_Handle, 2, 0, need, L_BB2_Buffer); // BB(2σ)の下バンドを最新からneed本読み込み、その取得本数をL_BB2_Copyに受け取ります。
int M_BB3_Copy = CopyBuffer(BB3_Handle, 0, 0, need, M_BB3_Buffer); // BB(3σ)のミドル線を最新からneed本読み込み、その取得本数をM_BB3_Copyに受け取ります。
int U_BB3_Copy = CopyBuffer(BB3_Handle, 1, 0, need, U_BB3_Buffer); // BB(3σ)の上バンドを最新からneed本読み込み、その取得本数をU_BB3_Copyに受け取ります。
int L_BB3_Copy = CopyBuffer(BB3_Handle, 2, 0, need, L_BB3_Buffer); // BB(3σ)の下バンドを最新からneed本読み込み、その取得本数をL_BB3_Copyに受け取ります。
// CopyBuffer の返却本数を検証(ミドルを含む全系列)し、未取得・不足があれば未定義参照や誤描画を防ぐため早期終了する
if(ATR_Copy<=0 || U_BB2_Copy<=0 || L_BB2_Copy<=0 || M_BB2_Copy<=0 ||
U_BB3_Copy<=0 || L_BB3_Copy<=0 || M_BB3_Copy<=0)
{
// 取得失敗時は前回計算を維持して抜ける
return prev_calculated;
}
// ここは MinEnd_Or_Zouka を計算する前の下準備で、未確定足(インデックス0)の扱いを整えます。
// Zero_Signal が false の場合は、未確定足では「サインは出さず、ラインだけ最新値で更新する」方針にします。
if(!Zero_Signal)
{
// ±2σのライン表示設定
if(BB2_On_Off)
{
U_BB2_Line_Buffer[0] = U_BB2_Buffer[0]; // 未確定足(0)の+2σ上バンドの線を最新値で反映します。
L_BB2_Line_Buffer[0] = L_BB2_Buffer[0]; // 未確定足(0)の-2σ下バンドの線を最新値で反映します。
M_BB2_Line_Buffer[0] = M_BB2_Buffer[0]; // 未確定足(0)の±2σミドル線を最新値で反映します。
}
else
{
U_BB2_Line_Buffer[0] = EMPTY_VALUE; // ±2σがOFFのため、未確定足(0)の上バンド線は非表示にします。
L_BB2_Line_Buffer[0] = EMPTY_VALUE; // ±2σがOFFのため、未確定足(0)の下バンド線は非表示にします。
M_BB2_Line_Buffer[0] = EMPTY_VALUE; // ±2σがOFFのため、未確定足(0)のミドル線は非表示にします。
}
// ±3σのライン表示設定
if(BB3_On_Off)
{
U_BB3_Line_Buffer[0] = U_BB3_Buffer[0]; // 未確定足(0)の+3σ上バンドの線を最新値で反映します。
L_BB3_Line_Buffer[0] = L_BB3_Buffer[0]; // 未確定足(0)の-3σ下バンドの線を最新値で反映します。
M_BB3_Line_Buffer[0] = M_BB3_Buffer[0]; // 未確定足(0)の±3σミドル線を最新値で反映します。
}
else
{
U_BB3_Line_Buffer[0] = EMPTY_VALUE; // ±3σがOFFのため、未確定足(0)の上バンド線は非表示にします。
L_BB3_Line_Buffer[0] = EMPTY_VALUE; // ±3σがOFFのため、未確定足(0)の下バンド線は非表示にします。
M_BB3_Line_Buffer[0] = EMPTY_VALUE; // ±3σがOFFのため、未確定足(0)のミドル線は非表示にします。
}
}
// 配列境界の安全側クリップ(i+1 を参照する配列は -2、i のみは -1)
int End_ATR = ATR_Copy - 1; // ATR配列:iのみを使用(i+1は未使用)→ 終端インデックス = ATR_Copy - 1
int End_Price = need - 2; // 価格は i+1 を見るので need に合わせて -2
int End_U_BB2 = U_BB2_Copy - 2; // +2σ上バンド:i+1を参照する処理がある → 終端インデックス = U_BB2_Copy - 2
int End_L_BB2 = L_BB2_Copy - 2; // -2σ下バンド:i+1を参照する処理がある → 終端インデックス = L_BB2_Copy - 2
int End_U_BB3 = U_BB3_Copy - 2; // +3σ上バンド:i+1を参照する処理がある → 終端インデックス = U_BB3_Copy - 2
int End_L_BB3 = L_BB3_Copy - 2; // -3σ下バンド:i+1を参照する処理がある → 終端インデックス = L_BB3_Copy - 2
// ボリンジャーバンド(+2σ/-2σ、+3σ/-3σ)、価格、ATRの各終端インデックス(End_*)の中から最小値を採用する。
// MathMin(a, b) は小さいほうを返す。入れ子で6要素の最小値を求める。
// End_Price は i+1 を参照する処理があるため終端は「本数-2」。
// End_ATR は i のみ参照するため終端は「本数-1」。
// 各配列で安全に参照できる「最後の位置」(iやi+1を使う都合で上限が異なる)をまとめて比べ、範囲外アクセスを防ぐため最も小さい終端インデックスをMin_Endに設定します。
int Min_End = MathMin(
MathMin(MathMin(End_U_BB2, End_L_BB2), MathMin(End_U_BB3, End_L_BB3)),
MathMin(End_Price, End_ATR)
);
// Min_End が(Zero_Signalに応じた必要最小インデックス:未確定足を使うなら0/確定足のみなら1)を下回る場合はデータ不足のため以降の計算を行わず、現状の本数(rates_total)を返して早期終了します。
if (Min_End < (Zero_Signal ? 0 : 1))
{
return(rates_total);
}
// 「今回どれだけ再計算するか」を求めており、初回(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;
// メインループの開始位置を、未確定足も評価する設定なら0(バー0)/確定足のみなら1に設定します。
int MainLoopStart = (Zero_Signal ? 0 : 1);
/*
メインの for ループを「どこまで回すか(最後のインデックス)」を決めます。
─────────────────────────────────────────────────────────────────────
1) Syokai_Or_AllKeisan ? Min_End : ... について
・Syokai_Or_AllKeisan が true(=初回計算、または「全再計算」の合図が出た)なら、
参照安全な最大位置 Min_End まで一気に計算します(全面再計算)。
・false(=前回以降の増分だけ計算すればよい)なら、右側の式で「必要な分だけ」の終点を算出します。
2) 右側の MathMin(MainLoopStart + MathMax(1, MinEnd_Or_Zouka + Yobi) - 1, Min_End) について
・MainLoopStart …… ループ開始位置(Zero_Signal に応じて 0 か 1)。未確定足も評価するなら 0、確定足のみなら 1。
・MinEnd_Or_Zouka … 今回再計算が必要な本数。
= 初回は Min_End(安全に回せる最大)、2回目以降は「現在本数 − 前回計算済本数」の増分(0 未満は 0)。
・Yobi ……………… 安全マージンの 1 本。インジ計算で i+1 を参照する箇所の“境目”抜け漏れを防ぐため、1 本だけ余分に回します。
・MathMax(1, MinEnd_Or_Zouka + Yobi) … 増分が 0 のときでも最低 1 本は回して表示更新が止まらないようにします。
・MainLoopStart + (本数) - 1 … 「開始インデックスに“回す本数”を足して終点インデックスに直す」ための -1 調整です。
・最後の MathMin(…, Min_End) … どんな計算でも参照安全な上限 Min_End を超えないようにクリップします。
3) これにより実現できること
・初回は全域を正しく構築、以降は新規バーや再描画が必要な最小範囲だけを再計算して効率化。
・i+1 参照の境目での取りこぼし防止(=Yobi)。
・常に配列境界内に収めて、安全で破綻しない走行(=Min_End での上限クリップ)。
*/
int MainLoopEnd = Syokai_Or_AllKeisan ? Min_End: MathMin(MainLoopStart + MathMax(1, MinEnd_Or_Zouka + Yobi) - 1, Min_End);
// 万一、計算した「終了位置」が「開始位置」より小さい(範囲が逆転)場合は、これ以上処理せず安全に抜けて前回までの結果を保ちます。
if(MainLoopEnd < MainLoopStart)
{
return(rates_total);
}
// // 全体再計算の合図(All_Keisan)を処理後に必ず解除し、次回は通常どおり差分計算に戻すためのリセットです。
All_Keisan = false;
/*-----------------------------------------------------------------
メインのループ処理
------------------------------------------------------------------*/
for(int i=MainLoopStart; i<=MainLoopEnd; i++)
{
// 矢印の上下幅:ATR×Haba(0.4) を採用
double ATR_Haba = ATR_Buffer[i] * Haba;
// タッチ判定に使うため、各バンドの「現在の足 i」と「直前の足 i+1」の値を取得します。
double U_BB2_Now = U_BB2_Buffer[i]; // +2σ 上バンドの現在値(i)
double U_BB2_Prev = U_BB2_Buffer[i+1]; // +2σ 上バンドの直前値(i+1)
double L_BB2_Now = L_BB2_Buffer[i]; // -2σ 下バンドの現在値(i)
double L_BB2_Prev = L_BB2_Buffer[i+1]; // -2σ 下バンドの直前値(i+1)
double U_BB3_Now = U_BB3_Buffer[i]; // +3σ 上バンドの現在値(i)
double U_BB3_Prev = U_BB3_Buffer[i+1]; // +3σ 上バンドの直前値(i+1)
double L_BB3_Now = L_BB3_Buffer[i]; // -3σ 下バンドの現在値(i)
double L_BB3_Prev = L_BB3_Buffer[i+1]; // -3σ 下バンドの直前値(i+1)
// 「タッチ」判定
bool Touch_U_BB2 = (high[i] >= U_BB2_Now) && (high[i+1] < U_BB2_Prev); // +2σ上バンドに新規タッチ:今足の高値が現在の+2σ以上 かつ 前足の高値は前回+2σ未満
bool Touch_U_BB3 = (high[i] >= U_BB3_Now) && (high[i+1] < U_BB3_Prev); // +3σ上バンドに新規タッチ:今足の高値が現在の+3σ以上 かつ 前足の高値は前回+3σ未満
bool Touch_L_BB2 = (low[i] <= L_BB2_Now) && (low[i+1] > L_BB2_Prev); // -2σ下バンドに新規タッチ:今足の安値が現在の-2σ以下 かつ 前足の安値は前回-2σより上
bool Touch_L_BB3 = (low[i] <= L_BB3_Now) && (low[i+1] > L_BB3_Prev); // -3σ下バンドに新規タッチ:今足の安値が現在の-3σ以下 かつ 前足の安値は前回-3σより上
// 「タッチ」判定(RepeatボタンがONの時用の)
bool Touch_U_BB2_Repeat = (high[i] >= U_BB2_Now); // +2σ上バンドに新規タッチ:今足の高値が現在の+2σ以上
bool Touch_U_BB3_Repeat = (high[i] >= U_BB3_Now); // +3σ上バンドに新規タッチ:今足の高値が現在の+3σ以上
bool Touch_L_BB2_Repeat = (low[i] <= L_BB2_Now); // -2σ下バンドに新規タッチ:今足の安値が現在の-2σ以下
bool Touch_L_BB3_Repeat = (low[i] <= L_BB3_Now); // -3σ下バンドに新規タッチ:今足の安値が現在の-3σ以下
// ±2σ touchボタンがONの時
if(BB2_On_Off)
{
if(Repeat_On_Off) // RepeatボタンがONの時
{
U_BB2_Sign_Buffer[i] = Touch_U_BB2_Repeat ? (high[i] + ATR_Haba) : EMPTY_VALUE; // +2σタッチ(連続)成立なら高値の少し上にサイン、未成立は非表示
L_BB2_Sign_Buffer[i] = Touch_L_BB2_Repeat ? (low[i] - ATR_Haba) : EMPTY_VALUE; // -2σタッチ(連続)成立なら安値の少し下にサイン、未成立は非表示
}
else // RepeatボタンがOFFの時
{
U_BB2_Sign_Buffer[i] = Touch_U_BB2 ? (high[i] + ATR_Haba) : EMPTY_VALUE; // +2σタッチ成立なら高値の少し上にサイン、未成立は非表示
L_BB2_Sign_Buffer[i] = Touch_L_BB2 ? (low[i] - ATR_Haba) : EMPTY_VALUE; // -2σタッチ成立なら安値の少し下にサイン、未成立は非表示
}
U_BB2_Line_Buffer[i] = U_BB2_Buffer[i]; // +2σのラインを描画
L_BB2_Line_Buffer[i] = L_BB2_Buffer[i]; // -2σのラインを描画
M_BB2_Line_Buffer[i] = M_BB2_Buffer[i]; // ±2σのラインを描画
}
else
{
U_BB2_Line_Buffer[i] = EMPTY_VALUE; // 表示OFF:+2σのラインを非表示
L_BB2_Line_Buffer[i] = EMPTY_VALUE; // 表示OFF:-2σのラインを非表示
M_BB2_Line_Buffer[i] = EMPTY_VALUE; // 表示OFF:±2σのミドルラインを非表示
}
// ±3σ touchボタンがONの時
if(BB3_On_Off)
{
if(Repeat_On_Off) // RepeatボタンがONの時
{
U_BB3_Sign_Buffer[i] = Touch_U_BB3_Repeat ? (high[i] + ATR_Haba) : EMPTY_VALUE; // +3σタッチ(連続)成立なら高値の少し上にサイン、未成立は非表示
L_BB3_Sign_Buffer[i] = Touch_L_BB3_Repeat ? (low[i] - ATR_Haba) : EMPTY_VALUE; // -3σタッチ(連続)成立なら安値の少し下にサイン、未成立は非表示
}
else // RepeatボタンがOFFの時
{
U_BB3_Sign_Buffer[i] = Touch_U_BB3 ? (high[i] + ATR_Haba) : EMPTY_VALUE; // +3σタッチ成立なら高値の少し上にサイン、未成立は非表示
L_BB3_Sign_Buffer[i] = Touch_L_BB3 ? (low[i] - ATR_Haba) : EMPTY_VALUE; // -3σタッチ成立なら安値の少し下にサイン、未成立は非表示
}
U_BB3_Line_Buffer[i] = U_BB3_Buffer[i]; // +3σのラインを描画
L_BB3_Line_Buffer[i] = L_BB3_Buffer[i]; // -3σのラインを描画
M_BB3_Line_Buffer[i] = M_BB3_Buffer[i]; // ±3σのラインを描画
}
else
{
U_BB3_Line_Buffer[i] = EMPTY_VALUE; // 表示OFF:+3σのラインを非表示
L_BB3_Line_Buffer[i] = EMPTY_VALUE; // 表示OFF:-3σのラインを非表示
M_BB3_Line_Buffer[i] = EMPTY_VALUE; // 表示OFF:±3σのミドルラインを非表示
}
// アラートの判定に使うバーを切り替え(Zero_Signal=true なら未確定足[0]、false なら直近の確定足[1] を評価)
const int Alert_Bar = (Zero_Signal ? 0 : 1);
// アラートの実行条件:評価対象のバー(i==Alert_Bar:0=未確定/1=直近確定)かつ機能がONのときだけ動かし、最終通知時刻(LastAlertTime_*)で同一バーの重複通知を防ぎます
if(i==Alert_Bar && Alert_On_Off)
{
if(BB2_On_Off) // 「±2σ touch」がONのときだけ判定します
{
// +2σサインがこのバーで出ており、まだこのバーでは通知していなければ通知します
if(U_BB2_Sign_Buffer[i]!=EMPTY_VALUE && (LastAlertTime_U_BB2==0 || time[i]>LastAlertTime_U_BB2))
{
Alert(StringFormat("[%s] +2σ TOUCH %s (TF:%s)", _Symbol, TimeToString(time[i], TIME_DATE|TIME_MINUTES), EnumToString((ENUM_TIMEFRAMES)_Period))); // +2σタッチを通知
LastAlertTime_U_BB2 = time[i]; // 通知したバーの時刻を「最終通知時刻」として保存します
}
// -2σサインが出ていて未通知なら通知し、最終通知時刻を更新します
else if(L_BB2_Sign_Buffer[i]!=EMPTY_VALUE && (LastAlertTime_L_BB2==0 || time[i]>LastAlertTime_L_BB2))
{
Alert(StringFormat("[%s] -2σ TOUCH %s (TF:%s)", _Symbol, TimeToString(time[i], TIME_DATE|TIME_MINUTES), EnumToString((ENUM_TIMEFRAMES)_Period))); // -2σタッチを通知
LastAlertTime_L_BB2 = time[i]; // 通知したバーの時刻を保存します
}
}
if(BB3_On_Off) // 「±3σ touch」がONのときだけ判定します
{
// +3σサインが出ていて未通知なら通知し、最終通知時刻を更新します
if(U_BB3_Sign_Buffer[i]!=EMPTY_VALUE && (LastAlertTime_U_BB3==0 || time[i]>LastAlertTime_U_BB3))
{
Alert(StringFormat("[%s] +3σ TOUCH %s (TF:%s)", _Symbol, TimeToString(time[i], TIME_DATE|TIME_MINUTES), EnumToString((ENUM_TIMEFRAMES)_Period))); // +3σタッチを通知
LastAlertTime_U_BB3 = time[i]; // 通知したバーの時刻を保存します
}
// -3σサインが出ていて未通知なら通知し、最終通知時刻を更新します
else if(L_BB3_Sign_Buffer[i]!=EMPTY_VALUE && (LastAlertTime_L_BB3==0 || time[i]>LastAlertTime_L_BB3))
{
Alert(StringFormat("[%s] -3σ TOUCH %s (TF:%s)", _Symbol, TimeToString(time[i], TIME_DATE|TIME_MINUTES), EnumToString((ENUM_TIMEFRAMES)_Period))); // -3σタッチを通知
LastAlertTime_L_BB3 = time[i]; // 通知したバーの時刻を保存します
}
}
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//| 削除処理 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// 1) 先に各ハンドルを必ず解放(二重解放防止のため INVALID_HANDLE へ)
if(ATR_Handle != INVALID_HANDLE){ IndicatorRelease(ATR_Handle); ATR_Handle = INVALID_HANDLE; }
if(BB2_Handle != INVALID_HANDLE){ IndicatorRelease(BB2_Handle); BB2_Handle = INVALID_HANDLE; }
if(BB3_Handle != INVALID_HANDLE){ IndicatorRelease(BB3_Handle); BB3_Handle = INVALID_HANDLE; }
// 2) チャート変更/パラメータ変更/再コンパイル時はUIを残す(サイン配列だけクリアして残像防止)
if(reason==REASON_CHARTCHANGE || reason==REASON_PARAMETERS || reason==REASON_RECOMPILE)
{
ArrayInitialize(U_BB2_Sign_Buffer, EMPTY_VALUE);
ArrayInitialize(U_BB3_Sign_Buffer, EMPTY_VALUE);
ArrayInitialize(L_BB2_Sign_Buffer, EMPTY_VALUE);
ArrayInitialize(L_BB3_Sign_Buffer, EMPTY_VALUE);
ChartRedraw();
return; // UIオブジェクトは削除しない
}
// 3) 本当に終了する場合のみ、UIオブジェクトを削除
ObjectDelete(0, BB2_Btn_Waku); ObjectDelete(0, BB2_Btn_Label);
ObjectDelete(0, BB3_Btn_Waku); ObjectDelete(0, BB3_Btn_Label);
ObjectDelete(0, Alert_Btn_Waku); ObjectDelete(0, Alert_Btn_Label);
ObjectDelete(0, Repeat_Btn_Waku); ObjectDelete(0, Repeat_Btn_Label);
// サイン配列のクリア(任意)
ArrayInitialize(U_BB2_Sign_Buffer, EMPTY_VALUE);
ArrayInitialize(U_BB3_Sign_Buffer, EMPTY_VALUE);
ArrayInitialize(L_BB2_Sign_Buffer, EMPTY_VALUE);
ArrayInitialize(L_BB3_Sign_Buffer, EMPTY_VALUE);
ChartRedraw();
}



コメント