移動平均線(MA)にタッチした瞬間にサイン&アラート通知を出すMT4・MT5インジケーターを無料配布!
設定画面を開かず、チャート上のパネルで期間変更やSMA/EMA切替が可能。ソースコードも完全公開し、UI作成や計算ロジックを詳しく解説しています。
MAタッチ通知パネル・インジケーター 取扱説明書

このインジケーターは、ローソク足が移動平均線(MA)にタッチした瞬間に、チャート上に矢印(買いサイン形状)を表示し、アラートやスマホ通知でお知らせするツールです。
最大の特徴は、チャート上に表示される操作パネルです。
設定画面を開くことなく、期間の変更や判定基準(実体/ヒゲ)、通知のON/OFFをワンクリックで切り替えることができます。
パネル全体の表示/非表示を切り替えます。
チャートを広く見たい時はOFFにします。
単純移動平均線(Simple Moving Average)を使用します。
SMA・EMAどちらか片方が緑色(ON)になります。
指数平滑移動平均線(Exponential Moving Average)を使用します。
SMA・EMAどちらか片方が緑色(ON)になります。
足が確定した後にサインを出します。
足が形成中でも、タッチした瞬間にサインを出します(リアルタイム通知)。
ローソク足の高値~安値(ヒゲを含む全体)がMAに触れたらサインを出します。
HIGE・JITTAIどちらか片方が緑色(ON)になります。
ローソク足の始値~終値(実体部分)がMAに触れたらサインを出します。
HIGE・JITTAIどちらか片方が緑色(ON)になります。
サインが出るとアラートが鳴ります。
デフォルトはOFFなのでアラートが必要な場合はボタンを緑色(ON)にします。
サインが出るとMT4・MT5へのスマホアプリへプッシュ通知が送られます。
デフォルトはOFFなのでプッシュ通知が必要な場合はボタンを緑色(ON)にします。
パネル下部にある2つのスライダーで、数値を直感的に調整できます。(スライダーのツマミをドラッグするか、左右の + – ボタンで微調整可能です)
移動平均線の期間を 1 ~ 200 の間で変更できます。
スライダーを動かすと、チャート上のMAのラインと過去のサインが即座に再計算され、どのように反応するかシミュレーションできます。
一度サインが出た後、次のサインが出るまでの「待機本数」を設定します(0 ~ 50 本)。
「0」の場合、タッチするたびに毎回サインが出ます。
「5」などの場合:一度サインが出たら、その後ローソク足が5本経過するまでは、再びタッチしてもサインを出しません。頻繁な通知を防ぎたい場合に便利です。
パラメータ設定(インジケーター設定画面)

インジケーター投入時、または設定画面(Ctrl+I)から変更できる項目です。
trueにすると、土日でもボタン操作時の反応が良くなるよう強制的に更新をかけます。過去のサインを土日確認したい場合のみお使い下さい。
※動作が重くなるので普段はfalse推奨
矢印を表示する位置(ローソク足からの距離)を計算するために参照する過去の足の本数です。
1440本は1分足で約1日分で、その平均の0.3倍にサインを出していますので毎回同じ位置に出るわけではありませんが、ある程度安定した位置に出ます。
サインの位置を離したい場合は、0.4と0.3と数を増やして下さい。

