イントロダクション(概要)
- サマリー:遅行指標(インジケーター)を100%排除し、MT5のミリ秒単位のTick配信構造に潜む「流動性の偏り」と「大口のステルス執行」を看破する本プロジェクトの目的を宣言。
- ターゲット:スピード競争で勝てない個人投資家が、統計的・需給的なエッジ(優位性)を持って「極小SL(損切り)」の待ち伏せを成立させるための着地点を提示。
市場マイクロストラクチャーにおける需給のゆがみ
MQL5 Tick構造とフラグ
struct MqlTick
{
datetime time; // time of this price update
double bid; // current Bid price
double ask; // current Ask price
double last; // Last trade price
ulong volume; // volume for Last price
long time_msc; // time of this price update in milliseconds
uint flags; // flags (which fields of the structure have changed)
double volume_real; // volume for the Last price with increased accuracy
};
| 定数 | 値 | 説明 |
| TICK_FLAG_BID | 2 | Bid price changed |
| TICK_FLAG_ASK | 4 | Ask price changed |
| TICK_FLAG_LAST | 8 | Last price changed |
| TICK_FLAG_VOLUME | 16 | Volume changed |
| TICK_FLAG_BUY | 32 | Tick was generated as a result of a buy trade |
| TICK_FLAG_SELL | 64 | Tick was generated as a result of a sell tradec |
| TICK_FLAG_VOLUME_REAL | 1024 | Large-Order Intersection |
フラグは上記の和が戻り値となる
参照(MQL5公式リファレンス)
①:フラグ98/100が示すパニックバイアスの基本定義
フラグ98(Bidのみチカチカ)= 一般大衆による下落パニック(成行売り殺到)のサンプリング動態。フラグ100(Askのみチカチカ)= 一般大衆による上昇踏み上げ(成行買い殺到)のサンプリング動態。- 日常のボラティリティにおける理論的期待値(1分間に通常70、閑散期に20〜30Tick)の統計的整合性。
②:板読みの盲点「価格と数値の逆転現象(ハメ技)」
- 本来ならば、98が優勢なら価格下落、100なら価格上昇だが、ローソクが逆のほうに動くことがある。
- 98:相次ぐ成行売りを、一瞬で大口のステルス買いが吸収。流動性が薄くなったところへ買いが入ると価格が上昇
- 100:相次ぐ成行買いを、一瞬で大口のステルス売りが吸収。流動性が薄くなったところへ売りがが入ると価格が下落
インジケータ
TickHistTxt
直近N個のTickデータをチャートの左上にコメント表示。
#property copyright "Custom Quant"
#property version "1.05"
#property indicator_chart_window // メインチャートウィンドウにテキスト表示
#property indicator_buffers 1
#property indicator_plots 1
// グラフを描くためのプロパティ(indicator_buffersやplots)は、エラーの原因になるためすべて完全削除
// 内部で「96」の連続をカウントするための変数
int silence_96_streak = 0;
int last_bar_index = -1;
// 直近5回分のフラグの履歴を保存する配列
int flag_history[5];
//+------------------------------------------------------------------+
//| フラグを人間用に読みやすく翻訳(デコード)する関数 |
//+------------------------------------------------------------------+
string DecodeFlag(int flag_val)
{
if(flag_val < 1024) return(""); // 1024未満なら余計な文字は出さない
int base_flag = flag_val - 1024; // 1024を引いたピュアな数字
string detail = "";
if(base_flag == 96) detail = " (1024系 + 🔴96完全静寂)";
if(base_flag == 98) detail = " (1024系 + 💧98Bidチカ)";
if(base_flag == 100) detail = " (1024系 + 🍇100Askチカ)";
if(base_flag == 102) detail = " (1024系 + ⚡102両方変更)";
return(" => [ " + IntegerToString(base_flag) + " ]" + detail);
}
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
IndicatorSetString(INDICATOR_SHORTNAME, "Quant_Number_Monitor");
silence_96_streak = 0;
last_bar_index = -1;
// 履歴配列の初期化
for(int i=0; i<5; i++) flag_history[i] = 0;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// インジケーターを消した時は、画面の文字を綺麗に消去する
Comment("");
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
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[])
{
// 最新の1Tick情報を取得
MqlTick last_tick;
if(!SymbolInfoTick(_Symbol, last_tick)) return(rates_total);
// 本物のスプレッド(pips)を計算
double current_spread = (last_tick.ask - last_tick.bid) * 100.0;
// 1024が加算されていても、されていなくてもベースの数字を取り出す
int base_flag = (int)last_tick.flags;
if(base_flag >= 1024)
{
base_flag = base_flag - 1024; // 1024系の場合は1024を引いてピュアな数字にする
}
// --- ここまで追加 ---
// 【96の連続カウントロジック】(すべてbase_flagでの判定に切り替え)
if(base_flag == 96)
{
silence_96_streak++; // 1024系を含めた「96」が来たら漏らさずカウントアップ
}
else if(base_flag == 98 || base_flag == 100 || base_flag == 102)
{
// 1024系を含めた他のフラグが来たら、静寂が明けたとみなしてリセット
silence_96_streak = 0;
}
// 直近のフラグ履歴をずらしながら記憶(歩み値の数字)
if(flag_history[0] != (int)last_tick.flags)
{
for(int i = 4; i > 0; i--) {
flag_history[i] = flag_history[i-1];
}
flag_history[0] = (int)last_tick.flags;
}
// タイムスタンプを文字化
string time_str = TimeToString(last_tick.time, TIME_DATE|TIME_MINUTES|TIME_SECONDS) + "." + IntegerToString(last_tick.time_msc % 1000, 3, '0');
// 【生の数字を画面左上に叩き出す】
string comment_text =
"=========================================\n" +
" 📊 [QUANT ORDERFLOW MONITOR] \n" +
"=========================================\n" +
" ⏰ 現在時刻 (ミリ秒) : " + time_str + "\n" +
" 🎫 最新のフラグ番号 : " + IntegerToString(last_tick.flags) + "\n" +
" 🔴 現在の96連続回数 : " + IntegerToString(silence_96_streak) + " 回連続中\n" +
" 📏 リアルスプレッド : " + DoubleToString(current_spread, 1) + " pips\n" +
" 💵 現在のBid価格 : " + DoubleToString(last_tick.bid, 3) + "\n" +
" 💴 現在のAsk価格 : " + DoubleToString(last_tick.ask, 3) + "\n" +
"=========================================\n" +
" 📜 直近のフラグ歩み値履歴(上が最新):\n" +
" - Tick 1 : フラグ [" + IntegerToString(flag_history[0]) + DecodeFlag(flag_history[0])+ "]\n" +
" - Tick 2 : フラグ [" + IntegerToString(flag_history[1]) + DecodeFlag(flag_history[1])+ "]\n" +
" - Tick 3 : フラグ [" + IntegerToString(flag_history[2]) + DecodeFlag(flag_history[2])+ "]\n" +
" - Tick 4 : フラグ [" + IntegerToString(flag_history[3]) + DecodeFlag(flag_history[3])+ "]\n" +
" - Tick 5 : フラグ [" + IntegerToString(flag_history[4]) + DecodeFlag(flag_history[4])+ "]\n" +
"=========================================";
Comment(comment_text);
return(rates_total);
}
TickLabel
ラベルに各ティックフラグの1分間の状態をまとめて表示。
#property copyright "Custom Quant"
#property version "3.15"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
input int Label_X_Coordinate = 50;
input int Label_Y_Coordinate = 150;
input int Label_Y_Step = 15;
input int Alert_Hold_Seconds = 3;
// --- 時間軸管理 ---
datetime current_bar_time = 0;
datetime current_day_time = 0;
// --- ミクロ(1分リセット)カウンタ ---
int sum_98_ticks = 0; int sum_100_ticks = 0; int sum_102_ticks = 0; int sum_96_0_ticks = 0;
// --- マクロ(本日累計)カウンタ ---
long total_98_day = 0;
long total_100_day = 0;
// --- A. 真空チェイサー(96/0直後追跡)用変数 ---
bool silence_tracked = false;
datetime silence_time = 0;
string next_move_direction = "NONE";
// --- B. 超軽量リングバッファ(密度急減速)用変数 (要素数50固定) ---
#define RING_SIZE 10
#define RING_THRESHOLD 3
ulong buffer_98[RING_SIZE];
int idx_98 = 0; int cnt_98 = 0; ulong prev_dur_98 = 0;
string status_98 = "STABLE"; datetime alert_time_98 = 0;
ulong buffer_100[RING_SIZE];
int idx_100 = 0; int cnt_100 = 0; ulong prev_dur_100 = 0;
string status_100 = "STABLE"; datetime alert_time_100 = 0;
// オブジェクト配列定義 (8行構成)
string label_names[8];
// ==========================================
// 💡 オブジェクトの生成と配置プロパティを100%担保する安全関数
// ==========================================
void EnforceLabelCreation()
{
for(int i = 0; i < 8; i++)
{
// 存在しない場合のみ安全に新規生成
if(ObjectFind(0, label_names[i]) < 0)
{
ObjectCreate(0, label_names[i], OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, label_names[i], OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, label_names[i], OBJPROP_FONTSIZE, 10);
ObjectSetString(0, label_names[i], OBJPROP_FONT, "Meiryo");
ObjectSetInteger(0, label_names[i], OBJPROP_XDISTANCE, Label_X_Coordinate);
ObjectSetInteger(0, label_names[i], OBJPROP_YDISTANCE, Label_Y_Coordinate + (i * Label_Y_Step));
}
}
}
// ==========================================
// 今日の00:00からの履歴Tickを完全に復元する関数
// ==========================================
void RestoreTodayTicks()
{
total_98_day = 0;
total_100_day = 0;
datetime now_time = TimeCurrent();
MqlDateTime dt;
TimeToStruct(now_time, dt);
dt.hour = 0; dt.min = 0; dt.sec = 0;
datetime today_00 = StructToTime(dt);
MqlTick tick_array[];
int copied = CopyTicksRange(_Symbol, tick_array, COPY_TICKS_ALL, today_00 * 1000, now_time * 1000);
if(copied > 0)
{
for(int i = 0; i < copied; i++)
{
int flags = (int)tick_array[i].flags;
if(flags >= 1024) flags -= 1024;
if(flags == 98) total_98_day++;
else if(flags == 100) total_100_day++;
}
}
current_day_time = today_00;
}
int OnInit()
{
IndicatorSetString(INDICATOR_SHORTNAME, "Quant_Matrix_v3.15_Stable");
label_names[0] = "Quant_98_Label";
label_names[1] = "Quant_100_Label";
label_names[2] = "Quant_102_Label";
label_names[3] = "Quant_96_0_Label";
label_names[4] = "Quant_Bias_Label";
label_names[5] = "Quant_Silence_Trigger_Label";
label_names[6] = "Quant_Weak_98_Label";
label_names[7] = "Quant_Weak_100_Label";
// 初期化時は一度全削除
for(int i = 0; i < 8; i++) ObjectDelete(0, label_names[i]);
current_bar_time = 0;
sum_98_ticks = 0; sum_100_ticks = 0; sum_102_ticks = 0; sum_96_0_ticks = 0;
silence_tracked = false; next_move_direction = "NONE";
idx_98 = 0; cnt_98 = 0; prev_dur_98 = 0; status_98 = "STABLE";
idx_100 = 0; cnt_100 = 0; prev_dur_100 = 0; status_100 = "STABLE";
ArrayInitialize(buffer_98, 0);
ArrayInitialize(buffer_100, 0);
// 履歴の復元
RestoreTodayTicks();
// 初回生成
EnforceLabelCreation();
ChartRedraw(0);
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
for(int i=0; i<8; i++) ObjectDelete(0, label_names[i]);
ChartRedraw(0);
}
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[])
{
if(rates_total < 2) return(0);
// 💡 【最強のガード】1行目のオブジェクトがあるか1回だけチェック。
// チャットの時間足変更やリロードで万が一表示が消えても、ミリ秒の次の1Tickで即座に一斉自動復活させる
if(ObjectFind(0, label_names[0]) < 0)
{
EnforceLabelCreation();
}
if(prev_calculated == 0)
{
RestoreTodayTicks();
current_bar_time = 0;
sum_98_ticks = 0; sum_100_ticks = 0; sum_102_ticks = 0; sum_96_0_ticks = 0;
}
MqlTick last_tick;
if(!SymbolInfoTick(_Symbol, last_tick)) return(prev_calculated);
datetime now = TimeCurrent();
datetime tick_minute_time = (last_tick.time / 60) * 60;
// 日付リセット
MqlDateTime dt; TimeToStruct(last_tick.time, dt);
dt.hour = 0; dt.min = 0; dt.sec = 0;
datetime today_00 = StructToTime(dt);
if(today_00 != current_day_time) RestoreTodayTicks();
// 1分リセット
if(tick_minute_time != current_bar_time)
{
sum_98_ticks = 0; sum_100_ticks = 0; sum_102_ticks = 0; sum_96_0_ticks = 0;
current_bar_time = tick_minute_time;
}
// 1024剥ぎ取り
int base_flag = (int)last_tick.flags;
if(base_flag >= 1024) base_flag -= 1024;
// サインタイマー管理
if(silence_tracked && (now - silence_time > Alert_Hold_Seconds)) { silence_tracked = false; next_move_direction = "NONE"; }
if(status_98 != "STABLE" && (now - alert_time_98 > Alert_Hold_Seconds)) status_98 = "STABLE";
if(status_100 != "STABLE" && (now - alert_time_100 > Alert_Hold_Seconds)) status_100 = "STABLE";
// --- リアルタイムHFT・リングバッファ処理 ---
if(prev_calculated >= rates_total - 1)
{
if(base_flag == 96 || base_flag == 0)
{
sum_96_0_ticks++;
silence_tracked = true;
silence_time = now;
next_move_direction = "WAITING...";
}
else if(base_flag == 102)
{
sum_102_ticks++;
}
else if(base_flag == 98)
{
sum_98_ticks++;
total_98_day++;
if(silence_tracked && next_move_direction == "WAITING...") next_move_direction = "BID_BREAK";
buffer_98[idx_98] = last_tick.time_msc;
idx_98 = (idx_98 + 1) % RING_SIZE;
if(cnt_98 < RING_SIZE) cnt_98++;
if(cnt_98 == RING_SIZE)
{
ulong oldest = buffer_98[idx_98];
ulong cur_dur = last_tick.time_msc - oldest;
if(prev_dur_98 > 0 && prev_dur_98 < 1000)
{
if(cur_dur > (prev_dur_98 * RING_THRESHOLD))
{
status_98 = "💧 [ 98_BID WEAK ] !!! (売り手弾切れ)";
alert_time_98 = now;
}
}
prev_dur_98 = cur_dur;
}
}
else if(base_flag == 100)
{
sum_100_ticks++;
total_100_day++;
if(silence_tracked && next_move_direction == "WAITING...") next_move_direction = "ASK_BREAK";
buffer_100[idx_100] = last_tick.time_msc;
idx_100 = (idx_100 + 1) % RING_SIZE;
if(cnt_100 < RING_SIZE) cnt_100++;
if(cnt_100 == RING_SIZE)
{
ulong oldest = buffer_100[idx_100];
ulong cur_dur = last_tick.time_msc - oldest;
if(prev_dur_100 > 0 && prev_dur_100 < 1000)
{
if(cur_dur > (prev_dur_100 * RING_THRESHOLD))
{
status_100 = "🔥 [ 100_ASK WEAK ] !!! (買い手弾切れ)";
alert_time_100 = now;
}
}
prev_dur_100 = cur_dur;
}
}
}
// マクロ比率計算
double ask_pct = 0.0, bid_pct = 0.0;
long total_day_ticks = total_98_day + total_100_day;
if(total_day_ticks > 0) { ask_pct = ((double)total_100_day / total_day_ticks) * 100.0; bid_pct = ((double)total_98_day / total_day_ticks) * 100.0; }
// 出力テキスト配列の格納
string texts[8];
texts[0] = "💧 [ 98_Bid_Chika ] : " + IntegerToString(sum_98_ticks) + " Ticks";
texts[1] = "🔥 [ 100_Ask_Chika ] : " + IntegerToString(sum_100_ticks) + " Ticks";
texts[2] = "⚔️ [ 102_Collision ] : " + IntegerToString(sum_102_ticks) + " Ticks";
texts[3] = "❄️ [ 96/0_Silence ] : " + IntegerToString(sum_96_0_ticks) + " Ticks";
texts[4] = "⚖️ [ Today_Bias ] : Ask " + DoubleToString(ask_pct, 1) + "% vs " + DoubleToString(bid_pct, 1) + "% Bid (総 " + IntegerToString(total_day_ticks) + " Ticks)";
color color_silence = clrDarkGray;
if(next_move_direction == "WAITING...") { texts[5] = "⚡ [ Silence Trigger ] : ❄️ READY..."; color_silence = clrOrange; }
else if(next_move_direction == "ASK_BREAK") { texts[5] = "⚡ [ Silence Trigger ] : 🔥 ASK BREAK !!!"; color_silence = clrDeepSkyBlue; }
else if(next_move_direction == "BID_BREAK") { texts[5] = "⚡ [ Silence Trigger ] : 💧 BID BREAK !!!"; color_silence = clrCrimson; }
else { texts[5] = "⚡ [ Silence Trigger ] : STANDBY"; color_silence = clrDarkGray; }
color color_98 = clrDarkGray;
if(status_98 == "STABLE") texts[6] = "⚡ [ Bid Momentum ] : MONITORING...";
else { texts[6] = status_98; color_98 = clrDeepSkyBlue; }
color color_100 = clrDarkGray;
if(status_100 == "STABLE") texts[7] = "⚡ [ Ask Momentum ] : MONITORING...";
else { texts[7] = status_100; color_100 = clrCrimson; }
color label_colors[8] = { clrCrimson, clrDeepSkyBlue, clrGold, clrWhite, clrLimeGreen, color_silence, color_98, color_100 };
// 値の上書き更新
for(int i = 0; i < 8; i++)
{
ObjectSetInteger(0, label_names[i], OBJPROP_COLOR, label_colors[i]);
ObjectSetString(0, label_names[i], OBJPROP_TEXT, texts[i]);
}
ChartRedraw(0);
return(rates_total);
}
/*#property copyright "Custom Quant"
#property version "2.35"
#property indicator_chart_window
// MT5の制約回避用ダミー設定
#property indicator_buffers 1
#property indicator_plots 1
input int Label_X_Coordinate = 50;
input int Label_Y_Coordinate = 150;
input int Label_Y_Step = 25;
input int Alert_Hold_Seconds = 5;
// --- 時間軸管理 ---
datetime current_bar_time = 0;
datetime current_day_time = 0;
// --- ミクロ(1分リセット)カウンタ ---
int sum_98_ticks = 0; int sum_100_ticks = 0; int sum_102_ticks = 0; int sum_96_0_ticks = 0;
// --- マクロ(本日累計:リセットなし)カウンタ ---
long total_98_day = 0;
long total_100_day = 0;
// --- 静寂チェイサー用 ---
bool silence_tracked = false;
datetime silence_time = 0;
string next_move_direction = "NONE";
// 💡 配列サイズを [6] で完全固定定義
string label_names[6];
// ==========================================
// 💡 今日の00:00からの履歴Tickを完全に復元する関数
// ==========================================
void RestoreTodayTicks()
{
total_98_day = 0;
total_100_day = 0;
datetime now_time = TimeCurrent();
// タイムゾーンによるズレを防ぐため、本日の00:00を厳密に計算
MqlDateTime dt;
TimeToStruct(now_time, dt);
dt.hour = 0; dt.min = 0; dt.sec = 0;
datetime today_00 = StructToTime(dt);
MqlTick tick_array[];
// 今日の00:00から現在までのTick履歴をすべて取得
int copied = CopyTicksRange(_Symbol, tick_array, COPY_TICKS_ALL, today_00 * 1000, now_time * 1000);
if(copied > 0)
{
for(int i = 0; i < copied; i++)
{
int flags = (int)tick_array[i].flags;
if(flags >= 1024) flags -= 1024; // 1024剥ぎ取り
if(flags == 98) total_98_day++;
else if(flags == 100) total_100_day++;
}
}
current_day_time = today_00; // 本日の00:00のタイムスタンプを保持
}
int OnInit()
{
IndicatorSetString(INDICATOR_SHORTNAME, "Quant_Silence_Chaser_v2.35");
current_bar_time = 0;
sum_98_ticks = 0; sum_100_ticks = 0; sum_102_ticks = 0; sum_96_0_ticks = 0;
silence_tracked = false; next_move_direction = "NONE";
// 💡 文字列配列の中身を安全に初期化
label_names[0] = "Quant_98_Label";
label_names[1] = "Quant_100_Label";
label_names[2] = "Quant_102_Label";
label_names[3] = "Quant_96_0_Label";
label_names[4] = "Quant_Bias_Label";
label_names[5] = "Quant_Silence_Trigger_Label";
// 起動時に本日のTick履歴を一撃で復元
RestoreTodayTicks();
// 既存オブジェクトの完全クリーンアップ
for(int i=0; i<6; i++)
{
ObjectDelete(0, label_names[i]);
}
ChartRedraw(0);
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
for(int i=0; i<6; i++)
{
ObjectDelete(0, label_names[i]);
}
ChartRedraw(0);
}
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[])
{
if(rates_total < 2) return(0);
// フルリロード時(過去データ再計算時)の累積値狂いを防止
if(prev_calculated == 0)
{
RestoreTodayTicks();
current_bar_time = 0;
sum_98_ticks = 0; sum_100_ticks = 0; sum_102_ticks = 0; sum_96_0_ticks = 0;
}
MqlTick last_tick;
if(!SymbolInfoTick(_Symbol, last_tick)) return(prev_calculated);
datetime now = TimeCurrent();
datetime tick_minute_time = (last_tick.time / 60) * 60;
// 💡 日付の切り替わり判定(00:00を跨いだら本日累積をクリアして再集計)
MqlDateTime dt;
TimeToStruct(last_tick.time, dt);
dt.hour = 0; dt.min = 0; dt.sec = 0;
datetime today_00 = StructToTime(dt);
if(today_00 != current_day_time)
{
RestoreTodayTicks();
}
// 【ミクロ1分リセット】分が変わったら1分カウンタのみをクリア
if(current_bar_time == 0)
{
current_bar_time = tick_minute_time;
}
else if(tick_minute_time != current_bar_time)
{
sum_98_ticks = 0; sum_100_ticks = 0; sum_102_ticks = 0; sum_96_0_ticks = 0;
current_bar_time = tick_minute_time;
}
// 1024剥ぎ取り
int base_flag = (int)last_tick.flags;
if(base_flag >= 1024) base_flag -= 1024;
// 静寂チェイサーの表示タイマー消去
if(silence_tracked && (now - silence_time > Alert_Hold_Seconds))
{
silence_tracked = false;
next_move_direction = "NONE";
}
// --- 💡 毎Tickの確実なリアルタイム加算判定 ---
// 過去のバーデータを読み込んでいる最中(prev_calculated < rates_total)は履歴重複を避けるため加算せず、
// 最新バーのリアルタイムTickが動いている局面でのみ純粋に加算を実行する
if(prev_calculated >= rates_total - 1)
{
if(base_flag == 96 || base_flag == 0)
{
sum_96_0_ticks++;
silence_tracked = true;
silence_time = now;
next_move_direction = "WAITING...";
}
else if(base_flag == 98)
{
sum_98_ticks++;
total_98_day++; // 本日累計へ確実にリアルタイム加算
if(silence_tracked && next_move_direction == "WAITING...") next_move_direction = "BID_BREAK";
}
else if(base_flag == 100)
{
sum_100_ticks++;
total_100_day++; // 本日累計へ確実にリアルタイム加算
if(silence_tracked && next_move_direction == "WAITING...") next_move_direction = "ASK_BREAK";
}
else if(base_flag == 102)
{
sum_102_ticks++;
}
}
// --- マクロ比率計算 ---
double ask_pct = 0.0, bid_pct = 0.0;
long total_day_ticks = total_98_day + total_100_day;
if(total_day_ticks > 0)
{
ask_pct = ((double)total_100_day / total_day_ticks) * 100.0;
bid_pct = ((double)total_98_day / total_day_ticks) * 100.0;
}
// --- 💡 テキスト文字列配列への格納(バグを完全解消) ---
string texts[6];
texts[0] = "💧 [ 98_Bid_Chika ] : " + IntegerToString(sum_98_ticks) + " Ticks";
texts[1] = "🔥 [ 100_Ask_Chika ] : " + IntegerToString(sum_100_ticks) + " Ticks";
texts[2] = "⚔️ [ 102_Collision ] : " + IntegerToString(sum_102_ticks) + " Ticks";
texts[3] = "❄️ [ 96/0_Silence ] : " + IntegerToString(sum_96_0_ticks) + " Ticks";
texts[4] = "⚖️ [ Today_Bias ] : Ask " + DoubleToString(ask_pct, 1) + "% vs " + DoubleToString(bid_pct, 1) + "% Bid (総 " + IntegerToString(total_day_ticks) + " Ticks)";
color trigger_color = clrGray;
if(next_move_direction == "WAITING...") { texts[5] = "⚡ [ Silence Trigger ] : ❄️ READY..."; trigger_color = clrOrange; }
else if(next_move_direction == "ASK_BREAK") { texts[5] = "⚡ [ Silence Trigger ] : 🔥 ASK BREAK !!!"; trigger_color = clrDeepSkyBlue; }
else if(next_move_direction == "BID_BREAK") { texts[5] = "⚡ [ Silence Trigger ] : 💧 BID BREAK !!!"; trigger_color = clrCrimson; }
else { texts[5] = "⚡ [ Silence Trigger ] : STANDBY"; trigger_color = clrDarkGray; }
color label_colors[6];
label_colors[0] = clrCrimson;
label_colors[1] = clrDeepSkyBlue;
label_colors[2] = clrGold;
label_colors[3] = clrWhite;
label_colors[4] = clrLimeGreen;
label_colors[5] = trigger_color;
// --- 💡 オブジェクト生成・配置ロジックの完全正常化 ---
for(int i = 0; i < 6; i++)
{
// 存在しない場合は新規作成。OnInitで削除されているため確実にここでCreateが走ります
if(ObjectFind(0, label_names[i]) < 0)
{
ObjectCreate(0, label_names[i], OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, label_names[i], OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, label_names[i], OBJPROP_FONTSIZE, 13);
ObjectSetString(0, label_names[i], OBJPROP_FONT, "Meiryo");
}
// 毎Tick、最新のテキストと最新の座標に確実に更新
ObjectSetInteger(0, label_names[i], OBJPROP_COLOR, label_colors[i]);
ObjectSetInteger(0, label_names[i], OBJPROP_XDISTANCE, Label_X_Coordinate);
ObjectSetInteger(0, label_names[i], OBJPROP_YDISTANCE, Label_Y_Coordinate + (i * Label_Y_Step));
ObjectSetString(0, label_names[i], OBJPROP_TEXT, texts[i]);
}
ChartRedraw(0);
return(rates_total);
}
*/
/*#property copyright "Custom Quant"
#property version "2.22"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
input int Label_X_Coordinate = 50;
input int Label_Y_Coordinate = 150;
input int Label_Y_Step = 25;
input int Alert_Hold_Seconds = 5;
// --- ミクロ/マクロ管理用の日付保持 ---
datetime current_bar_time = 0;
datetime current_day_time = 0;
// --- ミクロ(1分リセット)カウンタ ---
int sum_98_ticks = 0; int sum_100_ticks = 0; int sum_102_ticks = 0; int sum_96_0_ticks = 0;
// --- マクロ(本日累計:リセットなし)カウンタ ---
long total_98_day = 0;
long total_100_day = 0;
// --- 静寂チェイサー用 ---
bool silence_tracked = false;
datetime silence_time = 0;
string next_move_direction = "NONE";
string label_names[] = {
"Quant_98_Label", "Quant_100_Label", "Quant_102_Label", "Quant_96_0_Label",
"Quant_Bias_Label", "Quant_Silence_Trigger_Label"
};
int OnInit()
{
IndicatorSetString(INDICATOR_SHORTNAME, "Quant_Silence_Chaser_Fixed");
current_bar_time = 0;
current_day_time = 0;
sum_98_ticks = 0; sum_100_ticks = 0; sum_102_ticks = 0; sum_96_0_ticks = 0;
total_98_day = 0; total_100_day = 0;
silence_tracked = false; next_move_direction = "NONE";
for(int i=0; i<6; i++) ObjectDelete(0, label_names[i]);
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
for(int i=0; i<6; i++) ObjectDelete(0, label_names[i]);
}
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[])
{
if(rates_total < 2) return(0);
MqlTick last_tick;
if(!SymbolInfoTick(_Symbol, last_tick)) return(prev_calculated);
// --- 1. 時間軸の定義(ミリ秒ではなくサーバータイムの秒から丸める) ---
datetime tick_minute_time = (last_tick.time / 60) * 60;
datetime tick_day_time = (last_tick.time / 86400) * 86400; // 00:00
// 日付の切り替わり判定(新しい日になったら本日累積をリセット)
if(current_day_time == 0)
{
current_day_time = tick_day_time;
}
else if(tick_day_time != current_day_time)
{
total_98_day = 0;
total_100_day = 0;
current_day_time = tick_day_time;
}
// 1分ウィンドウのリセット判定
if(current_bar_time == 0)
{
current_bar_time = tick_minute_time;
}
else if(tick_minute_time != current_bar_time)
{
sum_98_ticks = 0; sum_100_ticks = 0; sum_102_ticks = 0; sum_96_0_ticks = 0;
current_bar_time = tick_minute_time;
}
// --- 2. 過去バーの初回計算時は累計に加算しない(リロード時の二重カウント・リセットを防止) ---
// prev_calculated == rates_total の時だけが「今リアルタイムに届いた最新の1Tick」
bool is_live_tick = (prev_calculated == rates_total);
// 1024剥ぎ取り
int base_flag = (int)last_tick.flags;
if(base_flag >= 1024) base_flag -= 1024;
// 静寂チェイサーのタイマー消去処理
datetime now = TimeCurrent();
if(silence_tracked && (now - silence_time > Alert_Hold_Seconds))
{
silence_tracked = false;
next_move_direction = "NONE";
}
// --- 3. リアルタイムTickのみを厳密にカウント ---
if(base_flag == 96 || base_flag == 0)
{
sum_96_0_ticks++;
silence_tracked = true;
silence_time = now;
next_move_direction = "WAITING...";
}
else if(base_flag == 98)
{
sum_98_ticks++;
if(is_live_tick) total_98_day++; // ライブTickのみ本日累計へ固定
if(silence_tracked && next_move_direction == "WAITING...") next_move_direction = "BID_BREAK";
}
else if(base_flag == 100)
{
sum_100_ticks++;
if(is_live_tick) total_100_day++; // ライブTickのみ本日累計へ固定
if(silence_tracked && next_move_direction == "WAITING...") next_move_direction = "ASK_BREAK";
}
else if(base_flag == 102)
{
sum_102_ticks++;
}
// --- 4. マクロ比率計算 ---
double ask_pct = 0.0, bid_pct = 0.0;
long total_day_ticks = total_98_day + total_100_day;
if(total_day_ticks > 0)
{
ask_pct = ((double)total_100_day / total_day_ticks) * 100.0;
bid_pct = ((double)total_98_day / total_day_ticks) * 100.0;
}
// --- 5. 文字列出力 ---
string texts[6];
texts[0] = "💧 [ 98_Bid_Chika ] : " + IntegerToString(sum_98_ticks) + " Ticks";
texts[1] = "🔥 [ 100_Ask_Chika ] : " + IntegerToString(sum_100_ticks) + " Ticks";
texts[2] = "⚔️ [ 102_Collision ] : " + IntegerToString(sum_102_ticks) + " Ticks";
texts[3] = "❄️ [ 96/0_Silence ] : " + IntegerToString(sum_96_0_ticks) + " Ticks";
texts[4] = "⚖️ [ Today_Bias ] : Ask " + DoubleToString(ask_pct, 1) + "% vs " + DoubleToString(bid_pct, 1) + "% Bid (総 " + IntegerToString(total_day_ticks) + " Ticks)";
color trigger_color = clrGray;
if(next_move_direction == "WAITING...") { texts[5] = "⚡ [ Silence Trigger ] : ❄️ READY..."; trigger_color = clrOrange; }
else if(next_move_direction == "ASK_BREAK") { texts[5] = "⚡ [ Silence Trigger ] : 🔥 ASK BREAK !!!"; trigger_color = clrDeepSkyBlue; }
else if(next_move_direction == "BID_BREAK") { texts[5] = "⚡ [ Silence Trigger ] : 💧 BID BREAK !!!"; trigger_color = clrCrimson; }
else { texts[5] = "⚡ [ Silence Trigger ] : STANDBY"; trigger_color = clrDarkGray; }
color label_colors[] = { clrCrimson, clrDeepSkyBlue, clrGold, clrWhite, clrLimeGreen, trigger_color };
for(int i = 0; i < 6; i++)
{
if(ObjectFind(0, label_names[i]) < 0)
{
ObjectCreate(0, label_names[i], OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, label_names[i], OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, label_names[i], OBJPROP_FONTSIZE, 13);
ObjectSetString(0, label_names[i], OBJPROP_FONT, "Meiryo");
}
ObjectSetInteger(0, label_names[i], OBJPROP_COLOR, label_colors[i]);
ObjectSetInteger(0, label_names[i], OBJPROP_XDISTANCE, Label_X_Coordinate);
ObjectSetInteger(0, label_names[i], OBJPROP_YDISTANCE, Label_Y_Coordinate + (i * Label_Y_Step));
ObjectSetString(0, label_names[i], OBJPROP_TEXT, texts[i]);
}
ChartRedraw(0);
// 本日累計カウントを破壊しないよう、prev_calculatedの仕組みのために正常な値を返す
return(rates_total);
}
*/
/*#property copyright "Custom Quant"
#property version "1.00"
#property indicator_chart_window
//plotとBufferがないと怒られるのでダミー設定
#property indicator_buffers 1
#property indicator_plots 1
input int Label_X_Coordinate = 50;
input int Label_Y_Coordinate = 150;
datetime current_bar_time = 0;
int sum_98_ticks = 0;
int OnInit()
{
IndicatorSetString(INDICATOR_SHORTNAME, "Pure_98_Counter");
current_bar_time = 0;
sum_98_ticks = 0;
ObjectDelete(0, "Quant_98_Label");
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
ObjectDelete(0, "Quant_98_Label");
}
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[])
{
if(rates_total < 2) return(0);
MqlTick last_tick;
if(!SymbolInfoTick(_Symbol, last_tick)) return(prev_calculated);
datetime tick_minute_time = (last_tick.time / 60) * 60;
if(current_bar_time == 0)
{
current_bar_time = tick_minute_time;
}
else if(tick_minute_time != current_bar_time)
{
sum_98_ticks = 0;
current_bar_time = tick_minute_time;
}
int base_flag = (int)last_tick.flags;
if(base_flag >= 1024) base_flag -= 1024;
if(base_flag == 98)
{
sum_98_ticks++;
}
string label_name = "Quant_98_Label";
string text_to_display = "💧 [ 98_Bid_Chika ] : " + IntegerToString(sum_98_ticks) + " Ticks";
if(ObjectFind(0, label_name) < 0)
{
ObjectCreate(0, label_name, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, label_name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, label_name, OBJPROP_FONTSIZE, 14);
ObjectSetString(0, label_name, OBJPROP_FONT, "Meiryo");
ObjectSetInteger(0, label_name, OBJPROP_COLOR, clrCrimson);
}
ObjectSetInteger(0, label_name, OBJPROP_XDISTANCE, Label_X_Coordinate);
ObjectSetInteger(0, label_name, OBJPROP_YDISTANCE, Label_Y_Coordinate);
ObjectSetString(0, label_name, OBJPROP_TEXT, text_to_display);
ChartRedraw(0);
return(rates_total);
}
*/

