MQL5:TickData取得ドル円ミリ秒パルス急減速ハッキングアーキテクチャ1

イントロダクション(概要)

  • サマリー:遅行指標(インジケーター)を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_BID2Bid price changed
TICK_FLAG_ASK4Ask price changed
TICK_FLAG_LAST8Last price changed
TICK_FLAG_VOLUME16Volume changed
TICK_FLAG_BUY32Tick was generated as a result of a buy trade
TICK_FLAG_SELL64Tick was generated as a result of a sell tradec
TICK_FLAG_VOLUME_REAL1024Large-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);
}
*/
タイトルとURLをコピーしました