※二次配布や販売、自作発言は禁止です。
※本ツールによる損害の責任は負いかねます。
※学習用としてご活用下さい。
simple-ma-touch-alertのコードを公開【MT5/MQL5】
//+------------------------------------------------------------------+ // 区切り線(ここからファイル情報)
//| btn+sma_touch.mq5 | // ファイル名
//| Copyright 2025, ayase fumiya | // 著作権表記
//| https://pokyun.com/ | // 参考URL
//+------------------------------------------------------------------+ // 区切り線(ここまで)
#property copyright "ayase fumiya" // 著作権表示
#property link "https://pokyun.com/" // 作者サイト
#property version "1.00" // バージョン
#property indicator_chart_window // チャート上に表示するインジケータ
// 使用するバッファ数とプロット数
#property indicator_buffers 2 // バッファは2本使う
#property indicator_plots 2 // 描画(プロット)も2つ使う
// ---- SMAタッチ(買いサイン)
#property indicator_label1 "SMA_Touch_Buy" // 1本目の表示名
#property indicator_type1 DRAW_ARROW // 矢印で描画
#property indicator_color1 clrDeepSkyBlue // 色
#property indicator_width1 1 // 太さ
// ---- SMAライン(SMA描画用)
#property indicator_label2 "SMA_Line" // 2本目の表示名
#property indicator_type2 DRAW_LINE // 線で描画
#property indicator_color2 clrYellow // 色(黄色で見やすく)
#property indicator_style2 STYLE_SOLID // 実線
#property indicator_width2 1 // 太さ
// ボタン用オブジェクト名(枠と文字ラベル)
string alert_waku = "AlertButtonFrame"; // Alertボタンの枠名
string alert_moji = "AlertButtonText"; // Alertボタンの文字名
string push_waku = "PushButtonFrame"; // Pushボタンの枠名
string push_moji = "PushButtonText"; // Pushボタンの文字名
string open_waku = "OpenButtonFrame"; // Openボタンの枠名
string open_moji = "OpenButtonText"; // Openボタンの文字名
// 追加:KAKUTEIボタン
string kakutei_waku = "KakuteiButtonFrame"; // KAKUTEIボタンの枠名
string kakutei_moji = "KakuteiButtonText"; // KAKUTEIボタンの文字名
// 追加:MI-KAKUTEIボタン
string mikakutei_waku = "MiKakuteiButtonFrame"; // MI-KAKUTEIボタンの枠名
string mikakutei_moji = "MiKakuteiButtonText"; // MI-KAKUTEIボタンの文字名
// 追加:SMA/EMA切り替えボタン(変数名をシンプルに修正)
string sma_waku = "SMAButtonFrame"; // SMAボタンの枠名
string sma_moji = "SMAButtonText"; // SMAボタンの文字名
string ema_waku = "EMAButtonFrame"; // EMAボタンの枠名
string ema_moji = "EMAButtonText"; // EMAボタンの文字名
// 追加:HIGE/JITTAI切り替えボタン(変数名をシンプルに修正)
string hige_waku = "HIGEButtonFrame"; // HIGEボタンの枠名
string hige_moji = "HIGEButtonText"; // HIGEボタンの文字名
string jittai_waku = "JITTAIButtonFrame"; // JITTAIボタンの枠名
string jittai_moji = "JITTAIButtonText"; // JITTAIボタンの文字名
// 入力パラメータ(ATR、土日対策)
input bool doniti = false; // 土日対策:false=平日、true=土日
input int heikin_honsuu = 1440; // 平均を取る本数
input double haba = 0.3; // 平均×0.3を矢印の上下幅に使う
double heikin_nagasa = 0.0; // 足長(High-Low)の平均値
bool heikin_keisan_zumi = false; // 平均を一度だけ計算するフラグ
// バッファ
double sign_buffer[]; // 矢印(買いサイン)
double ma_buffer[]; // SMA計算・描画用バッファ
// 真偽値(スイッチ)
bool alert_switch = false; // AlertボタンのON/OFF
bool push_switch = false; // Push通知のON/OFF
bool open_switch = true; // UI表示のON/OFF
bool kakutei_switch = false; // KAKUTEIのON/OFF
bool mikakutei_switch = true; // MI-KAKUTEIのON/OFF(デフォルトON)
bool zenbu_keisan = false; // 全範囲を再計算するフラグ
// 追加:MA種別スイッチ
bool sma_switch = true; // SMAボタンのON/OFF(デフォルトON)
bool ema_switch = false; // EMAボタンのON/OFF(デフォルトOFF)
// 追加:判定種別スイッチ
bool hige_switch = true; // HIGEボタンのON/OFF(デフォルトON)
bool jittai_switch = false; // JITTAIボタンのON/OFF(デフォルトOFF)
// 多重通知防止
datetime last_alert_time = 0; // 矢印の最終通知時刻
// ------------------------------------------------------------------
// SMA Period Slider (変数名を kikan_slider に変更)
// ------------------------------------------------------------------
string kikan_slider_namae_ = "SMASlider_"; // スライダー1の共通名
string kikan_slider_haikei = kikan_slider_namae_ + "BG"; // 背景名
string kikan_slider_tumami = kikan_slider_namae_ + "Knob"; // つまみ名
string kikan_slider_atai_label = kikan_slider_namae_ + "Value"; // 値表示名
// + / - 追加
string kikan_slider_minus = kikan_slider_namae_ + "Minus"; // マイナスボタン名
string kikan_slider_plus = kikan_slider_namae_ + "Plus"; // プラスボタン名
int kikan_slider_x = 25; // スライダー1のX位置
int kikan_slider_y = 92; // スライダー1のY位置
int kikan_slider_habasa = 200; // スライダー1の横幅
int kikan_slider_takasa = 5; // スライダー1の高さ
int kikan_slider_tumami_size = 15; // つまみのサイズ
int kikan_slider_atai = 20; // スライダーの現在値(1~200)
bool kikan_slider_drug_tyu = false; // ドラッグ中かどうか
int kikan_slider_kotei_atai = 20; // 離した瞬間に確定する値
int sma_kikan = 20; // 実行時のSMA期間
bool mouse_scroll_mae = true; // 変更前のスクロール状態
int handle_ma = INVALID_HANDLE; // MAのハンドル
// ------------------------------------------------------------------
// Repeat Slider (変数名を repeat_slider に変更)
// ------------------------------------------------------------------
string repeat_slider_namae_ = "RepeatSlider_"; // スライダー2の共通名
string repeat_slider_haikei = repeat_slider_namae_ + "BG"; // 背景名
string repeat_slider_tumami = repeat_slider_namae_ + "Knob"; // つまみ名
string repeat_slider_atai_label = repeat_slider_namae_ + "Value"; // 値表示名
string repeat_slider_minus = repeat_slider_namae_ + "Minus"; // マイナスボタン名
string repeat_slider_plus = repeat_slider_namae_ + "Plus"; // プラスボタン名
int repeat_slider_x = 25; // スライダー2のX位置
int repeat_slider_y = 122; // スライダー2のY位置
int repeat_slider_habasa = 200; // スライダー2の横幅
int repeat_slider_takasa = 5; // スライダー2の高さ
int repeat_slider_tumami_size = 15; // つまみのサイズ
int repeat_slider_atai = 0; // スライダー2の現在値
bool repeat_slider_drug_tyu = false; // ドラッグ中かどうか
int repeat_slider_kotei_atai = 0; // 離した瞬間の確定値
string gv_key(string name) // グローバル変数キーを作る関数
{
return("btn+sma_touch:" + (string)ChartID() + ":" + _Symbol + ":" + name); // キーを返す
}
void ui_state_save() // UI状態を保存する関数
{
GlobalVariableSet(gv_key("alert_switch"), alert_switch ? 1.0 : 0.0); // 保存
GlobalVariableSet(gv_key("push_switch"), push_switch ? 1.0 : 0.0); // 保存
GlobalVariableSet(gv_key("open_switch"), open_switch ? 1.0 : 0.0); // 保存
GlobalVariableSet(gv_key("kakutei_switch"), kakutei_switch ? 1.0 : 0.0); // 保存
GlobalVariableSet(gv_key("mikakutei_switch"), mikakutei_switch ? 1.0 : 0.0); // 保存
GlobalVariableSet(gv_key("sma_switch"), sma_switch ? 1.0 : 0.0); // 保存
GlobalVariableSet(gv_key("ema_switch"), ema_switch ? 1.0 : 0.0); // 保存
GlobalVariableSet(gv_key("hige_switch"), hige_switch ? 1.0 : 0.0); // 保存
GlobalVariableSet(gv_key("jittai_switch"), jittai_switch ? 1.0 : 0.0); // 保存
GlobalVariableSet(gv_key("kikan_slider_atai"), (double)kikan_slider_atai); // 保存
GlobalVariableSet(gv_key("kikan_slider_kotei_atai"), (double)kikan_slider_kotei_atai); // 保存
GlobalVariableSet(gv_key("sma_kikan"), (double)sma_kikan); // 保存
GlobalVariableSet(gv_key("repeat_slider_atai"), (double)repeat_slider_atai); // 保存
GlobalVariableSet(gv_key("repeat_slider_kotei_atai"), (double)repeat_slider_kotei_atai); // 保存
}
bool ui_state_load() // UI状態を読み込む関数
{
bool loaded = false; // 読み込み済みフラグ
string k; // キー
k = gv_key("alert_switch");
if(GlobalVariableCheck(k))
{
alert_switch = (GlobalVariableGet(k) > 0.5); loaded = true;
} // 読込
k = gv_key("push_switch");
if(GlobalVariableCheck(k))
{
push_switch = (GlobalVariableGet(k) > 0.5); loaded = true;
} // 読込
k = gv_key("open_switch");
if(GlobalVariableCheck(k))
{
open_switch = (GlobalVariableGet(k) > 0.5); loaded = true;
} // 読込
k = gv_key("kakutei_switch");
if(GlobalVariableCheck(k))
{
kakutei_switch = (GlobalVariableGet(k) > 0.5); loaded = true;
} // 読込
k = gv_key("mikakutei_switch");
if(GlobalVariableCheck(k))
{
mikakutei_switch = (GlobalVariableGet(k) > 0.5); loaded = true;
} // 読込
k = gv_key("sma_switch");
if(GlobalVariableCheck(k))
{
sma_switch = (GlobalVariableGet(k) > 0.5); loaded = true;
} // 読込
k = gv_key("ema_switch");
if(GlobalVariableCheck(k))
{
ema_switch = (GlobalVariableGet(k) > 0.5); loaded = true;
} // 読込
k = gv_key("hige_switch");
if(GlobalVariableCheck(k))
{
hige_switch = (GlobalVariableGet(k) > 0.5); loaded = true;
} // 読込
k = gv_key("jittai_switch");
if(GlobalVariableCheck(k))
{
jittai_switch = (GlobalVariableGet(k) > 0.5); loaded = true;
} // 読込
k = gv_key("kikan_slider_atai");
if(GlobalVariableCheck(k))
{
kikan_slider_atai = (int)MathRound(GlobalVariableGet(k)); loaded = true;
} // 読込
k = gv_key("kikan_slider_kotei_atai");
if(GlobalVariableCheck(k))
{
kikan_slider_kotei_atai = (int)MathRound(GlobalVariableGet(k)); loaded = true;
} // 読込
k = gv_key("sma_kikan");
if(GlobalVariableCheck(k))
{
sma_kikan = (int)MathRound(GlobalVariableGet(k)); loaded = true;
} // 読込
k = gv_key("repeat_slider_atai");
if(GlobalVariableCheck(k))
{
repeat_slider_atai = (int)MathRound(GlobalVariableGet(k)); loaded = true;
} // 読込
k = gv_key("repeat_slider_kotei_atai");
if(GlobalVariableCheck(k))
{
repeat_slider_kotei_atai = (int)MathRound(GlobalVariableGet(k)); loaded = true;
} // 読込
if(kikan_slider_atai < 1)
{
kikan_slider_atai = 1;
} // 1未満なら1へ
if(kikan_slider_atai > 200)
{
kikan_slider_atai = 200;
} // 200超なら200へ
if(kikan_slider_kotei_atai < 1)
{
kikan_slider_kotei_atai = 1;
} // 1未満なら1へ
if(kikan_slider_kotei_atai > 200)
{
kikan_slider_kotei_atai = 200;
} // 200超なら200へ
if(sma_kikan < 1)
{
sma_kikan = 1;
} // 1未満なら1へ
if(sma_kikan > 200)
{
sma_kikan = 200;
} // 200超なら200へ
if(repeat_slider_atai < 0)
{
repeat_slider_atai = 0;
} // 0未満なら0へ
if(repeat_slider_atai > 50)
{
repeat_slider_atai = 50;
} // 50超なら50へ
if(repeat_slider_kotei_atai < 0)
{
repeat_slider_kotei_atai = 0;
} // 0未満なら0へ
if(repeat_slider_kotei_atai > 50)
{
repeat_slider_kotei_atai = 50;
} // 50超なら50へ
if(sma_switch && ema_switch)
{
ema_switch = false;
} // 片方にする
if(!sma_switch && !ema_switch)
{
sma_switch = true;
} // どちらかON
if(hige_switch && jittai_switch)
{
jittai_switch = false;
} // 片方にする
if(!hige_switch && !jittai_switch)
{
hige_switch = true;
} // どちらかON
if(kakutei_switch && mikakutei_switch)
{
mikakutei_switch = false;
} // 片方にする
if(!kakutei_switch && !mikakutei_switch)
{
mikakutei_switch = true;
} // どちらかON
return(loaded); // 読み込み結果を返す
}
//+------------------------------------------------------------------+
//| 過去5年分の有効バー数を求める(series=0が最新前提) |
//+------------------------------------------------------------------+
int kako_5nen_check(const datetime &time[], const int rates_total) // 5年分のバー数を返す関数
{
datetime latest = time[0]; // 最新バーの時間
datetime kijyun_jikan = latest - (datetime)(86400 * 365 * 5); // 5年前の基準時刻
int idx = iBarShift(_Symbol, _Period, kijyun_jikan, false); // 5年前に近いバー位置を探す
if(idx < 0) // 見つからない場合
{
//idx = rates_total - 1; // いちばん古いバーにする
idx = 200; //初動対策
}
if(idx > rates_total - 1) // 範囲外なら
{
idx = rates_total - 1; // 最大に丸める
}
return(idx + 1); // 0始まりなので+1して本数にする
}
//+------------------------------------------------------------------+
//| MAハンドルを更新する関数 |
//+------------------------------------------------------------------+
void ma_handle_kousin(int kikan) // ハンドル更新関数(★関数名変更)
{
if(handle_ma != INVALID_HANDLE) // 既にハンドルがあるなら
{
IndicatorRelease(handle_ma); // 解放する
}
//スイッチの状態を見てSMAかEMAかを決定する
ENUM_MA_METHOD method = MODE_SMA; // デフォルトはSMA
if(ema_switch)
{
method = MODE_EMA;
} // EMAスイッチがONならEMA
handle_ma = iMA(_Symbol, _Period, kikan, 0, method, PRICE_CLOSE); // 新しい期間とメソッドで作成
}
//+------------------------------------------------------------------+
//| Alertボタン作成 |
//+------------------------------------------------------------------+
void alert_button_tukuru() // Alertボタンを作る関数
{
ObjectDelete(0, alert_waku); // 既存の枠を削除
ObjectCreate(0, alert_waku, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 枠を作成
ObjectSetInteger(0, alert_waku, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, alert_waku, OBJPROP_XDISTANCE, 85); // X位置
ObjectSetInteger(0, alert_waku, OBJPROP_YDISTANCE, 20); // Y位置
ObjectSetInteger(0, alert_waku, OBJPROP_XSIZE, 80); // 横幅
ObjectSetInteger(0, alert_waku, OBJPROP_YSIZE, 24); // 高さ
ObjectSetInteger(0, alert_waku, OBJPROP_COLOR, clrBlack); // 枠線色
ObjectSetInteger(0, alert_waku, OBJPROP_BGCOLOR, alert_switch ? clrLime : clrWhite); // 背景色(ONなら緑)
ObjectDelete(0, alert_moji); // 既存の文字を削除
ObjectCreate(0, alert_moji, OBJ_LABEL, 0, 0, 0); // 文字ラベルを作成
ObjectSetInteger(0, alert_moji, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, alert_moji, OBJPROP_ANCHOR, ANCHOR_CENTER); // 左上基準
ObjectSetInteger(0, alert_moji, OBJPROP_XDISTANCE, 125); // 文字のX位置
ObjectSetInteger(0, alert_moji, OBJPROP_YDISTANCE, 32); // 文字のY位置
ObjectSetInteger(0, alert_moji, OBJPROP_FONTSIZE, 10); // 文字サイズ
ObjectSetInteger(0, alert_moji, OBJPROP_COLOR, clrBlack); // 文字色
ObjectSetString (0, alert_moji, OBJPROP_TEXT, alert_switch ? "Alert" : "Alert"); // 表示文字
}
//+------------------------------------------------------------------+
//| Pushボタン作成(Alertの横) |
//+------------------------------------------------------------------+
void push_button_tukuru() // Pushボタンを作る関数
{
ObjectDelete(0, push_waku); // 既存の枠を削除
ObjectCreate(0, push_waku, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 枠を作成
ObjectSetInteger(0, push_waku, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
// Alertボタンの右側に配置
ObjectSetInteger(0, push_waku, OBJPROP_XDISTANCE, 170); // X位置
ObjectSetInteger(0, push_waku, OBJPROP_YDISTANCE, 20); // Y位置
ObjectSetInteger(0, push_waku, OBJPROP_XSIZE, 80); // 横幅
ObjectSetInteger(0, push_waku, OBJPROP_YSIZE, 24); // 高さ
ObjectSetInteger(0, push_waku, OBJPROP_COLOR, clrBlack); // 枠線色
ObjectSetInteger(0, push_waku, OBJPROP_BGCOLOR, push_switch ? clrLime : clrWhite); // 背景色(ONなら緑)
ObjectDelete(0, push_moji); // 既存の文字を削除
ObjectCreate(0, push_moji, OBJ_LABEL, 0, 0, 0); // 文字ラベルを作成
ObjectSetInteger(0, push_moji, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, push_moji, OBJPROP_ANCHOR, ANCHOR_CENTER); // 左上基準
ObjectSetInteger(0, push_moji, OBJPROP_XDISTANCE, 210); // 文字X位置
ObjectSetInteger(0, push_moji, OBJPROP_YDISTANCE, 32); // 文字Y位置
ObjectSetInteger(0, push_moji, OBJPROP_FONTSIZE, 10); // 文字サイズ
ObjectSetInteger(0, push_moji, OBJPROP_COLOR, clrBlack); // 文字色
ObjectSetString (0, push_moji, OBJPROP_TEXT, "Push"); // 表示文字
}
// 追加:SMAボタン作成(Pushの横)
void sma_button_tukuru() // SMAボタンを作る関数
{
ObjectDelete(0, sma_waku); // 既存の枠を削除
ObjectCreate(0, sma_waku, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 枠を作成
ObjectSetInteger(0, sma_waku, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, sma_waku, OBJPROP_XDISTANCE, 255); // X位置
ObjectSetInteger(0, sma_waku, OBJPROP_YDISTANCE, 20); // Y位置
ObjectSetInteger(0, sma_waku, OBJPROP_XSIZE, 80); // 横幅
ObjectSetInteger(0, sma_waku, OBJPROP_YSIZE, 24); // 高さ
ObjectSetInteger(0, sma_waku, OBJPROP_COLOR, clrBlack); // 枠線色
ObjectSetInteger(0, sma_waku, OBJPROP_BGCOLOR, sma_switch ? clrLime : clrWhite); // 背景色(ONなら緑)
ObjectDelete(0, sma_moji); // 既存の文字を削除
ObjectCreate(0, sma_moji, OBJ_LABEL, 0, 0, 0); // 文字ラベルを作成
ObjectSetInteger(0, sma_moji, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, sma_moji, OBJPROP_ANCHOR, ANCHOR_CENTER); // 左上基準
ObjectSetInteger(0, sma_moji, OBJPROP_XDISTANCE, 295); // 文字X位置
ObjectSetInteger(0, sma_moji, OBJPROP_YDISTANCE, 32); // 文字Y位置
ObjectSetInteger(0, sma_moji, OBJPROP_FONTSIZE, 10); // 文字サイズ
ObjectSetInteger(0, sma_moji, OBJPROP_COLOR, clrBlack); // 文字色
ObjectSetString (0, sma_moji, OBJPROP_TEXT, "SMA"); // 表示文字
}
// 追加:EMAボタン作成(SMAの横)
void ema_button_tukuru() // EMAボタンを作る関数
{
ObjectDelete(0, ema_waku); // 既存の枠を削除
ObjectCreate(0, ema_waku, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 枠を作成
ObjectSetInteger(0, ema_waku, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, ema_waku, OBJPROP_XDISTANCE, 340); // X位置
ObjectSetInteger(0, ema_waku, OBJPROP_YDISTANCE, 20); // Y位置
ObjectSetInteger(0, ema_waku, OBJPROP_XSIZE, 80); // 横幅
ObjectSetInteger(0, ema_waku, OBJPROP_YSIZE, 24); // 高さ
ObjectSetInteger(0, ema_waku, OBJPROP_COLOR, clrBlack); // 枠線色
ObjectSetInteger(0, ema_waku, OBJPROP_BGCOLOR, ema_switch ? clrLime : clrWhite); // 背景色(ONなら緑)
ObjectDelete(0, ema_moji); // 既存の文字を削除
ObjectCreate(0, ema_moji, OBJ_LABEL, 0, 0, 0); // 文字ラベルを作成
ObjectSetInteger(0, ema_moji, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, ema_moji, OBJPROP_ANCHOR, ANCHOR_CENTER); // 左上基準
ObjectSetInteger(0, ema_moji, OBJPROP_XDISTANCE, 380); // 文字X位置
ObjectSetInteger(0, ema_moji, OBJPROP_YDISTANCE, 32); // 文字Y位置
ObjectSetInteger(0, ema_moji, OBJPROP_FONTSIZE, 10); // 文字サイズ
ObjectSetInteger(0, ema_moji, OBJPROP_COLOR, clrBlack); // 文字色
ObjectSetString (0, ema_moji, OBJPROP_TEXT, "EMA"); // 表示文字
}
// 追加:HIGEボタン作成(EMAの横)
void hige_button_tukuru() // HIGEボタンを作る関数
{
ObjectDelete(0, hige_waku); // 既存の枠を削除
ObjectCreate(0, hige_waku, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 枠を作成
ObjectSetInteger(0, hige_waku, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, hige_waku, OBJPROP_XDISTANCE, 170); // X位置
ObjectSetInteger(0, hige_waku, OBJPROP_YDISTANCE, 50); // Y位置
ObjectSetInteger(0, hige_waku, OBJPROP_XSIZE, 80); // 横幅
ObjectSetInteger(0, hige_waku, OBJPROP_YSIZE, 24); // 高さ
ObjectSetInteger(0, hige_waku, OBJPROP_COLOR, clrBlack); // 枠線色
ObjectSetInteger(0, hige_waku, OBJPROP_BGCOLOR, hige_switch ? clrLime : clrWhite); // 背景色(ONなら緑)
ObjectDelete(0, hige_moji); // 既存の文字を削除
ObjectCreate(0, hige_moji, OBJ_LABEL, 0, 0, 0); // 文字ラベルを作成
ObjectSetInteger(0, hige_moji, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, hige_moji, OBJPROP_ANCHOR, ANCHOR_CENTER); // 左上基準
ObjectSetInteger(0, hige_moji, OBJPROP_XDISTANCE, 210); // 文字X位置
ObjectSetInteger(0, hige_moji, OBJPROP_YDISTANCE, 62); // 文字Y位置
ObjectSetInteger(0, hige_moji, OBJPROP_FONTSIZE, 10); // 文字サイズ
ObjectSetInteger(0, hige_moji, OBJPROP_COLOR, clrBlack); // 文字色
ObjectSetString (0, hige_moji, OBJPROP_TEXT, "HIGE"); // 表示文字
}
// 追加:JITTAIボタン作成(HIGEの横)
void jittai_button_tukuru() // JITTAIボタンを作る関数
{
ObjectDelete(0, jittai_waku); // 既存の枠を削除
ObjectCreate(0, jittai_waku, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 枠を作成
ObjectSetInteger(0, jittai_waku, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, jittai_waku, OBJPROP_XDISTANCE, 255); // X位置
ObjectSetInteger(0, jittai_waku, OBJPROP_YDISTANCE, 50); // Y位置
ObjectSetInteger(0, jittai_waku, OBJPROP_XSIZE, 80); // 横幅
ObjectSetInteger(0, jittai_waku, OBJPROP_YSIZE, 24); // 高さ
ObjectSetInteger(0, jittai_waku, OBJPROP_COLOR, clrBlack); // 枠線色
ObjectSetInteger(0, jittai_waku, OBJPROP_BGCOLOR, jittai_switch ? clrLime : clrWhite); // 背景色(ONなら緑)
ObjectDelete(0, jittai_moji); // 既存の文字を削除
ObjectCreate(0, jittai_moji, OBJ_LABEL, 0, 0, 0); // 文字ラベルを作成
ObjectSetInteger(0, jittai_moji, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, jittai_moji, OBJPROP_ANCHOR, ANCHOR_CENTER); // 左上基準
ObjectSetInteger(0, jittai_moji, OBJPROP_XDISTANCE, 295); // 文字X位置
ObjectSetInteger(0, jittai_moji, OBJPROP_YDISTANCE, 62); // 文字Y位置
ObjectSetInteger(0, jittai_moji, OBJPROP_FONTSIZE, 10); // 文字サイズ
ObjectSetInteger(0, jittai_moji, OBJPROP_COLOR, clrBlack); // 文字色
ObjectSetString (0, jittai_moji, OBJPROP_TEXT, "JITTAI"); // 表示文字
}
void open_button_tukuru() // Openボタンを作る関数
{
ObjectDelete(0, open_waku); // 既存の枠を削除
ObjectCreate(0, open_waku, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 枠を作成
ObjectSetInteger(0, open_waku, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, open_waku, OBJPROP_XDISTANCE, 0); // X位置
ObjectSetInteger(0, open_waku, OBJPROP_YDISTANCE, 20); // Y位置
ObjectSetInteger(0, open_waku, OBJPROP_XSIZE, 80); // 横幅
ObjectSetInteger(0, open_waku, OBJPROP_YSIZE, 24); // 高さ
ObjectSetInteger(0, open_waku, OBJPROP_COLOR, clrBlack); // 枠線色
ObjectSetInteger(0, open_waku, OBJPROP_BGCOLOR, open_switch ? clrLime : clrWhite); // 背景色(ONなら緑)
ObjectDelete(0, open_moji); // 既存の文字を削除
ObjectCreate(0, open_moji, OBJ_LABEL, 0, 0, 0); // 文字ラベルを作成
ObjectSetInteger(0, open_moji, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, open_moji, OBJPROP_ANCHOR, ANCHOR_CENTER); // 左上基準
ObjectSetInteger(0, open_moji, OBJPROP_XDISTANCE, 40); // 文字X位置
ObjectSetInteger(0, open_moji, OBJPROP_YDISTANCE, 32); // 文字Y位置
ObjectSetInteger(0, open_moji, OBJPROP_FONTSIZE, 10); // 文字サイズ
ObjectSetInteger(0, open_moji, OBJPROP_COLOR, clrBlack); // 文字色
ObjectSetString (0, open_moji, OBJPROP_TEXT, "Open"); // 表示文字
}
// 追加:KAKUTEIボタン作成(Openの横)
void kakutei_button_tukuru() // KAKUTEIボタンを作る関数
{
ObjectDelete(0, kakutei_waku); // 既存の枠を削除
ObjectCreate(0, kakutei_waku, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 枠を作成
ObjectSetInteger(0, kakutei_waku, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, kakutei_waku, OBJPROP_XDISTANCE, 0); // X位置(Openの右)
ObjectSetInteger(0, kakutei_waku, OBJPROP_YDISTANCE, 50); // Y位置
ObjectSetInteger(0, kakutei_waku, OBJPROP_XSIZE, 80); // 横幅
ObjectSetInteger(0, kakutei_waku, OBJPROP_YSIZE, 24); // 高さ
ObjectSetInteger(0, kakutei_waku, OBJPROP_COLOR, clrBlack); // 枠線色
ObjectSetInteger(0, kakutei_waku, OBJPROP_BGCOLOR, kakutei_switch ? clrLime : clrWhite); // 背景色(ONなら緑)
ObjectDelete(0, kakutei_moji); // 既存の文字を削除
ObjectCreate(0, kakutei_moji, OBJ_LABEL, 0, 0, 0); // 文字ラベルを作成
ObjectSetInteger(0, kakutei_moji, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, kakutei_moji, OBJPROP_ANCHOR, ANCHOR_CENTER); // 左上基準
ObjectSetInteger(0, kakutei_moji, OBJPROP_XDISTANCE, 40); // 文字X位置
ObjectSetInteger(0, kakutei_moji, OBJPROP_YDISTANCE, 62); // 文字Y位置
ObjectSetInteger(0, kakutei_moji, OBJPROP_FONTSIZE, 9); // 文字サイズ
ObjectSetInteger(0, kakutei_moji, OBJPROP_COLOR, clrBlack); // 文字色
ObjectSetString (0, kakutei_moji, OBJPROP_TEXT, "KAKUTEI"); // 表示文字
}
// 追加:MI-KAKUTEIボタン作成(KAKUTEIの横)
void mikakutei_button_tukuru() // MI-KAKUTEIボタンを作る関数
{
ObjectDelete(0, mikakutei_waku); // 既存の枠を削除
ObjectCreate(0, mikakutei_waku, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 枠を作成
ObjectSetInteger(0, mikakutei_waku, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, mikakutei_waku, OBJPROP_XDISTANCE, 85); // X位置(KAKUTEIの右)
ObjectSetInteger(0, mikakutei_waku, OBJPROP_YDISTANCE, 50); // Y位置
ObjectSetInteger(0, mikakutei_waku, OBJPROP_XSIZE, 80); // 横幅
ObjectSetInteger(0, mikakutei_waku, OBJPROP_YSIZE, 24); // 高さ
ObjectSetInteger(0, mikakutei_waku, OBJPROP_COLOR, clrBlack); // 枠線色
ObjectSetInteger(0, mikakutei_waku, OBJPROP_BGCOLOR, mikakutei_switch ? clrLime : clrWhite); // 背景色(ONなら緑)
ObjectDelete(0, mikakutei_moji); // 既存の文字を削除
ObjectCreate(0, mikakutei_moji, OBJ_LABEL, 0, 0, 0); // 文字ラベルを作成
ObjectSetInteger(0, mikakutei_moji, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, mikakutei_moji, OBJPROP_ANCHOR, ANCHOR_CENTER); // 左上基準
ObjectSetInteger(0, mikakutei_moji, OBJPROP_XDISTANCE, 125); // 文字X位置
ObjectSetInteger(0, mikakutei_moji, OBJPROP_YDISTANCE, 62); // 文字Y位置
ObjectSetInteger(0, mikakutei_moji, OBJPROP_FONTSIZE, 9); // 文字サイズ
ObjectSetInteger(0, mikakutei_moji, OBJPROP_COLOR, clrBlack); // 文字色
ObjectSetString (0, mikakutei_moji, OBJPROP_TEXT, "MI-KAKUTEI"); // 表示文字
}
void kikan_slider_tukuru() // 期間スライダーを作る関数
{
ObjectDelete(0, kikan_slider_haikei); // 背景を削除
ObjectDelete(0, kikan_slider_tumami); // つまみを削除
ObjectDelete(0, kikan_slider_atai_label); // 値ラベルを削除
ObjectDelete(0, kikan_slider_minus); // マイナスボタンを削除
ObjectDelete(0, kikan_slider_plus); // プラスボタンを削除
ObjectCreate(0, kikan_slider_haikei, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 背景を作成
ObjectSetInteger(0, kikan_slider_haikei, OBJPROP_XDISTANCE, kikan_slider_x); // 背景X位置
ObjectSetInteger(0, kikan_slider_haikei, OBJPROP_YDISTANCE, kikan_slider_y); // 背景Y位置
ObjectSetInteger(0, kikan_slider_haikei, OBJPROP_XSIZE, kikan_slider_habasa + kikan_slider_tumami_size); // 背景横幅
ObjectSetInteger(0, kikan_slider_haikei, OBJPROP_YSIZE, kikan_slider_takasa); // 背景高さ
ObjectSetInteger(0, kikan_slider_haikei, OBJPROP_BGCOLOR, clrGray); // 背景色
ObjectSetInteger(0, kikan_slider_haikei, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, kikan_slider_haikei, OBJPROP_SELECTABLE, false); // 選択不可
ObjectCreate(0, kikan_slider_tumami, OBJ_BUTTON, 0, 0, 0); // つまみを作成
ObjectSetInteger(0, kikan_slider_tumami, OBJPROP_BORDER_COLOR, clrLime); // 枠色
int knobY = kikan_slider_y - (kikan_slider_tumami_size / 2) + (kikan_slider_takasa / 2); // つまみY計算
ObjectSetInteger(0, kikan_slider_tumami, OBJPROP_YDISTANCE, knobY); // つまみY位置
ObjectSetInteger(0, kikan_slider_tumami, OBJPROP_XSIZE, kikan_slider_tumami_size); // つまみ横幅
ObjectSetInteger(0, kikan_slider_tumami, OBJPROP_YSIZE, kikan_slider_tumami_size); // つまみ高さ
ObjectSetInteger(0, kikan_slider_tumami, OBJPROP_BGCOLOR, clrLime); // つまみ背景色
ObjectSetInteger(0, kikan_slider_tumami, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, kikan_slider_tumami, OBJPROP_SELECTABLE, false); // 選択不可
ObjectSetInteger(0, kikan_slider_tumami, OBJPROP_ZORDER, 10); // 前面に表示
ObjectCreate(0, kikan_slider_atai_label, OBJ_BUTTON, 0, 0, 0); // 値ラベルを作成
ObjectSetInteger(0, kikan_slider_atai_label, OBJPROP_XDISTANCE, kikan_slider_x + kikan_slider_habasa + kikan_slider_tumami_size + 30); // ラベルX位置
ObjectSetInteger(0, kikan_slider_atai_label, OBJPROP_YDISTANCE, kikan_slider_y - 8); // ラベルY位置
ObjectSetInteger(0, kikan_slider_atai_label, OBJPROP_XSIZE, 90); // ラベル横幅
ObjectSetInteger(0, kikan_slider_atai_label, OBJPROP_YSIZE, 20); // ラベル高さ
ObjectSetInteger(0, kikan_slider_atai_label, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, kikan_slider_atai_label, OBJPROP_BGCOLOR, clrBlack); // 背景色
ObjectSetInteger(0, kikan_slider_atai_label, OBJPROP_COLOR, clrWhite); // 文字色
ObjectSetInteger(0, kikan_slider_atai_label, OBJPROP_BORDER_COLOR, clrWhite); // 枠色
ObjectSetInteger(0, kikan_slider_atai_label, OBJPROP_FONTSIZE, 10); // 文字サイズ
ObjectSetInteger(0, kikan_slider_atai_label, OBJPROP_SELECTABLE, false); // 選択不可
ObjectCreate(0, kikan_slider_minus, OBJ_BUTTON, 0, 0, 0); // 「-」ボタン作成
ObjectSetInteger(0, kikan_slider_minus, OBJPROP_XDISTANCE, kikan_slider_x - 25); // X位置
ObjectSetInteger(0, kikan_slider_minus, OBJPROP_YDISTANCE, kikan_slider_y - 8); // Y位置
ObjectSetInteger(0, kikan_slider_minus, OBJPROP_XSIZE, 20); // 横幅
ObjectSetInteger(0, kikan_slider_minus, OBJPROP_YSIZE, 20); // 高さ
ObjectSetInteger(0, kikan_slider_minus, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, kikan_slider_minus, OBJPROP_BGCOLOR, clrBlack); // 背景色
ObjectSetInteger(0, kikan_slider_minus, OBJPROP_COLOR, clrWhite); // 文字色
ObjectSetInteger(0, kikan_slider_minus, OBJPROP_BORDER_COLOR, clrWhite); // 枠色
ObjectSetInteger(0, kikan_slider_minus, OBJPROP_FONTSIZE, 12); // 文字サイズ
ObjectSetInteger(0, kikan_slider_minus, OBJPROP_SELECTABLE, false); // 選択不可
ObjectSetString (0, kikan_slider_minus, OBJPROP_TEXT, "-"); // 表示文字
ObjectCreate(0, kikan_slider_plus, OBJ_BUTTON, 0, 0, 0); // 「+」ボタン作成
ObjectSetInteger(0, kikan_slider_plus, OBJPROP_XDISTANCE, kikan_slider_x + kikan_slider_habasa + kikan_slider_tumami_size + 5); // X位置
ObjectSetInteger(0, kikan_slider_plus, OBJPROP_YDISTANCE, kikan_slider_y - 8); // Y位置
ObjectSetInteger(0, kikan_slider_plus, OBJPROP_XSIZE, 20); // 横幅
ObjectSetInteger(0, kikan_slider_plus, OBJPROP_YSIZE, 20); // 高さ
ObjectSetInteger(0, kikan_slider_plus, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, kikan_slider_plus, OBJPROP_BGCOLOR, clrBlack); // 背景色
ObjectSetInteger(0, kikan_slider_plus, OBJPROP_COLOR, clrWhite); // 文字色
ObjectSetInteger(0, kikan_slider_plus, OBJPROP_BORDER_COLOR, clrWhite); // 枠色
ObjectSetInteger(0, kikan_slider_plus, OBJPROP_FONTSIZE, 12); // 文字サイズ
ObjectSetInteger(0, kikan_slider_plus, OBJPROP_SELECTABLE, false); // 選択不可
ObjectSetString (0, kikan_slider_plus, OBJPROP_TEXT, "+"); // 表示文字
if(kikan_slider_atai < 1)
{
kikan_slider_atai = 1;
} // 1未満なら1へ
if(kikan_slider_atai > 200)
{
kikan_slider_atai = 200;
} // 200超なら200へ
// 1~200の範囲で位置計算
int knobX = kikan_slider_x + (int)((double)kikan_slider_habasa * ((double)(kikan_slider_atai - 1) / 199.0) + 0.5);
ObjectSetInteger(0, kikan_slider_tumami, OBJPROP_XDISTANCE, knobX); // つまみX位置を反映
}
void kikan_slider_kousin() // 期間スライダーの表示更新
{
if(kikan_slider_atai < 1)
{
kikan_slider_atai = 1;
}
if(kikan_slider_atai > 200)
{
kikan_slider_atai = 200;
}
ObjectSetString(0, kikan_slider_atai_label, OBJPROP_TEXT, "MA: " + (string)kikan_slider_atai); // 表示文字を更新
}
void repeat_slider_tukuru() // リピートスライダーを作る関数
{
ObjectDelete(0, repeat_slider_haikei); // 背景を削除
ObjectDelete(0, repeat_slider_tumami); // つまみを削除
ObjectDelete(0, repeat_slider_atai_label); // 値ラベルを削除
ObjectDelete(0, repeat_slider_minus); // マイナスボタンを削除
ObjectDelete(0, repeat_slider_plus); // プラスボタンを削除
ObjectCreate(0, repeat_slider_haikei, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 背景を作成
ObjectSetInteger(0, repeat_slider_haikei, OBJPROP_XDISTANCE, repeat_slider_x); // 背景X位置
ObjectSetInteger(0, repeat_slider_haikei, OBJPROP_YDISTANCE, repeat_slider_y); // 背景Y位置
ObjectSetInteger(0, repeat_slider_haikei, OBJPROP_XSIZE, repeat_slider_habasa + repeat_slider_tumami_size); // 背景横幅
ObjectSetInteger(0, repeat_slider_haikei, OBJPROP_YSIZE, repeat_slider_takasa); // 背景高さ
ObjectSetInteger(0, repeat_slider_haikei, OBJPROP_BGCOLOR, clrGray); // 背景色
ObjectSetInteger(0, repeat_slider_haikei, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, repeat_slider_haikei, OBJPROP_SELECTABLE, false); // 選択不可
ObjectCreate(0, repeat_slider_tumami, OBJ_BUTTON, 0, 0, 0); // つまみを作成
ObjectSetInteger(0, repeat_slider_tumami, OBJPROP_BORDER_COLOR, clrLime); // 枠色
int knobY = repeat_slider_y - (repeat_slider_tumami_size / 2) + (repeat_slider_takasa / 2); // つまみY計算
ObjectSetInteger(0, repeat_slider_tumami, OBJPROP_YDISTANCE, knobY); // つまみY位置
ObjectSetInteger(0, repeat_slider_tumami, OBJPROP_XSIZE, repeat_slider_tumami_size); // つまみ横幅
ObjectSetInteger(0, repeat_slider_tumami, OBJPROP_YSIZE, repeat_slider_tumami_size); // つまみ高さ
ObjectSetInteger(0, repeat_slider_tumami, OBJPROP_BGCOLOR, clrLime); // つまみ背景色
ObjectSetInteger(0, repeat_slider_tumami, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, repeat_slider_tumami, OBJPROP_SELECTABLE, false); // 選択不可
ObjectSetInteger(0, repeat_slider_tumami, OBJPROP_ZORDER, 10); // 前面に表示
ObjectCreate(0, repeat_slider_atai_label, OBJ_BUTTON, 0, 0, 0); // 値ラベルを作成
ObjectSetInteger(0, repeat_slider_atai_label, OBJPROP_XDISTANCE, repeat_slider_x + repeat_slider_habasa + repeat_slider_tumami_size + 30); // ラベルX位置
ObjectSetInteger(0, repeat_slider_atai_label, OBJPROP_YDISTANCE, repeat_slider_y - 8); // ラベルY位置
ObjectSetInteger(0, repeat_slider_atai_label, OBJPROP_XSIZE, 90); // ラベル横幅
ObjectSetInteger(0, repeat_slider_atai_label, OBJPROP_YSIZE, 20); // ラベル高さ
ObjectSetInteger(0, repeat_slider_atai_label, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, repeat_slider_atai_label, OBJPROP_BGCOLOR, clrBlack); // 背景色
ObjectSetInteger(0, repeat_slider_atai_label, OBJPROP_COLOR, clrWhite); // 文字色
ObjectSetInteger(0, repeat_slider_atai_label, OBJPROP_BORDER_COLOR, clrWhite); // 枠色
ObjectSetInteger(0, repeat_slider_atai_label, OBJPROP_FONTSIZE, 10); // 文字サイズ
ObjectSetInteger(0, repeat_slider_atai_label, OBJPROP_SELECTABLE, false); // 選択不可
ObjectCreate(0, repeat_slider_minus, OBJ_BUTTON, 0, 0, 0); // 「-」ボタン作成
ObjectSetInteger(0, repeat_slider_minus, OBJPROP_XDISTANCE, repeat_slider_x - 25); // X位置
ObjectSetInteger(0, repeat_slider_minus, OBJPROP_YDISTANCE, repeat_slider_y - 8); // Y位置
ObjectSetInteger(0, repeat_slider_minus, OBJPROP_XSIZE, 20); // 横幅
ObjectSetInteger(0, repeat_slider_minus, OBJPROP_YSIZE, 20); // 高さ
ObjectSetInteger(0, repeat_slider_minus, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, repeat_slider_minus, OBJPROP_BGCOLOR, clrBlack); // 背景色
ObjectSetInteger(0, repeat_slider_minus, OBJPROP_COLOR, clrWhite); // 文字色
ObjectSetInteger(0, repeat_slider_minus, OBJPROP_BORDER_COLOR, clrWhite); // 枠色
ObjectSetInteger(0, repeat_slider_minus, OBJPROP_FONTSIZE, 12); // 文字サイズ
ObjectSetInteger(0, repeat_slider_minus, OBJPROP_SELECTABLE, false); // 選択不可
ObjectSetString (0, repeat_slider_minus, OBJPROP_TEXT, "-"); // 表示文字
ObjectCreate(0, repeat_slider_plus, OBJ_BUTTON, 0, 0, 0); // 「+」ボタン作成
ObjectSetInteger(0, repeat_slider_plus, OBJPROP_XDISTANCE, repeat_slider_x + repeat_slider_habasa + repeat_slider_tumami_size + 5); // X位置
ObjectSetInteger(0, repeat_slider_plus, OBJPROP_YDISTANCE, repeat_slider_y - 8); // Y位置
ObjectSetInteger(0, repeat_slider_plus, OBJPROP_XSIZE, 20); // 横幅
ObjectSetInteger(0, repeat_slider_plus, OBJPROP_YSIZE, 20); // 高さ
ObjectSetInteger(0, repeat_slider_plus, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 左上基準
ObjectSetInteger(0, repeat_slider_plus, OBJPROP_BGCOLOR, clrBlack); // 背景色
ObjectSetInteger(0, repeat_slider_plus, OBJPROP_COLOR, clrWhite); // 文字色
ObjectSetInteger(0, repeat_slider_plus, OBJPROP_BORDER_COLOR, clrWhite); // 枠色
ObjectSetInteger(0, repeat_slider_plus, OBJPROP_FONTSIZE, 12); // 文字サイズ
ObjectSetInteger(0, repeat_slider_plus, OBJPROP_SELECTABLE, false); // 選択不可
ObjectSetString (0, repeat_slider_plus, OBJPROP_TEXT, "+"); // 表示文字
if(repeat_slider_atai < 0)
{
repeat_slider_atai = 0;
} // 0未満なら0へ
if(repeat_slider_atai > 50)
{
repeat_slider_atai = 50;
} // 50超なら50へ
int knobX = repeat_slider_x + (int)((double)repeat_slider_habasa * ((double)(repeat_slider_atai) / 50.0) + 0.5); // つまみX計算
ObjectSetInteger(0, repeat_slider_tumami, OBJPROP_XDISTANCE, knobX); // つまみX位置を反映
}
void repeat_slider_kousin() // リピートスライダーの表示更新
{
if(repeat_slider_atai < 0)
{
repeat_slider_atai = 0;
}
if(repeat_slider_atai > 50)
{
repeat_slider_atai = 50;
}
ObjectSetString(0, repeat_slider_atai_label, OBJPROP_TEXT, "Repeat: " + (string)repeat_slider_atai); // 表示文字を更新
}
void open_igai_kesu() // Open以外のUIを消す
{
ObjectDelete(0, alert_waku); // Alert枠を削除
ObjectDelete(0, alert_moji); // Alert文字を削除
ObjectDelete(0, push_waku); // Push枠を削除
ObjectDelete(0, push_moji); // Push文字を削除
ObjectDelete(0, kakutei_waku); // KAKUTEI枠を削除
ObjectDelete(0, kakutei_moji); // KAKUTEI文字を削除
ObjectDelete(0, mikakutei_waku); // MI-KAKUTEI枠を削除
ObjectDelete(0, mikakutei_moji); // MI-KAKUTEI文字を削除
// 追加:SMA/EMAボタン削除
ObjectDelete(0, sma_waku); // SMA枠を削除
ObjectDelete(0, sma_moji); // SMA文字を削除
ObjectDelete(0, ema_waku); // EMA枠を削除
ObjectDelete(0, ema_moji); // EMA文字を削除
// 追加:HIGE/JITTAIボタン削除
ObjectDelete(0, hige_waku); // HIGE枠を削除
ObjectDelete(0, hige_moji); // HIGE文字を削除
ObjectDelete(0, jittai_waku); // JITTAI枠を削除
ObjectDelete(0, jittai_moji); // JITTAI文字を削除
ObjectDelete(0, kikan_slider_haikei); // スライダー1背景を削除
ObjectDelete(0, kikan_slider_tumami); // スライダー1つまみを削除
ObjectDelete(0, kikan_slider_atai_label); // スライダー1値表示を削除
ObjectDelete(0, kikan_slider_minus); // スライダー1「-」を削除
ObjectDelete(0, kikan_slider_plus); // スライダー1「+」を削除
ObjectDelete(0, repeat_slider_haikei); // スライダー2背景を削除
ObjectDelete(0, repeat_slider_tumami); // スライダー2つまみを削除
ObjectDelete(0, repeat_slider_atai_label); // スライダー2値表示を削除
ObjectDelete(0, repeat_slider_minus); // スライダー2「-」を削除
ObjectDelete(0, repeat_slider_plus); // スライダー2「+」を削除
ChartSetInteger(0, CHART_MOUSE_SCROLL, true); // スクロールを通常に戻す
ChartRedraw(); // 画面を再描画
}
void zenbu_hyouzi() // UIを全部表示する
{
open_button_tukuru(); // Openは常に作る
kakutei_button_tukuru(); // KAKUTEIを作る
mikakutei_button_tukuru(); // MI-KAKUTEIを作る
alert_button_tukuru(); // Alertを作る
push_button_tukuru(); // Pushを作る
// 追加:SMA/EMAボタンを作る
sma_button_tukuru(); // SMAを作る
ema_button_tukuru(); // EMAを作る
// 追加:HIGE/JITTAIボタンを作る
hige_button_tukuru(); // HIGEを作る
jittai_button_tukuru(); // JITTAIを作る
kikan_slider_tukuru(); // スライダー1を作る
kikan_slider_kousin(); // スライダー1を表示更新
repeat_slider_tukuru(); // スライダー2を作る
repeat_slider_kousin(); // スライダー2を表示更新
ChartRedraw(); // 画面を再描画
}
//+------------------------------------------------------------------+
//| 初期化 |
//+------------------------------------------------------------------+
int OnInit() // 初期化処理
{
kikan_slider_atai = 20; // 初期値20
kikan_slider_kotei_atai = 20; // 確定値も20
sma_kikan = 20; // 計算用も20(★変数名変更)
repeat_slider_atai = 0; // 初期値0
repeat_slider_kotei_atai = 0; // 確定値も0
ui_state_load(); // 状態を読み込む
ma_handle_kousin(sma_kikan); // MAハンドルを作成
if(handle_ma == INVALID_HANDLE) // 取得失敗なら
{
return(INIT_FAILED); // 初期化失敗で終了
}
ArraySetAsSeries(sign_buffer, true); // サインバッファを最新=0にする
ArraySetAsSeries(ma_buffer, true); // MAバッファを最新=0にする
SetIndexBuffer(0, sign_buffer, INDICATOR_DATA); // バッファ0を上に割り当て
SetIndexBuffer(1, ma_buffer, INDICATOR_DATA); // MAをバッファ1に割り当て
ArrayInitialize(sign_buffer, EMPTY_VALUE); // サインバッファを空で初期化
// ma_bufferは自動描画されるのでInitialize不要(CopyBufferで上書きされるため)
PlotIndexSetInteger(0, PLOT_ARROW, 110); // 矢印コード設定
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); // 空値設定
open_button_tukuru(); // Openボタンを作る
if(open_switch) // OpenがONなら
{
zenbu_hyouzi(); // 全表示
}
else // OpenがOFFなら
{
open_igai_kesu(); // Open以外を消す
}
ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true); // マウス移動イベントを有効化
datetime Kidoku_Time = iTime(_Symbol, _Period, 1); // 直近確定足の時間を取得
last_alert_time = Kidoku_Time; // 通知の既読時刻を設定
ui_state_save(); // 状態を保存する
return(INIT_SUCCEEDED); // 初期化成功
}
//+------------------------------------------------------------------+
//| クリックイベント |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &l, const double &d, const string &s) // チャートイベント
{
// Open OFF中は、Openボタン以外のイベントは無視
if(!open_switch) // OpenがOFFなら
{
if(id == CHARTEVENT_OBJECT_CLICK && (s == open_waku || s == open_moji)) // Openボタンが押されたら
{
open_switch = true; // OpenをONにする
ObjectSetInteger(0, open_waku, OBJPROP_BGCOLOR, clrLime); // Open枠を緑にする
zenbu_hyouzi(); // UIを全部表示する
ui_state_save(); // 状態を保存する
}
return; // ここで終了
}
// ---- スライダー(ドラッグ) ----
if(id == CHARTEVENT_MOUSE_MOVE) // マウス移動イベントなら
{
int x = (int)l; // マウスX座標
int y = (int)d; // マウスY座標
int mouseState = (int)StringToInteger(s); // マウス状態(押下など)
int margin = 10; // 判定の余白
bool hover3 = (x >= kikan_slider_x - margin && x <= kikan_slider_x + kikan_slider_habasa + kikan_slider_tumami_size + margin && y >= kikan_slider_y - margin && y <= kikan_slider_y + kikan_slider_takasa + margin); // スライダー1付近か
bool hover4 = (x >= repeat_slider_x - margin && x <= repeat_slider_x + repeat_slider_habasa + repeat_slider_tumami_size + margin && y >= repeat_slider_y - margin && y <= repeat_slider_y + repeat_slider_takasa + margin); // スライダー2付近か
bool isAnyHover = (hover3 || hover4); // どちらかに乗っているか
bool isAnyDragging = (kikan_slider_drug_tyu || repeat_slider_drug_tyu); // どちらかをドラッグ中か
if(isAnyHover || isAnyDragging) // 近い or ドラッグ中なら
{
ChartSetInteger(0, CHART_MOUSE_SCROLL, false); // チャートスクロールを無効化
}
else // それ以外なら
{
ChartSetInteger(0, CHART_MOUSE_SCROLL, true); // チャートスクロールを有効化
}
if((mouseState & 1) == 1) // 左クリック中なら
{
if(!kikan_slider_drug_tyu && !repeat_slider_drug_tyu) // まだドラッグ開始していないなら
{
long knobX2 = ObjectGetInteger(0, kikan_slider_tumami, OBJPROP_XDISTANCE); // スライダー1つまみX取得
long knobY2 = ObjectGetInteger(0, kikan_slider_tumami, OBJPROP_YDISTANCE); // スライダー1つまみY取得
long knobX3 = ObjectGetInteger(0, repeat_slider_tumami, OBJPROP_XDISTANCE); // スライダー2つまみX取得
long knobY3 = ObjectGetInteger(0, repeat_slider_tumami, OBJPROP_YDISTANCE); // スライダー2つまみY取得
if(x >= knobX2 && x <= knobX2 + kikan_slider_tumami_size && // つまみ範囲内か(X)
y >= knobY2 && y <= knobY2 + kikan_slider_tumami_size) // つまみ範囲内か(Y)
{
kikan_slider_drug_tyu = true; // スライダー1をドラッグ中にする
mouse_scroll_mae = (bool)ChartGetInteger(0, CHART_MOUSE_SCROLL); // 変更前を保存
ChartSetInteger(0, CHART_MOUSE_SCROLL, false); // スクロールを止める
}
else if(x >= knobX3 && x <= knobX3 + repeat_slider_tumami_size && // つまみ範囲内か(X)
y >= knobY3 && y <= knobY3 + repeat_slider_tumami_size) // つまみ範囲内か(Y)
{
repeat_slider_drug_tyu = true; // スライダー2をドラッグ中にする
mouse_scroll_mae = (bool)ChartGetInteger(0, CHART_MOUSE_SCROLL); // 変更前を保存
ChartSetInteger(0, CHART_MOUSE_SCROLL, false); // スクロールを止める
}
}
if(kikan_slider_drug_tyu) // スライダー1をドラッグ中なら
{
int newX = x - (kikan_slider_tumami_size / 2); // つまみ中心に合わせる
if(newX < kikan_slider_x) // 左端より左なら
{
newX = kikan_slider_x; // 左端に固定
}
if(newX > kikan_slider_x + kikan_slider_habasa) // 右端より右なら
{
newX = kikan_slider_x + kikan_slider_habasa; // 右端に固定
}
ObjectSetInteger(0, kikan_slider_tumami, OBJPROP_XDISTANCE, newX); // つまみ位置を移動
double percent = (double)(newX - kikan_slider_x) / (double)kikan_slider_habasa; // 0~1の割合
// 1~200に変換
int newVal = 1 + (int)(percent * 199.0 + 0.5);
if(newVal < 1)
{
newVal = 1;
} // 下限チェック
if(newVal > 200)
{
newVal = 200;
} // 上限チェック
if(kikan_slider_atai != newVal) // 値が変わったら
{
kikan_slider_atai = newVal; // 値を更新
kikan_slider_kousin(); // 表示更新
ChartRedraw(); // 再描画
}
}
else if(repeat_slider_drug_tyu) // スライダー2をドラッグ中なら
{
int newX = x - (repeat_slider_tumami_size / 2); // つまみ中心に合わせる
if(newX < repeat_slider_x) // 左端より左なら
{
newX = repeat_slider_x; // 左端に固定
}
if(newX > repeat_slider_x + repeat_slider_habasa) // 右端より右なら
{
newX = repeat_slider_x + repeat_slider_habasa; // 右端に固定
}
ObjectSetInteger(0, repeat_slider_tumami, OBJPROP_XDISTANCE, newX); // つまみ位置を移動
double percent = (double)(newX - repeat_slider_x) / (double)repeat_slider_habasa; // 0~1の割合
int newVal = (int)(percent * 50.0 + 0.5); // 0~50に変換
if(newVal < 0)
{
newVal = 0;
} // 下限チェック
if(newVal > 50)
{
newVal = 50;
} // 上限チェック
if(repeat_slider_atai != newVal) // 値が変わったら
{
repeat_slider_atai = newVal; // 値を更新
repeat_slider_kousin(); // 表示更新
ChartRedraw(); // 再描画
}
}
}
else // 左クリックを離したら
{
if(kikan_slider_drug_tyu) // スライダー1をドラッグしていたなら
{
kikan_slider_drug_tyu = false; // ドラッグ終了
ChartSetInteger(0, CHART_MOUSE_SCROLL, mouse_scroll_mae); // スクロール状態を戻す
if(kikan_slider_kotei_atai != kikan_slider_atai) // 確定値が変わったなら
{
kikan_slider_kotei_atai = kikan_slider_atai; // 確定値を更新
sma_kikan = kikan_slider_kotei_atai; // SMA期間を更新
ma_handle_kousin(sma_kikan); // ハンドル再作成
zenbu_keisan = true; // 全再計算を要求
if(doniti) // 土日対策がONなら
{
ChartSetSymbolPeriod(0, _Symbol, Period()); // チャート更新を促す
}
}
ui_state_save(); // 状態を保存する
ChartRedraw(); // 再描画
}
else if(repeat_slider_drug_tyu) // スライダー2をドラッグしていたなら
{
repeat_slider_drug_tyu = false; // ドラッグ終了
ChartSetInteger(0, CHART_MOUSE_SCROLL, mouse_scroll_mae); // スクロール状態を戻す
if(repeat_slider_kotei_atai != repeat_slider_atai) // 確定値が変わったなら
{
repeat_slider_kotei_atai = repeat_slider_atai; // 確定値を更新
zenbu_keisan = true; // 全再計算を要求
if(doniti) // 土日対策がONなら
{
ChartSetSymbolPeriod(0, _Symbol, Period()); // チャート更新を促す
}
}
ui_state_save(); // 状態を保存する
ChartRedraw(); // 再描画
}
}
return; // ここで終了
}
if(id == CHARTEVENT_OBJECT_CLICK && (s == kikan_slider_minus || s == kikan_slider_plus)) // スライダー1の+/-が押されたら
{
if(s == kikan_slider_minus) // 「-」なら
{
kikan_slider_atai--; // 値を1減らす
}
if(s == kikan_slider_plus) // 「+」なら
{
kikan_slider_atai++; // 値を1増やす
}
if(kikan_slider_atai < 1)
{
kikan_slider_atai = 1;
} // 下限チェック
if(kikan_slider_atai > 200)
{
kikan_slider_atai = 200;
} // 上限チェック
// つまみ位置反映
int knobX = kikan_slider_x + (int)((double)kikan_slider_habasa * ((double)(kikan_slider_atai - 1) / 199.0) + 0.5);
ObjectSetInteger(0, kikan_slider_tumami, OBJPROP_XDISTANCE, knobX); // つまみ位置を反映
kikan_slider_kousin(); // 表示更新
kikan_slider_kotei_atai = kikan_slider_atai; // 確定値を更新
sma_kikan = kikan_slider_kotei_atai; // SMA期間を更新
ma_handle_kousin(sma_kikan); // ハンドル再作成
zenbu_keisan = true; // 全再計算を要求
if(doniti) // 土日対策がONなら
{
ChartSetSymbolPeriod(0, _Symbol, Period()); // チャート更新を促す
}
ui_state_save(); // 状態を保存する
ChartRedraw(); // 再描画
return; // ここで終了
}
if(id == CHARTEVENT_OBJECT_CLICK && (s == repeat_slider_minus || s == repeat_slider_plus)) // スライダー2の+/-が押されたら
{
if(s == repeat_slider_minus) // 「-」なら
{
repeat_slider_atai--; // 値を1減らす
}
if(s == repeat_slider_plus) // 「+」なら
{
repeat_slider_atai++; // 値を1増やす
}
if(repeat_slider_atai < 0)
{
repeat_slider_atai = 0;
} // 下限チェック
if(repeat_slider_atai > 50)
{
repeat_slider_atai = 50;
} // 上限チェック
int knobX = repeat_slider_x + (int)((double)repeat_slider_habasa * ((double)(repeat_slider_atai) / 50.0) + 0.5); // つまみX計算
ObjectSetInteger(0, repeat_slider_tumami, OBJPROP_XDISTANCE, knobX); // つまみ位置を反映
repeat_slider_kousin(); // 表示更新
repeat_slider_kotei_atai = repeat_slider_atai; // 確定値を更新
zenbu_keisan = true; // 全再計算を要求
if(doniti) // 土日対策がONなら
{
ChartSetSymbolPeriod(0, _Symbol, Period()); // チャート更新を促す
}
ui_state_save(); // 状態を保存する
ChartRedraw(); // 再描画
return; // ここで終了
}
if(id == CHARTEVENT_OBJECT_CLICK && (s == open_waku || s == open_moji)) // Openボタンが押されたら
{
open_switch = false; // OpenをOFFにする
ObjectSetInteger(0, open_waku, OBJPROP_BGCOLOR, clrWhite); // Open枠を白にする
open_igai_kesu(); // Open以外を消す
ui_state_save(); // 状態を保存する
return; // ここで終了
}
if(id == CHARTEVENT_OBJECT_CLICK && (s == alert_waku || s == alert_moji)) // Alertボタンが押されたら
{
alert_switch = !alert_switch; // AlertをON/OFF切替
ObjectSetString (0, alert_moji, OBJPROP_TEXT, alert_switch ? "Alert" : "Alert"); // 文字を更新
ObjectSetInteger(0, alert_waku, OBJPROP_BGCOLOR, alert_switch ? clrLime : clrWhite); // 背景色を更新
datetime Last_Close_Time = iTime(_Symbol, _Period, 1); // 直近確定足の時間を取得
if(Last_Close_Time > 0) // 取得できたら
{
last_alert_time = Last_Close_Time; // 上の既読を更新
}
if(doniti) // 土日対策がONなら
{
ChartSetSymbolPeriod(0, _Symbol, Period()); // チャート更新を促す
}
ui_state_save(); // 状態を保存する
ChartRedraw(); // 再描画
return; // ここで終了
}
if(id == CHARTEVENT_OBJECT_CLICK && (s == kakutei_waku || s == kakutei_moji)) // KAKUTEIボタンが押されたら
{
if(!kakutei_switch) // 現在OFFならONにする
{
kakutei_switch = true; // KAKUTEIをON
mikakutei_switch = false; // MI-KAKUTEIをOFF
ObjectSetInteger(0, kakutei_waku, OBJPROP_BGCOLOR, clrLime); // 背景色を更新
ObjectSetInteger(0, mikakutei_waku, OBJPROP_BGCOLOR, clrWhite); // 背景色を更新
zenbu_keisan = true; // 全再計算を要求
if(doniti) // 土日対策がONなら
{
ChartSetSymbolPeriod(0, _Symbol, Period()); // チャート更新を促す
}
ui_state_save(); // 状態を保存する
ChartRedraw(); // 再描画
}
return; // ここで終了
}
if(id == CHARTEVENT_OBJECT_CLICK && (s == mikakutei_waku || s == mikakutei_moji)) // MI-KAKUTEIボタンが押されたら
{
if(!mikakutei_switch) // 現在OFFならONにする
{
mikakutei_switch = true; // MI-KAKUTEIをON
kakutei_switch = false; // KAKUTEIをOFF
ObjectSetInteger(0, mikakutei_waku, OBJPROP_BGCOLOR, clrLime); // 背景色を更新
ObjectSetInteger(0, kakutei_waku, OBJPROP_BGCOLOR, clrWhite); // 背景色を更新
zenbu_keisan = true; // 全再計算を要求
if(doniti) // 土日対策がONなら
{
ChartSetSymbolPeriod(0, _Symbol, Period()); // チャート更新を促す
}
ui_state_save(); // 状態を保存する
ChartRedraw(); // 再描画
}
return; // ここで終了
}
if(id == CHARTEVENT_OBJECT_CLICK && (s == push_waku || s == push_moji)) // Pushボタンが押されたら
{
push_switch = !push_switch; // PushをON/OFF切替
ObjectSetString (0, push_moji, OBJPROP_TEXT, "Push"); // 表示文字
ObjectSetInteger(0, push_waku, OBJPROP_BGCOLOR, push_switch ? clrLime : clrWhite); // 背景色を更新
datetime Last_Close_Time = iTime(_Symbol, _Period, 1); // 直近確定足の時間を取得
if(Last_Close_Time > 0) // 取得できたら
{
last_alert_time = Last_Close_Time; // 上の既読を更新
}
if(doniti) // 土日対策がONなら
{
ChartSetSymbolPeriod(0, _Symbol, Period()); // チャート更新を促す
}
ui_state_save(); // 状態を保存する
ChartRedraw(); // 再描画
return; // ここで終了
}
// SMAボタンクリック処理
if(id == CHARTEVENT_OBJECT_CLICK && (s == sma_waku || s == sma_moji))
{
if(!sma_switch) // 現在OFFならONにする
{
sma_switch = true; // SMAをON
ema_switch = false; // EMAをOFF
// 色の更新
ObjectSetInteger(0, sma_waku, OBJPROP_BGCOLOR, clrLime); // 背景色を更新
ObjectSetInteger(0, ema_waku, OBJPROP_BGCOLOR, clrWhite); // 背景色を更新
ma_handle_kousin(sma_kikan); // ハンドル更新(SMAモードへ)
zenbu_keisan = true; // 再計算
if(doniti)
{
ChartSetSymbolPeriod(0, _Symbol, Period());
} // チャート更新を促す
ui_state_save(); // 状態を保存する
ChartRedraw(); // 再描画
}
return; // ここで終了
}
// EMAボタンクリック処理
if(id == CHARTEVENT_OBJECT_CLICK && (s == ema_waku || s == ema_moji))
{
if(!ema_switch) // 現在OFFならONにする
{
ema_switch = true; // EMAをON
sma_switch = false; // SMAをOFF
// 色の更新
ObjectSetInteger(0, ema_waku, OBJPROP_BGCOLOR, clrLime); // 背景色を更新
ObjectSetInteger(0, sma_waku, OBJPROP_BGCOLOR, clrWhite); // 背景色を更新
ma_handle_kousin(sma_kikan); // ハンドル更新(EMAモードへ)
zenbu_keisan = true; // 再計算
if(doniti)
{
ChartSetSymbolPeriod(0, _Symbol, Period());
} // チャート更新を促す
ui_state_save(); // 状態を保存する
ChartRedraw(); // 再描画
}
return; // ここで終了
}
// HIGEボタンクリック処理
if(id == CHARTEVENT_OBJECT_CLICK && (s == hige_waku || s == hige_moji))
{
if(!hige_switch) // 現在OFFならONにする
{
hige_switch = true; // HIGEをON
jittai_switch = false; // JITTAIをOFF
// 色の更新
ObjectSetInteger(0, hige_waku, OBJPROP_BGCOLOR, clrLime); // 背景色を更新
ObjectSetInteger(0, jittai_waku, OBJPROP_BGCOLOR, clrWhite); // 背景色を更新
zenbu_keisan = true; // 再計算
if(doniti)
{
ChartSetSymbolPeriod(0, _Symbol, Period());
} // チャート更新を促す
ui_state_save(); // 状態を保存する
ChartRedraw(); // 再描画
}
return; // ここで終了
}
// JITTAIボタンクリック処理
if(id == CHARTEVENT_OBJECT_CLICK && (s == jittai_waku || s == jittai_moji))
{
if(!jittai_switch) // 現在OFFならONにする
{
jittai_switch = true; // JITTAIをON
hige_switch = false; // HIGEをOFF
// 色の更新
ObjectSetInteger(0, jittai_waku, OBJPROP_BGCOLOR, clrLime); // 背景色を更新
ObjectSetInteger(0, hige_waku, OBJPROP_BGCOLOR, clrWhite); // 背景色を更新
zenbu_keisan = true; // 再計算
if(doniti)
{
ChartSetSymbolPeriod(0, _Symbol, Period());
} // チャート更新を促す
ui_state_save(); // 状態を保存する
ChartRedraw(); // 再描画
}
return; // ここで終了
}
}
//+------------------------------------------------------------------+
//| メイン処理 |
//+------------------------------------------------------------------+
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. サーバーとの同期が完了していない(読み込み中)なら計算しない
if(SeriesInfoInteger(_Symbol, _Period, SERIES_SYNCHRONIZED) == false)
{
return(0); // 次回やり直し
}
// 2. データがあまりにも少ない(ダウンロード直後の初期段階)なら計算しない
// ※W1などで5年分欲しい場合、ここが少ないと計算がおかしくなるため待機させる
if(rates_total < sma_kikan + 200) // 最低でもSMA期間+200本は溜まるまで待つ
{
return(0); // データ不足なので計算せず終了
}
if(handle_ma == INVALID_HANDLE) // ハンドルが無効なら
{
return(prev_calculated); // 何もしない
}
// MAの内部計算が追いつくまで描画しない(CopyBuffer失敗や途中状態を防止)
int bc = BarsCalculated(handle_ma);
if(bc < rates_total - 1) // 緩和:最新足の計算遅延を許容する
{
return(prev_calculated); // 待機
}
if(!ArrayGetAsSeries(time))
{
ArraySetAsSeries(time, true);
} // 最新=0にする
if(!ArrayGetAsSeries(open))
{
ArraySetAsSeries(open, true);
} // 最新=0にする
if(!ArrayGetAsSeries(high))
{
ArraySetAsSeries(high, true);
} // 最新=0にする
if(!ArrayGetAsSeries(low))
{
ArraySetAsSeries(low, true);
} // 最新=0にする
if(!ArrayGetAsSeries(close))
{
ArraySetAsSeries(close, true);
} // 最新=0にする
// この後の処理で「現在の足だけでなく1本前の足も参照する」ため、SMA期間ぶんに加えて安全に参照できる余白として2本分を足しています。
if(rates_total < sma_kikan + 2) // 参照に必要な本数が足りないなら
{
return(0); // 計算しない
}
int get_5bars_count = kako_5nen_check(time, rates_total); // 5年分のバー数を取得
int bars5_or_total = (get_5bars_count > 0 ? MathMin(get_5bars_count, rates_total) : rates_total); // 使うバー数を決める
int begin = MathMax(0, rates_total - bars5_or_total); // 描画開始位置
for(int p = 0; p < 2; ++p) // 2プロット分
{
PlotIndexSetInteger(p, PLOT_DRAW_BEGIN, begin); // 描画開始を設定
}
static int zenkai_taisyougai = -1; // 前回のbeginを保存
bool left_kakudai = (zenkai_taisyougai >= 0 && begin < zenkai_taisyougai); // 左に拡大したか
if(left_kakudai) // 左に拡大したなら
{
int s_from = rates_total - zenkai_taisyougai; // クリア開始
int s_to = rates_total - 1 - begin; // クリア終了
if(s_from <= s_to) // 範囲が正しいなら
{
for(int i = s_from; i <= s_to; ++i) // その範囲を回す
{
if(!(i == 0 && !kakutei_switch)) // 未確定表示を許す条件でなければ
{
sign_buffer[i] = EMPTY_VALUE; // サインバッファを空にする
}
}
}
zenbu_keisan = true; // 全再計算を要求
}
zenkai_taisyougai = begin; // 今回のbeginを保存
bool syokai_or_all_keisan = (prev_calculated == 0) || zenbu_keisan; // 初回または全再計算か
int inc = (prev_calculated == 0) ? 0 : MathMax(0, rates_total - prev_calculated); // 増分
int need = syokai_or_all_keisan ? bars5_or_total : MathMax(2, MathMin(bars5_or_total, inc + 2)); // 必要計算本数
int min_end = need - 2; // i+1参照があるので-2まで
if(min_end < 0) // 範囲が取れないなら
{
return(rates_total); // 終了
}
int min_end_or_zouka = (prev_calculated == 0) ? min_end : MathMax(0, rates_total - prev_calculated); // 増分の目安
int yobi = 1; // 予備
int main_loop_start = 0; // 開始位置(最新から)
int main_loop_end = syokai_or_all_keisan ? min_end : MathMin(main_loop_start + MathMax(1, min_end_or_zouka + yobi) - 1, min_end); // 終了位置
// -------------------------------------------------------
// ★ 追加修正:計算対象外(5年以上前)のゴミデータを確実に消去する
// -------------------------------------------------------
if(syokai_or_all_keisan) // 初回または全計算時
{
for(int i = main_loop_end + 1; i < rates_total; i++) // 計算範囲より古いデータ
{
sign_buffer[i] = EMPTY_VALUE; // 強制的に消す
}
}
if(main_loop_end < main_loop_start) // 範囲が逆なら
{
return(rates_total); // 終了
}
zenbu_keisan = false; // 全再計算フラグを戻す
int needSMACount = main_loop_end + sma_kikan + 10; // SMAコピー本数
if(needSMACount > rates_total)
{
needSMACount = rates_total;
} // 最大に合わせる
// ArraySetAsSeriesはOnInitで設定済みだが、念のため確認
ArraySetAsSeries(ma_buffer, true);
int copied = CopyBuffer(handle_ma, 0, 0, needSMACount, ma_buffer); // SMA値をコピー(★これで描画もされる)
if(!heikin_keisan_zumi) // 平均をまだ計算していなければ
{
int n = MathMin(heikin_honsuu, rates_total - 1); // 使う本数を決める
double s = 0.0; // 合計用
for(int k = 1; k <= n; ++k) // k=1から(未確定0を避ける)
{
s += (high[k] - low[k]); // 足の長さを足す
}
heikin_nagasa = (n > 0) ? (s / n) : 0.0; // 平均を計算
heikin_keisan_zumi = true; // 計算済みにするフラグ
}
// 参照する範囲(0~main_loop_end)までコピーできていないなら、誤判定防止のため次回に回す
if(copied <= main_loop_end)
{
return(0); // 計算しない
}
if(copied <= 0) // 失敗なら
{
// 【修正後】「計算できてない」と伝えて、次回やり直させる
return(0); // 計算しない
}
for(int i = main_loop_start; i <= main_loop_end; i++) // 対象範囲を計算
{
// ★ここから判定ロジック(SMAタッチ)
double ima_no_sma = ma_buffer[i]; // 現在のSMA値
// 高値と安値の間にSMAがあれば「タッチ」したとみなす
bool touch_sita = (high[i] >= ima_no_sma && low[i] <= ima_no_sma);
// 追加:JITTAIがONの時は実体で判定する
if(jittai_switch) // 実体判定がONなら
{
double jittai_takane = MathMax(open[i], close[i]); // 実体の高値
double jittai_yasune = MathMin(open[i], close[i]); // 実体の安値
touch_sita = (jittai_takane >= ima_no_sma && jittai_yasune <= ima_no_sma); // 実体が触れているか
}
// KAKUTEIスイッチがONの場合、未確定足(0本目)ではサインを出さない
if(kakutei_switch && i == 0)
{
touch_sita = false; // 無効にする
}
sign_buffer[i] = EMPTY_VALUE; // 初期化
if(touch_sita) // タッチしたら
{
sign_buffer[i] = low[i] - (heikin_nagasa * haba); // 安値の下に矢印を表示
}
}
if(repeat_slider_kotei_atai > 0) // リピート防止があるなら
{
int cool_time = repeat_slider_kotei_atai; // クールタイム
int nokori = 0; // 残り時間
for(int i = MathMin(rates_total - 1, main_loop_end + cool_time); i >= main_loop_start; i--) // cool_time分だけ過去も含めてチェック
{
bool sign_aru = (sign_buffer[i] != EMPTY_VALUE); // サインがあるか
if(nokori > 0) // 待ち時間中なら
{
if(sign_aru) // サインがあれば
{
if(i <= main_loop_end)
{
sign_buffer[i] = EMPTY_VALUE;
} // 今回計算範囲だけ消す
}
nokori--; // 時間を減らす
}
else // 待ち時間でなければ
{
if(sign_aru) // サインがあれば
{
nokori = cool_time; // 待ち時間を設定
}
}
}
}
// KAKUTEIモードなら1本前(確定足)、通常なら0本目(未確定足)を見る
int alert_taisyou_bar = 0; // 通知対象バー
if(kakutei_switch)
{
alert_taisyou_bar = 1; // 確定なら1本前
}
if(alert_switch) // アラートがONなら
{
if(sign_buffer[alert_taisyou_bar] != EMPTY_VALUE && (last_alert_time != time[alert_taisyou_bar]))
{
Alert(StringFormat("[%s] MA Touch Sign %s (Period:%d)", _Symbol, TimeToString(time[alert_taisyou_bar], TIME_DATE | TIME_MINUTES), sma_kikan));
last_alert_time = time[alert_taisyou_bar]; // 既読時刻更新
if(push_switch) // PushがONなら
{
SendNotification(StringFormat("[%s] MA Touch Sign %s (Period:%d)", _Symbol, TimeToString(time[alert_taisyou_bar], TIME_DATE | TIME_MINUTES), sma_kikan));
}
}
}
return(rates_total); // 計算完了
}
//+------------------------------------------------------------------+
//| 削除処理 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) // 終了処理
{
ui_state_save(); // 状態を保存する
if(handle_ma != INVALID_HANDLE) // ハンドルが有効なら
{
IndicatorRelease(handle_ma); // ハンドル解放
}
handle_ma = INVALID_HANDLE; // 無効にする
ObjectDelete(0, alert_waku); // Alert枠を削除
ObjectDelete(0, alert_moji); // Alert文字を削除
ObjectDelete(0, push_waku); // Push枠を削除
ObjectDelete(0, push_moji); // Push文字を削除
ObjectDelete(0, open_waku); // Open枠を削除
ObjectDelete(0, open_moji); // Open文字を削除
ObjectDelete(0, kakutei_waku); // KAKUTEI枠を削除
ObjectDelete(0, kakutei_moji); // KAKUTEI文字を削除
ObjectDelete(0, mikakutei_waku); // MI-KAKUTEI枠を削除
ObjectDelete(0, mikakutei_moji); // MI-KAKUTEI文字を削除
// 追加:SMA/EMAボタン削除
ObjectDelete(0, sma_waku); // SMA枠を削除
ObjectDelete(0, sma_moji); // SMA文字を削除
ObjectDelete(0, ema_waku); // EMA枠を削除
ObjectDelete(0, ema_moji); // EMA文字を削除
// 追加:HIGE/JITTAIボタン削除
ObjectDelete(0, hige_waku); // HIGE枠を削除
ObjectDelete(0, hige_moji); // HIGE文字を削除
ObjectDelete(0, jittai_waku); // JITTAI枠を削除
ObjectDelete(0, jittai_moji); // JITTAI文字を削除
ObjectDelete(0, kikan_slider_haikei); // スライダー1背景を削除
ObjectDelete(0, kikan_slider_tumami); // スライダー1つまみを削除
ObjectDelete(0, kikan_slider_atai_label); // スライダー1値表示を削除
ObjectDelete(0, kikan_slider_minus); // スライダー1「-」を削除
ObjectDelete(0, kikan_slider_plus); // スライダー1「+」を削除
ObjectDelete(0, repeat_slider_haikei); // スライダー2背景を削除
ObjectDelete(0, repeat_slider_tumami); // スライダー2つまみを削除
ObjectDelete(0, repeat_slider_atai_label); // スライダー2値表示を削除
ObjectDelete(0, repeat_slider_minus); // スライダー2「-」を削除
ObjectDelete(0, repeat_slider_plus); // スライダー2「+」を削除
ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false); // マウス移動イベントを無効化
ChartSetInteger(0, CHART_MOUSE_SCROLL, true); // スクロールを有効化
ArrayInitialize(sign_buffer, EMPTY_VALUE); // サインバッファをクリア
ChartRedraw(); // 再描画
}
//+------------------------------------------------------------------+ // ファイル終わり
simple-ma-touch-alertのコア部分
を紹介
simple-ma-touch-alertの特に重要な部分を詳しく解説します。
過去5年の計算について
MT4・MT5は時間足が短くなるほど、足の本数が増え処理が膨大になります。全期間でサインを出すこともできますが、1分足などは重たくなるので、ある程度年数を絞る必要があります。
そのため、simple-ma-touch-alertでは過去5年までサインが出るようにしています。
※閏年は含まれないので約5年となります。
int kako_5nen_check(const datetime &time[], const int rates_total) // 5年分のバー数を返す関数
{
datetime latest = time[0]; // 最新バーの時間
datetime kijyun_jikan = latest - (datetime)(86400 * 365 * 5); // 5年前の基準時刻
int idx = iBarShift(_Symbol, _Period, kijyun_jikan, false); // 5年前に近いバー位置を探す
if(idx < 0) // 見つからない場合
{
//idx = rates_total - 1; // いちばん古いバーにする
idx = 200; //初動対策
}
if(idx > rates_total - 1) // 範囲外なら
{
idx = rates_total - 1; // 最大に丸める
}
return(idx + 1); // 0始まりなので+1して本数にする
}
この関数でやっていることは、「過去5年ぶんだけ計算したいので、どこまでが“過去5年”かを決める」ことです。
まず、最新の足の時間から「5年前の日時」を作ります。そして iBarShift() を使って、「その日時に一番近いローソク足は何本目か」を探します。
これが見つかれば、その番号(idx)までを計算対象にすれば、だいたい過去5年ぶんだけ計算すれば済む、という考え方です。
問題は、初めて開く銘柄や、ヒストリデータがまだダウンロード途中のときです。
このときは、端末の中にそもそも5年分のローソク足が入っていないので、「5年前に当たる足」が見つかりません。
すると iBarShift() は -1 を返します。つまり「5年前がどこか分からない」状態です。
そこで idx = 200; があります。
これは「5年前が分からないなら、いったん“200本ぶん”だけを計算対象にしておこう」という“仮の上限”です。
200という数字自体に「5年」の意味があるわけではなく、重くならない範囲で、とりあえず計算量を抑えるための目安です。
週足(W1)なら200本は数年分に相当するので、初回の仮の範囲として使いやすい、というだけです。
ただし、W1は開いた瞬間に200本あるとは限りません。だから次の行で「もし200本も無いなら、今ある分だけに縮める」という調整をしています。
これで、データが少ないときでも無理に200本目を見に行ってエラーになるのを防げます。
そしてもう一つ大事なのは、「H1などでも常に200本しか見ない」という意味ではないことです。
idx=200 が使われるのは、iBarShift() が失敗して -1 になったときだけ、つまり5年分の位置が見つからない“例外のときだけ”です。
ふつうにヒストリが揃っていればH1でもD1でも iBarShift() は成功するので、その場合は200は登場せず、きちんと「過去5年ぶん」の位置が使われます。
逆に、初めて開いた直後などでH1でも履歴がまだ薄い場合は一時的に200本上限になることがありますが、データが揃って iBarShift() が5年前を見つけられるようになった瞬間から、自動的に「過去5年」基準に切り替わります。
まとめると、この関数は「本来は5年前の足を探して、そこまでを計算する」。
それができない初回などだけは「とりあえず200本までにして重さを抑える」。そして200本すら無ければ「ある分だけ使う」。
さらに、データが揃えば自動で本来の“過去5年”に戻る、という安全な動きになるように作られています。
SMAハンドルを更新する関数
void ma_handle_kousin(int kikan) // ハンドル更新関数(★関数名変更)
{
if(handle_ma != INVALID_HANDLE) // 既にハンドルがあるなら
{
IndicatorRelease(handle_ma); // 解放する
}
//スイッチの状態を見てSMAかEMAかを決定する
ENUM_MA_METHOD method = MODE_SMA; // デフォルトはSMA
if(ema_switch)
{
method = MODE_EMA;
} // EMAスイッチがONならEMA
handle_ma = iMA(_Symbol, _Period, kikan, 0, method, PRICE_CLOSE); // 新しい期間とメソッドで作成
}
この関数は、移動平均線の「計算の元になる部品」を作り直すための処理です。
MQL5では、iMA() のようなインジケータ計算は、毎回その場で値を直接出すのではなく、まず「ハンドル」という番号札(参照番号)を作り、あとから CopyBuffer() で値を取り出す形になります。
期間や種類(SMA/EMA)を変更したのに、古いハンドルのまま使い続けると、表示や計算が古い設定のままになってしまいます。
最初にやっているのは、古いハンドルが残っている場合の後片付けです。
handle_ma が有効なら IndicatorRelease() で解放して、古い計算部品を捨てます。これをしないと、設定を変えるたびに古い部品が残り続けて、無駄なメモリ使用や動作の不安定につながります。
次に、どの種類の移動平均を作るかを決めます。
基本は MODE_SMA(単純移動平均)ですが、ema_switch がONなら MODE_EMA(指数移動平均)に切り替えます。ここで「いま選ばれている計算方法」が確定します。
最後に iMA() を呼んで、新しい期間 kikan と、今決めた method でハンドルを作成し直します。こうしておくと、スライダーで期間を変えたり、SMA/EMAボタンを押した瞬間に、以降の CopyBuffer() が新しい設定の移動平均を正しく参照できるようになります。
スライダーのドラッグ処理
// ---- スライダー(ドラッグ) ----
if(id == CHARTEVENT_MOUSE_MOVE) // マウス移動イベントなら
{
int x = (int)l; // マウスX座標
int y = (int)d; // マウスY座標
int mouseState = (int)StringToInteger(s); // マウス状態(押下など)
int margin = 10; // 判定の余白
bool hover3 = (x >= kikan_slider_x - margin && x <= kikan_slider_x + kikan_slider_habasa + kikan_slider_tumami_size + margin && y >= kikan_slider_y - margin && y <= kikan_slider_y + kikan_slider_takasa + margin); // スライダー1付近か
bool hover4 = (x >= repeat_slider_x - margin && x <= repeat_slider_x + repeat_slider_habasa + repeat_slider_tumami_size + margin && y >= repeat_slider_y - margin && y <= repeat_slider_y + repeat_slider_takasa + margin); // スライダー2付近か
bool isAnyHover = (hover3 || hover4); // どちらかに乗っているか
bool isAnyDragging = (kikan_slider_drug_tyu || repeat_slider_drug_tyu); // どちらかをドラッグ中か
if(isAnyHover || isAnyDragging) // 近い or ドラッグ中なら
{
ChartSetInteger(0, CHART_MOUSE_SCROLL, false); // チャートスクロールを無効化
}
else // それ以外なら
{
ChartSetInteger(0, CHART_MOUSE_SCROLL, true); // チャートスクロールを有効化
}
if((mouseState & 1) == 1) // 左クリック中なら
{
if(!kikan_slider_drug_tyu && !repeat_slider_drug_tyu) // まだドラッグ開始していないなら
{
long knobX2 = ObjectGetInteger(0, kikan_slider_tumami, OBJPROP_XDISTANCE); // スライダー1つまみX取得
long knobY2 = ObjectGetInteger(0, kikan_slider_tumami, OBJPROP_YDISTANCE); // スライダー1つまみY取得
long knobX3 = ObjectGetInteger(0, repeat_slider_tumami, OBJPROP_XDISTANCE); // スライダー2つまみX取得
long knobY3 = ObjectGetInteger(0, repeat_slider_tumami, OBJPROP_YDISTANCE); // スライダー2つまみY取得
if(x >= knobX2 && x <= knobX2 + kikan_slider_tumami_size && // つまみ範囲内か(X)
y >= knobY2 && y <= knobY2 + kikan_slider_tumami_size) // つまみ範囲内か(Y)
{
kikan_slider_drug_tyu = true; // スライダー1をドラッグ中にする
mouse_scroll_mae = (bool)ChartGetInteger(0, CHART_MOUSE_SCROLL); // 変更前を保存
ChartSetInteger(0, CHART_MOUSE_SCROLL, false); // スクロールを止める
}
else if(x >= knobX3 && x <= knobX3 + repeat_slider_tumami_size && // つまみ範囲内か(X)
y >= knobY3 && y <= knobY3 + repeat_slider_tumami_size) // つまみ範囲内か(Y)
{
repeat_slider_drug_tyu = true; // スライダー2をドラッグ中にする
mouse_scroll_mae = (bool)ChartGetInteger(0, CHART_MOUSE_SCROLL); // 変更前を保存
ChartSetInteger(0, CHART_MOUSE_SCROLL, false); // スクロールを止める
}
}
if(kikan_slider_drug_tyu) // スライダー1をドラッグ中なら
{
int newX = x - (kikan_slider_tumami_size / 2); // つまみ中心に合わせる
if(newX < kikan_slider_x) // 左端より左なら
{
newX = kikan_slider_x; // 左端に固定
}
if(newX > kikan_slider_x + kikan_slider_habasa) // 右端より右なら
{
newX = kikan_slider_x + kikan_slider_habasa; // 右端に固定
}
ObjectSetInteger(0, kikan_slider_tumami, OBJPROP_XDISTANCE, newX); // つまみ位置を移動
double percent = (double)(newX - kikan_slider_x) / (double)kikan_slider_habasa; // 0~1の割合
// 1~200に変換
int newVal = 1 + (int)(percent * 199.0 + 0.5);
if(newVal < 1)
{
newVal = 1;
} // 下限チェック
if(newVal > 200)
{
newVal = 200;
} // 上限チェック
if(kikan_slider_atai != newVal) // 値が変わったら
{
kikan_slider_atai = newVal; // 値を更新
kikan_slider_kousin(); // 表示更新
ChartRedraw(); // 再描画
}
}
else if(repeat_slider_drug_tyu) // スライダー2をドラッグ中なら
{
int newX = x - (repeat_slider_tumami_size / 2); // つまみ中心に合わせる
if(newX < repeat_slider_x) // 左端より左なら
{
newX = repeat_slider_x; // 左端に固定
}
if(newX > repeat_slider_x + repeat_slider_habasa) // 右端より右なら
{
newX = repeat_slider_x + repeat_slider_habasa; // 右端に固定
}
ObjectSetInteger(0, repeat_slider_tumami, OBJPROP_XDISTANCE, newX); // つまみ位置を移動
double percent = (double)(newX - repeat_slider_x) / (double)repeat_slider_habasa; // 0~1の割合
int newVal = (int)(percent * 50.0 + 0.5); // 0~50に変換
if(newVal < 0)
{
newVal = 0;
} // 下限チェック
if(newVal > 50)
{
newVal = 50;
} // 上限チェック
if(repeat_slider_atai != newVal) // 値が変わったら
{
repeat_slider_atai = newVal; // 値を更新
repeat_slider_kousin(); // 表示更新
ChartRedraw(); // 再描画
}
}
}
else // 左クリックを離したら
{
if(kikan_slider_drug_tyu) // スライダー1をドラッグしていたなら
{
kikan_slider_drug_tyu = false; // ドラッグ終了
ChartSetInteger(0, CHART_MOUSE_SCROLL, mouse_scroll_mae); // スクロール状態を戻す
if(kikan_slider_kotei_atai != kikan_slider_atai) // 確定値が変わったなら
{
kikan_slider_kotei_atai = kikan_slider_atai; // 確定値を更新
sma_kikan = kikan_slider_kotei_atai; // SMA期間を更新
ma_handle_kousin(sma_kikan); // ハンドル再作成
zenbu_keisan = true; // 全再計算を要求
if(doniti) // 土日対策がONなら
{
ChartSetSymbolPeriod(0, _Symbol, Period()); // チャート更新を促す
}
}
ui_state_save(); // 状態を保存する
ChartRedraw(); // 再描画
}
else if(repeat_slider_drug_tyu) // スライダー2をドラッグしていたなら
{
repeat_slider_drug_tyu = false; // ドラッグ終了
ChartSetInteger(0, CHART_MOUSE_SCROLL, mouse_scroll_mae); // スクロール状態を戻す
if(repeat_slider_kotei_atai != repeat_slider_atai) // 確定値が変わったなら
{
repeat_slider_kotei_atai = repeat_slider_atai; // 確定値を更新
zenbu_keisan = true; // 全再計算を要求
if(doniti) // 土日対策がONなら
{
ChartSetSymbolPeriod(0, _Symbol, Period()); // チャート更新を促す
}
}
ui_state_save(); // 状態を保存する
ChartRedraw(); // 再描画
}
}
return; // ここで終了
}
この処理は、チャート上に表示したスライダーをマウスでつかんで動かし、その位置に応じて数値を変えるための仕組みです。
MQL5では「マウスが動いた」「ボタンが押された」「離された」といった操作がイベントとして通知されるため、それを利用して人間の操作をプログラムに伝えています。
まず、このコードは「マウスが動いたとき」にだけ反応するようになっています。マウスが少しでも動くたびに、現在のマウスのX座標とY座標、そして左クリックが押されているかどうかを取得します。
これによって「今どこにマウスがあるのか」「押している最中なのか」を常に把握しています。
次に行っているのが、「スライダーの近くにマウスがあるかどうか」の判定です。
スライダーそのものは細いので、少し余白を持たせて「この辺にいたらスライダー操作中とみなす」という範囲を作っています。
どちらかのスライダーの近くにマウスがある、もしくはすでにドラッグ中であれば、チャート本来のマウススクロール機能を一時的に止めます。
これは、スライダーを動かしている最中にチャートが左右にずれてしまうのを防ぐためです。
左クリックが押されている場合は、さらに処理が進みます。
まだドラッグを開始していない状態で、マウスが「つまみ」の上に乗っていれば、その瞬間に「このスライダーをドラッグ中」というフラグを立てます。
同時に、もともとのチャートスクロール設定を覚えておき、強制的にスクロールを止めます。ここで初めて「つまみをつかんだ」状態になります。
ドラッグ中は、マウスのX座標に合わせてつまみの位置を更新します。
ただし、スライダーの左端より左へ行かないように、右端より右へ行かないように制限をかけています。これによって、つまみがレールから飛び出すことはありません。
つまみの位置が決まったら、その位置を「割合」に変換します。左端なら0、右端なら1になるように計算し、その割合を使って「1~200」や「0~50」といった実際の設定値に変換します。
値が前回と違っていれば、内部の数値を更新し、表示を更新して再描画します。これにより、ドラッグしている最中もリアルタイムで数値が変わって見えるようになります。
左クリックを離した瞬間には、「ドラッグ終了」の処理に入ります。
ドラッグ中フラグを下ろし、止めていたチャートスクロール設定を元に戻します。そして、ドラッグ前に確定していた値と比べて変更があった場合は、その値を正式な設定として確定させます。
MA期間のスライダーであれば、ここで移動平均線の期間を更新し、ハンドルを作り直して全再計算を要求します。
この一連の流れによって、スライダーをつかんで動かすと、その場では滑らかに動き、指を離した瞬間に設定が確定し、インジケータが正しく更新されるようになっています。
同期の完了待ち
if(SeriesInfoInteger(_Symbol, _Period, SERIES_SYNCHRONIZED) == false)
{
return(0); // 次回やり直し
}
これは、ローソク足データがまだ読み込み途中のときに計算してしまうと、必要な本数が足りなかったり、配列の中身が途中の状態だったりして、矢印や判定が一瞬おかしくなるのを防ぐための安全装置です。
SERIES_SYNCHRONIZED が false の間は「まだデータがそろっていない」と判断して、その回の OnCalculate() を中断します。
データがそろった次回以降に計算させることで、起動直後や銘柄切り替え直後でも安定して動くようになります。
最低でもSMA期間+200本は溜まるまで待つ
if(rates_total < sma_kikan + 200) // 最低でもSMA期間+200本は溜まるまで待つ
{
return(0); // データ不足なので計算せず終了
}
これは「ローソク足の本数がまだ少ない段階で計算を始めて、表示や判定が不安定になる」のを防ぐための待機条件です。
まず rates_total は、いま端末に入っているローソク足の本数です。
チャートを開いた直後や週足(W1)などでは、ヒストリデータがまだ全部ダウンロードされておらず、この本数が少ないことがあります。
そんな状態でSMAやサイン判定を動かすと、必要な本数が足りなかったり、途中で本数が増えて計算範囲が揺れたりして、矢印が出たり消えたり、判定がズレたり、CopyBuffer() が失敗しやすくなります。
だから「本数が十分になるまでは計算しない」として一度抜けています。
次に「なぜ sma_kikan + 200 なのか」ですが、SMAは期間が sma_kikan なら最低でもその本数が必要です。
ただ、最低ラインぴったりだと“ギリギリで不安定”になりやすいので、追加で余裕を持たせています。
その余裕が +200 で、SMA計算に必要な本数に加えて、さらに200本ぶんデータが貯まるまで待つ、という意味です。
200は唯一の正解ではなく、「短期足ではすぐ満たせる」「長期足ではデータ不足のまま無理に動かさない」というバランスを取るための実務的な目安です。
そして、よく混同される idx = 200 の方は「5年前の位置が見つからないときに、とりあえず計算範囲の目安を200本にする」という代替策で、データ不足を解決する“待機”ではありません。
データが少ないときに本当に待つ役割は、この rates_total < sma_kikan + 200 のような return(0) 条件が担っています。
拡大処理
bool left_kakudai = (zenkai_taisyougai >= 0 && begin < zenkai_taisyougai); // 左に拡大したか
if(left_kakudai) // 左に拡大したなら
{
int s_from = rates_total - zenkai_taisyougai; // クリア開始
int s_to = rates_total - 1 - begin; // クリア終了
if(s_from <= s_to) // 範囲が正しいなら
{
for(int i = s_from; i <= s_to; ++i) // その範囲を回す
{
if(!(i == 0 && !kakutei_switch)) // 未確定表示を許す条件でなければ
{
sign_buffer[i] = EMPTY_VALUE; // サインバッファを空にする
}
}
}
zenbu_keisan = true; // 全再計算を要求
}
この処理は「今回から新しく計算対象に入った古い足のぶんだけ、いったん矢印を消して、次に正しく作り直す」ためのものです。
ポイントは、ここで扱っている番号に 2種類あることです。
begin は 左端(いちばん古い足)を0として数える番号で、「何本目から描画(計算)を始めるか」という境界です。
※int begin = MathMax(0, rates_total - bars5_or_total); // 描画開始位置になるので基本的には5年分
int s_to = rates_total - 1 - begin;はローソク足のトータル-5年分を引いた数(5年前の位置)がs_toになります。
一方 sign_buffer[i] は ArraySetAsSeries(true) の配列なので、最新の足が0、1本前が1…という番号です。
つまり、begin と sign_buffer は 数え方が逆です。
そこでまず left_kakudai は、「前回の begin より今回の begin のほうが小さいか」を見ています。
begin が小さくなるのは、描画開始がさらに左(より古い足)へ広がったという意味です。
ここで初めて「前回まで触っていなかった古い足が、今回から範囲に入った」と分かります。
次に s_from と s_to は、その“増えた分の区間”を sign_buffer の番号(最新=0の番号)に変換しています。
たとえば rates_total=1000、前回 begin=740、今回 begin=700 だったとします。すると「新しく増えた古い区間」は左端の数え方では 700~739 です。
でも sign_buffer は最新=0なので、その区間を変換して 260~299 のような番号に直し、そこを EMPTY_VALUE で一回全部消します。
これをやる理由は、その区間の sign_buffer に古い値が残っていると、今回まだ正しく計算していないのに矢印だけ出ることがあるからです。
最後に zenbu_keisan = true を立てて、「消した区間も含めて、次の計算で全体をきちんと計算し直して、正しい矢印を入れ直す」という合図にしています。
つまりこの処理は、左へ範囲が広がった瞬間に起きやすい “残り矢印”の混入を防ぐための掃除+作り直し指示です。
メイン処理を詳しく解説
for(int i = main_loop_start; i <= main_loop_end; i++)
ここで重要なのは「start と end がどう決まるか」です。
この範囲が分からないと、何を計算しているかが追えなくなります。
前提:配列は series(最新が 0)なので、i = 0 が最新(いま動いている足)で、i が大きいほど過去になります。つまり 0 → 1 → 2 → … と進むほど時間は古くなります。
main_loop_start は固定(最新から始める)
main_loop_start = 0;
毎回「最新側」から計算を始める設計です。ここでのポイントは、最新足付近は毎ティック状況が変わり得るため、最新側から作り直すことでサインの残りや取りこぼしを避けやすくしている点です。
main_loop_end は「今回どれだけ計算するか」で変わります。
bars5_or_total によって「今回使う最大本数」が決まり、これより古い領域は計算対象外になります。
これは無限に遡って重くなるのを防ぐためで、同時に「表示対象外の古い領域のサインを維持しない」方針にもつながります。
初回 / 全再計算では 5年分をまとめて計算し、通常(2回目以降)では増えた分(inc)に安全分を足した範囲だけを計算し直します。
これにより、毎回全バーを計算するのではなく、直近で変化し得るところだけを軽く回す設計になります。
特に「増えた分だけ」だと境界付近で判定がずれることがあるため、安全分を足して余裕を持たせているのが意図です。
min_end = need - 2;
これは「この後のどこかで i+1 など隣の足を参照しても範囲外にならないように、余白を残す」ためです。
つまり need 本ぶん計算したい気持ちはあっても、配列参照の安全性を優先して、for の終点は min_end までに抑えています。
この考え方があるため、ループは「必要十分」より少し手前で止まる形になりますが、逆に言うと参照エラーや不定動作を避けるための保険になっています。
この for の仕事を一言で言うと、「その足がMAに触れたかを判定し、触れた足にだけ矢印用の値を入れる」です。
処理の流れは次の通りです。
double ima_no_sma = ma_buffer[i];
この ma_buffer は、直前の CopyBuffer(handle_ma, ...) で取得したMAの値です。
スイッチで SMA/EMA を切り替えてもハンドル側が切り替わるので、ここに入っているのは常に「今のモードのMA」です。
したがって、この行は「MAの種類が何であれ、ここから先は同じ手続きで判定する」ための入口になります。
touch_sita = (high[i] >= ima_no_sma && low[i] <= ima_no_sma);
これは高値がMA以上まで届いていて、かつ安値がMA以下まで下がっているかを同時に見ています。
成立するなら、その足の値幅の中にMAが含まれているので、ローソク足がMAをまたいだ、すなわちMAに触れたと判断できます。
直感的には、MAを水平線と見たときに、その足の縦の範囲(高値~安値)がその線を跨いでいれば「触れた」と言える、という意味になります。
if(jittai_switch) { ... touch_sita = (jittai_takane >= ima_no_sma && jittai_yasune <= ima_no_sma); }
ここが重要で、JITTAI が ON のときはヒゲ判定を“追加で見る”のではなく、判定基準そのものを実体に差し替えています。
実体の高値は open/close の大きい方、実体の安値は open/close の小さい方なので、結局「実体の範囲がMAをまたいだか」を見ています。
したがってモード差は、HIGE はローソク足全体(ヒゲ含む)がMAに触れたかを見て、JITTAI は実体だけがMAに触れたかを見る、という違いになります。
if(kakutei_switch && i == 0) touch_sita = false;
series配列の i=0 は「今まさに動いている足」です。
未確定足は高値・安値・終値が動くため、触れた/触れていないが途中で変わります。KAKUTEI モードは「確定した足だけを対象にしたい」ので、i=0 が触れていてもサインを出さないように、判定を強制的に無効化しています。
sign_buffer[i] = EMPTY_VALUE;
この1行があることで、再計算した範囲については必ず「いったん消してから作り直す」ことになります。
前回はサインがあったが今回は条件を満たさない場合でも、古い矢印が残りません。
つまり sign_buffer は「最新側の一定範囲は常に再構築される」状態になり、表示の整合性が保たれます。
そしてタッチした場合だけ矢印を出します。
sign_buffer[i] = low[i] - (heikin_nagasa * haba);
ここは矢印をどこに描くかの計算です。low[i](その足の安値)を基準にして、平均足長 heikin_nagasa の haba 倍だけ下にずらしています。
目的は、ローソク足の直下に一定の見やすい距離を確保して矢印を置くことです。
ATRではなく平均足長を使っているため、相場の値幅が大きい局面では矢印の距離も自然に広がり、チャート上で矢印が詰まって見づらくなるのを避けやすくなります。
スライダーの数値やボタンの配置を他の時間足で反映
スライダーの数値やボタンの配置を他の時間足で反映させる方法を紹介。
UIの状態を「記憶(保存)」している場所:ui_state_save()
void ui_state_save() // UI状態を保存する関数
void ui_state_save() // UI状態を保存する関数
{
GlobalVariableSet(gv_key("alert_switch"), alert_switch ? 1.0 : 0.0); // 保存
GlobalVariableSet(gv_key("push_switch"), push_switch ? 1.0 : 0.0); // 保存
GlobalVariableSet(gv_key("open_switch"), open_switch ? 1.0 : 0.0); // 保存
GlobalVariableSet(gv_key("kakutei_switch"), kakutei_switch ? 1.0 : 0.0); // 保存
GlobalVariableSet(gv_key("mikakutei_switch"), mikakutei_switch ? 1.0 : 0.0); // 保存
GlobalVariableSet(gv_key("sma_switch"), sma_switch ? 1.0 : 0.0); // 保存
GlobalVariableSet(gv_key("ema_switch"), ema_switch ? 1.0 : 0.0); // 保存
GlobalVariableSet(gv_key("hige_switch"), hige_switch ? 1.0 : 0.0); // 保存
GlobalVariableSet(gv_key("jittai_switch"), jittai_switch ? 1.0 : 0.0); // 保存
GlobalVariableSet(gv_key("kikan_slider_atai"), (double)kikan_slider_atai); // 保存
GlobalVariableSet(gv_key("kikan_slider_kotei_atai"), (double)kikan_slider_kotei_atai); // 保存
GlobalVariableSet(gv_key("sma_kikan"), (double)sma_kikan); // 保存
GlobalVariableSet(gv_key("repeat_slider_atai"), (double)repeat_slider_atai); // 保存
GlobalVariableSet(gv_key("repeat_slider_kotei_atai"), (double)repeat_slider_kotei_atai); // 保存
}
これは、ボタンのON/OFFやスライダーの数値を「グローバル変数(GlobalVariable)」へ保存して、次回起動時や再読み込み時に同じ状態へ戻せるようにする処理です。
保存対象は スイッチ類(alert/push/open/確定/未確定/SMA-EMA/HIGE-JITTAI) と、スライダー値(期間・リピート) です。
ポイントは、保存キーが固定文字列ではなく、後述のgv_key() で作られていることです。これにより、チャートIDや通貨ペアが違っても別々に状態が保存されるようになっています。
保存先キーを作っている場所:gv_key()
string gv_key(string name) // グローバル変数キーを作る関数
{
return("btn+sma_touch:" + (string)ChartID() + ":" + _Symbol + ":" + name); // キーを返す
}
これは、GlobalVariable の「キー名(名前)」を作る関数です。
キーの中にChartID()と_Symbolが入っているので、
同じインジでも チャートが違えば別キー
同じチャートでも 銘柄が違えば別キーになります。
そのため「他のチャートの設定が混ざる」事故を避けられます。
UIの状態を「復元(読み込み)」している場所:ui_state_load()
bool ui_state_load() // UI状態を読み込む関数
{
bool loaded = false; // 読み込み済みフラグ
string k; // キー
k = gv_key("alert_switch");
if(GlobalVariableCheck(k))
{
alert_switch = (GlobalVariableGet(k) > 0.5); loaded = true;
} // 読込
k = gv_key("push_switch");
if(GlobalVariableCheck(k))
{
push_switch = (GlobalVariableGet(k) > 0.5); loaded = true;
} // 読込
k = gv_key("open_switch");
if(GlobalVariableCheck(k))
{
open_switch = (GlobalVariableGet(k) > 0.5); loaded = true;
} // 読込
k = gv_key("kakutei_switch");
if(GlobalVariableCheck(k))
{
kakutei_switch = (GlobalVariableGet(k) > 0.5); loaded = true;
} // 読込
k = gv_key("mikakutei_switch");
if(GlobalVariableCheck(k))
{
mikakutei_switch = (GlobalVariableGet(k) > 0.5); loaded = true;
} // 読込
k = gv_key("sma_switch");
if(GlobalVariableCheck(k))
{
sma_switch = (GlobalVariableGet(k) > 0.5); loaded = true;
} // 読込
k = gv_key("ema_switch");
if(GlobalVariableCheck(k))
{
ema_switch = (GlobalVariableGet(k) > 0.5); loaded = true;
} // 読込
k = gv_key("hige_switch");
if(GlobalVariableCheck(k))
{
hige_switch = (GlobalVariableGet(k) > 0.5); loaded = true;
} // 読込
k = gv_key("jittai_switch");
if(GlobalVariableCheck(k))
{
jittai_switch = (GlobalVariableGet(k) > 0.5); loaded = true;
} // 読込
k = gv_key("kikan_slider_atai");
if(GlobalVariableCheck(k))
{
kikan_slider_atai = (int)MathRound(GlobalVariableGet(k)); loaded = true;
} // 読込
k = gv_key("kikan_slider_kotei_atai");
if(GlobalVariableCheck(k))
{
kikan_slider_kotei_atai = (int)MathRound(GlobalVariableGet(k)); loaded = true;
} // 読込
k = gv_key("sma_kikan");
if(GlobalVariableCheck(k))
{
sma_kikan = (int)MathRound(GlobalVariableGet(k)); loaded = true;
} // 読込
k = gv_key("repeat_slider_atai");
if(GlobalVariableCheck(k))
{
repeat_slider_atai = (int)MathRound(GlobalVariableGet(k)); loaded = true;
} // 読込
k = gv_key("repeat_slider_kotei_atai");
if(GlobalVariableCheck(k))
{
repeat_slider_kotei_atai = (int)MathRound(GlobalVariableGet(k)); loaded = true;
} // 読込
if(kikan_slider_atai < 1)
{
kikan_slider_atai = 1;
} // 1未満なら1へ
if(kikan_slider_atai > 200)
{
kikan_slider_atai = 200;
} // 200超なら200へ
if(kikan_slider_kotei_atai < 1)
{
kikan_slider_kotei_atai = 1;
} // 1未満なら1へ
if(kikan_slider_kotei_atai > 200)
{
kikan_slider_kotei_atai = 200;
} // 200超なら200へ
if(sma_kikan < 1)
{
sma_kikan = 1;
} // 1未満なら1へ
if(sma_kikan > 200)
{
sma_kikan = 200;
} // 200超なら200へ
if(repeat_slider_atai < 0)
{
repeat_slider_atai = 0;
} // 0未満なら0へ
if(repeat_slider_atai > 50)
{
repeat_slider_atai = 50;
} // 50超なら50へ
if(repeat_slider_kotei_atai < 0)
{
repeat_slider_kotei_atai = 0;
} // 0未満なら0へ
if(repeat_slider_kotei_atai > 50)
{
repeat_slider_kotei_atai = 50;
} // 50超なら50へ
if(sma_switch && ema_switch)
{
ema_switch = false;
} // 片方にする
if(!sma_switch && !ema_switch)
{
sma_switch = true;
} // どちらかON
if(hige_switch && jittai_switch)
{
jittai_switch = false;
} // 片方にする
if(!hige_switch && !jittai_switch)
{
hige_switch = true;
} // どちらかON
if(kakutei_switch && mikakutei_switch)
{
mikakutei_switch = false;
} // 片方にする
if(!kakutei_switch && !mikakutei_switch)
{
mikakutei_switch = true;
} // どちらかON
return(loaded); // 読み込み結果を返す
}
これは、起動時に GlobalVariable から値を取り出して、変数へ戻す(復元する)処理です。
GlobalVariableCheck() で「保存があるか」を確認
あればGlobalVariableGet() で値を読み込む
スライダーは整数にしたいので MathRound() して int に戻す
さらに、範囲外の値が入っていても壊れないように 上下限チェック(1~200、0~50) をかけています
そして、SMA/EMAやHIGE/JITTAIなど「同時ONは不可」の組み合わせは、最後に 排他制御 で整えています
いつ読み込み・いつ保存しているか(呼び出し箇所)
int OnInit()
{
ui_state_load(); // 状態を読み込む
ui_state_save(); // 状態を保存する
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
ui_state_save(); // 状態を保存する
}
OnInit() の序盤で読み込み → 前回の状態に戻す
その後、ボタン生成やスライダー表示をその状態で作り直す
操作のたびにも ui_state_save() を呼んで更新している
終了時(OnDeinit)にも保存して、取りこぼしを防いでいる
ボタンの「配置(X/Y)」やスライダーの「位置そのもの」は記憶していない
ObjectSetInteger(0, alert_waku, OBJPROP_XDISTANCE, 85);
ObjectSetInteger(0, alert_waku, OBJPROP_YDISTANCE, 20);
int kikan_slider_x = 25; int kikan_slider_y = 92;
ここは、毎回コードの固定値で置いているだけです。
つまり「配置をユーザー操作で変えて、その配置を次回も保持する」ような仕組みは入っていません。
記憶しているのはあくまで、ボタンのON/OFF(スイッチ類)スライダーの数値(期間・リピート)で、座標(配置)を保存している処理は現状ありません。
simple-ma-touch-alertのMQL4のコードについて
今回紹介しているコードはMQL5のものになります。両方紹介するとページの文字数が恐ろしい事になるのでMQL4のコードに関しては、「simple-ma-touch-alert.mq4」のファイルからご確認下さい。
【MT4】simple-ma-touch-alretのファイルをダウンロードすれば、「simple-ma-touch-alert.ex4」「simple-ma-touch-alert.mq4」の2つのファイルが入っています。
simple-ma-touch-alert.ex4は実行ファイル、simple-ma-touch-alert.mq4はソースコードの確認が可能です。

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