Guest

Regime change for MES

Mar 5th, 2026
52
0
Never
Not a member of gistpad yet? Sign Up, it unlocks many cool features!
None 101.98 KB | None | 0 0
  1. //@version=5
  2. indicator("Phidias Master Engine v9.9.4 [Regime-Gated Alerts]", shorttitle="Phidias_v9.9.4", overlay=true, max_labels_count=500, max_boxes_count=500, max_lines_count=500)
  3.  
  4. // ═══════════════════════════════════════════════════════════════════════════════
  5. // ARCHITECTURE v9.9.4
  6. // • All alerts regime-gated: no standalone grade alerts outside regime context
  7. // • All four alert types carry full pipe-delimited payload for Gemini ingestion
  8. // • Type 1: fires on regime entry/flip when aligned FVG is fresh or reacting
  9. // • Type 2: fires once per regime when price first touches/reacts to aligned FVG
  10. // • Type 3: fires on regime breakdown to NEUTRAL
  11. // • Type 4: fires on X→A+/B+ grade transition within active regime + FVG
  12. // • Dead grade-toggle inputs removed; single regime_alert_on controls all
  13. // • Birth bar deduplication and reset on mitigation preserved from v9.9.2
  14. // ═══════════════════════════════════════════════════════════════════════════════
  15.  
  16. // ═══════════════════════════════════════════════════════════════════════════════
  17. // 1. INPUTS
  18. // ═══════════════════════════════════════════════════════════════════════════════
  19. nq_sym = input.symbol("NQ1!", "NQ Symbol", group="Institutional Sync")
  20. rty_sym = input.symbol("RTY1!", "RTY Symbol", group="Institutional Sync")
  21. pl_input = input.float(0, "Current P&L ($)", step=50, group="Preservation Gate")
  22. stop_pts = input.float(4.0, "Stop Loss (Pts)", group="Risk Audit")
  23. target_pts = input.float(8.0, "Target (Pts)", group="Risk Audit")
  24. rr_floor = 1.5
  25.  
  26. grp_auction = "Fixed Tick Auction (v9.6)"
  27. bin_res = input.float(1.0, "Bin Resolution (Points)", step=0.25, group=grp_auction)
  28. va_perc = input.float(70.0, "Value Area %", minval=10, maxval=100, group=grp_auction) / 100
  29. max_bins = 300
  30.  
  31. grp_sync = "Index Sync (Embedded)"
  32. sync_corr_len = input.int(30, "Sync Correlation Lookback", minval=10, maxval=100, group=grp_sync)
  33. sync_dir_len = input.int(5, "Sync Directional Lookback", minval=2, maxval=20, group=grp_sync)
  34. sync_smooth_len = input.int(5, "Sync Score Smoothing", minval=1, maxval=20, group=grp_sync)
  35. sync_w_nq = input.float(40.0, "Sync Weight: MES-NQ %", minval=0, maxval=100, group=grp_sync)
  36. sync_w_rty = input.float(30.0, "Sync Weight: MES-RTY %", minval=0, maxval=100, group=grp_sync)
  37. sync_w_dir = input.float(30.0, "Sync Weight: Direction %", minval=0, maxval=100, group=grp_sync)
  38.  
  39. grp_regime = "Regime Detection & Alerts"
  40. regime_window = input.int(20, "Regime Lookback (bars)", minval=10, maxval=60, group=grp_regime)
  41. regime_sync_thresh = input.float(70.0, "Sync Threshold for Regime", minval=50, maxval=95, group=grp_regime)
  42. regime_bias_thresh = input.float(60.0, "Directional Bias Threshold %", minval=51, maxval=80, group=grp_regime)
  43. regime_min_bars = input.int(5, "Min Bars to Confirm Regime", minval=2, maxval=20, group=grp_regime)
  44. regime_alert_on = input.bool(true, "Fire Regime + Grade Alerts", group=grp_regime)
  45. regime_fvg_proximity = input.int(10, "5M FVG Freshness Window (1M bars)", minval=2, maxval=40, group=grp_regime)
  46.  
  47. grp_sweep = "Sweep Detection"
  48. z_threshold = input.float(2.5, "Z-Score Volume Threshold", minval=0.5, step=0.1, group=grp_sweep)
  49. touch_zone_ticks = input.int(2, "Touch Zone (ticks)", minval=0, maxval=10, group=grp_sweep)
  50. tick_size_input = input.float(0.25, "Tick Size", step=0.25, group=grp_sweep)
  51. max_tracked = input.int(10, "Max Tracked Boxes", minval=5, maxval=20, group=grp_sweep)
  52. sweep_stale_bars = input.int(15, "Sweep Stale After (bars)", minval=5, maxval=60, group=grp_sweep)
  53. sweep_expire_bars = input.int(30, "Sweep Expires After (bars)", minval=10, maxval=120, group=grp_sweep)
  54. displacement_pts = input.float(1.0, "Displacement Threshold (Pts)", minval=0.25, step=0.25, group=grp_sweep)
  55.  
  56. grp_zones = "Zone Memory"
  57. zone_expire_bars = input.int(120, "Climax Zone Expires After (bars)", minval=30, maxval=500, group=grp_zones)
  58.  
  59. grp_rsi = "RSI Divergence"
  60. rsi_len = input.int(14, "RSI Length", minval=5, maxval=30, group=grp_rsi)
  61. rsi_ob = input.float(70.0, "Overbought", minval=60, maxval=90, group=grp_rsi)
  62. rsi_os = input.float(30.0, "Oversold", minval=10, maxval=40, group=grp_rsi)
  63. div_lookback = input.int(20, "Divergence Lookback (bars)", minval=5, maxval=30, group=grp_rsi)
  64. div_confirm = input.int(2, "Divergence Confirm (bars)", minval=1, maxval=5, group=grp_rsi)
  65.  
  66. grp_mdl = "Market Data Logger"
  67. sessionStart = input.session("0000-2359", "Trading Session (All Day)", group=grp_mdl)
  68. london_tz = input.string("Europe/London", "Timezone", group=grp_mdl)
  69. show_pivots = input.bool(false, "Show Pivot Levels", group=grp_mdl)
  70. show_vwap = input.bool(false, "Show VWAP Lines", group=grp_mdl)
  71. show_tick_auction = input.bool(true, "Show Tick Auction POC/VAL/VAH", group=grp_mdl)
  72. show_ob = input.bool(false, "Show Order Blocks", group=grp_mdl)
  73. show_fvg = input.bool(false, "Show Fair Value Gaps", group=grp_mdl)
  74.  
  75. // ═══════════════════════════════════════════════════════════════════════════════
  76. // 2. PRICE DATA
  77. // ═══════════════════════════════════════════════════════════════════════════════
  78. mes_c = close
  79. mes_v = volume
  80. nq_c = request.security(nq_sym, timeframe.period, close)
  81. rty_c = request.security(rty_sym, timeframe.period, close)
  82.  
  83. // ═══════════════════════════════════════════════════════════════════════════════
  84. // 3. EMBEDDED SYNC SCORE
  85. // ═══════════════════════════════════════════════════════════════════════════════
  86. float corr_mes_nq = ta.correlation(mes_c, nq_c, sync_corr_len)
  87. float corr_mes_rty = ta.correlation(mes_c, rty_c, sync_corr_len)
  88. float norm_mes_nq = na(corr_mes_nq) ? 50.0 : (corr_mes_nq + 1) / 2 * 100
  89. float norm_mes_rty = na(corr_mes_rty) ? 50.0 : (corr_mes_rty + 1) / 2 * 100
  90.  
  91. f_sync_dir(src, lookback) =>
  92. diff = src - src[lookback]
  93. diff > 0 ? 1 : diff < 0 ? -1 : 0
  94.  
  95. int sync_mes_dir = f_sync_dir(mes_c, sync_dir_len)
  96. int sync_nq_dir = f_sync_dir(nq_c, sync_dir_len)
  97. int sync_rty_dir = f_sync_dir(rty_c, sync_dir_len)
  98.  
  99. float sync_dir_score = 50.0
  100. if sync_mes_dir == 0 and sync_nq_dir == 0 and sync_rty_dir == 0
  101. sync_dir_score := 50.0
  102. else if sync_mes_dir == sync_nq_dir and sync_nq_dir == sync_rty_dir and sync_mes_dir != 0
  103. sync_dir_score := 100.0
  104. else if sync_mes_dir == sync_nq_dir and sync_mes_dir != 0
  105. sync_dir_score := 66.0
  106. else if sync_mes_dir == sync_rty_dir and sync_mes_dir != 0
  107. sync_dir_score := 66.0
  108. else if sync_nq_dir == sync_rty_dir and sync_nq_dir != 0
  109. sync_dir_score := 40.0
  110. else
  111. sync_dir_score := 33.0
  112.  
  113. float sync_total_w = sync_w_nq + sync_w_rty + sync_w_dir
  114. float sync_raw = sync_total_w > 0 ? ((norm_mes_nq * sync_w_nq) + (norm_mes_rty * sync_w_rty) + (sync_dir_score * sync_w_dir)) / sync_total_w : 50.0
  115. sync_raw := math.min(100, math.max(0, sync_raw))
  116. float sync_score = ta.ema(sync_raw, sync_smooth_len)
  117.  
  118. // ═══════════════════════════════════════════════════════════════════════════════
  119. // 4. LONDON MIDPOINT (08:00 UTC)
  120. // ═══════════════════════════════════════════════════════════════════════════════
  121. var float lon_mid = na
  122. var bool lon_tested = false
  123. is_lon_start = not na(time(timeframe.period, "0800-0805", "UTC"))
  124. if is_lon_start
  125. lon_mid := (high + low) / 2
  126. lon_tested := false
  127. if not na(lon_mid) and not lon_tested
  128. if low <= lon_mid and high >= lon_mid
  129. lon_tested := true
  130.  
  131. // ═══════════════════════════════════════════════════════════════════════════════
  132. // 5. ZONE MEMORY
  133. // ═══════════════════════════════════════════════════════════════════════════════
  134. vol_ma = ta.sma(mes_v, 20)
  135. atr_val = ta.atr(14)
  136. atr_ma = ta.sma(atr_val, 20)
  137. is_climax = mes_v > vol_ma * 1.8 or atr_val > atr_ma * 1.5
  138.  
  139. var red_highs = array.new_float(3, na)
  140. var red_lows = array.new_float(3, na)
  141. var grn_highs = array.new_float(3, na)
  142. var grn_lows = array.new_float(3, na)
  143. var red_births = array.new_int(3, 0)
  144. var grn_births = array.new_int(3, 0)
  145. var int r_ghosts = 0
  146. var int g_ghosts = 0
  147.  
  148. if is_lon_start
  149. r_ghosts := 0
  150. g_ghosts := 0
  151.  
  152. if is_climax
  153. if close < open
  154. array.unshift(red_highs, high)
  155. array.unshift(red_lows, low)
  156. array.unshift(red_births, bar_index)
  157. array.pop(red_highs)
  158. array.pop(red_lows)
  159. array.pop(red_births)
  160. else
  161. array.unshift(grn_highs, high)
  162. array.unshift(grn_lows, low)
  163. array.unshift(grn_births, bar_index)
  164. array.pop(grn_highs)
  165. array.pop(grn_lows)
  166. array.pop(grn_births)
  167.  
  168. // ═══════════════════════════════════════════════════════════════════════════════
  169. // 6. REFINED ZONE ANALYSIS
  170. // ═══════════════════════════════════════════════════════════════════════════════
  171. bool in_red = false
  172. bool in_grn = false
  173. bool box_broken = false
  174. string break_type = ""
  175. float nearest_resistance = na
  176. float nearest_support = na
  177.  
  178. for i = 0 to 2
  179. rh = array.get(red_highs, i)
  180. rl = array.get(red_lows, i)
  181. gh = array.get(grn_highs, i)
  182. gl = array.get(grn_lows, i)
  183. int rb = array.get(red_births, i)
  184. int gb = array.get(grn_births, i)
  185.  
  186. if not na(rh) and rb > 0 and (bar_index - rb) > zone_expire_bars
  187. array.set(red_highs, i, na)
  188. array.set(red_lows, i, na)
  189. rh := na
  190. rl := na
  191.  
  192. if not na(gh) and gb > 0 and (bar_index - gb) > zone_expire_bars
  193. array.set(grn_highs, i, na)
  194. array.set(grn_lows, i, na)
  195. gh := na
  196. gl := na
  197.  
  198. if not na(rh)
  199. if mes_c >= rl and mes_c <= rh
  200. in_red := true
  201. float red_edge = rh
  202. if red_edge > mes_c
  203. if na(nearest_resistance) or (red_edge - mes_c) < (nearest_resistance - mes_c)
  204. nearest_resistance := red_edge
  205.  
  206. if not na(gh)
  207. if mes_c >= gl and mes_c <= gh
  208. in_grn := true
  209. float grn_edge = gl
  210. if grn_edge < mes_c
  211. if na(nearest_support) or (mes_c - grn_edge) < (mes_c - nearest_support)
  212. nearest_support := grn_edge
  213.  
  214. if not na(rh) and close < rl
  215. box_broken := true
  216. break_type := "SUPPLY"
  217. array.set(red_highs, i, na)
  218. array.set(red_lows, i, na)
  219. if not na(gh) and close > gh
  220. box_broken := true
  221. break_type := "DEMAND"
  222. array.set(grn_highs, i, na)
  223. array.set(grn_lows, i, na)
  224.  
  225. if not na(rh) and close > rh
  226. array.set(red_highs, i, na)
  227. array.set(red_lows, i, na)
  228. r_ghosts += 1
  229. if not na(gh) and close < gl
  230. array.set(grn_highs, i, na)
  231. array.set(grn_lows, i, na)
  232. g_ghosts += 1
  233.  
  234. // ═══════════════════════════════════════════════════════════════════════════════
  235. // 7. FIXED TICK AUCTION ENGINE
  236. // ═══════════════════════════════════════════════════════════════════════════════
  237. var float daily_anchor = na
  238. var float[] tick_bins = array.new_float(max_bins, 0.0)
  239. var float tick_poc = na
  240. var float tick_val = na
  241. var float tick_vah = na
  242.  
  243. if ta.change(time("D"))
  244. daily_anchor := open
  245.  
  246. f_get_idx(price, anchor) =>
  247. if na(anchor) or price < anchor - (max_bins / 2) * bin_res or price > anchor + (max_bins / 2) * bin_res
  248. -1
  249. else
  250. idx = math.floor((price - anchor) / bin_res) + (max_bins / 2)
  251. math.min(max_bins - 1, math.max(0, int(idx)))
  252.  
  253. if not na(daily_anchor)
  254. idx = f_get_idx(hlc3, daily_anchor)
  255. if idx >= 0
  256. array.set(tick_bins, idx, array.get(tick_bins, idx) + volume)
  257.  
  258. bool recalc_tick_va = bar_index % 10 == 0 or ta.change(time("D"))
  259. if recalc_tick_va and not na(daily_anchor)
  260. max_vol = 0.0
  261. poc_idx = 0
  262. for j = 0 to max_bins - 1
  263. bv = array.get(tick_bins, j)
  264. if bv > max_vol
  265. max_vol := bv
  266. poc_idx := j
  267.  
  268. if max_vol > 0
  269. total_vol = 0.0
  270. for j = 0 to max_bins - 1
  271. total_vol += array.get(tick_bins, j)
  272.  
  273. if total_vol > 0
  274. captured = max_vol
  275. lo_idx = poc_idx
  276. hi_idx = poc_idx
  277. target_vol = total_vol * va_perc
  278.  
  279. while captured < target_vol and (lo_idx > 0 or hi_idx < max_bins - 1)
  280. vol_below = lo_idx > 0 ? array.get(tick_bins, lo_idx - 1) : 0.0
  281. vol_above = hi_idx < max_bins - 1 ? array.get(tick_bins, hi_idx + 1) : 0.0
  282.  
  283. if lo_idx <= 0
  284. hi_idx += 1
  285. captured += vol_above
  286. else if hi_idx >= max_bins - 1
  287. lo_idx -= 1
  288. captured += vol_below
  289. else if vol_below >= vol_above
  290. lo_idx -= 1
  291. captured += vol_below
  292. else
  293. hi_idx += 1
  294. captured += vol_above
  295.  
  296. tick_poc := daily_anchor + (poc_idx - (max_bins / 2)) * bin_res + (bin_res / 2)
  297. tick_val := daily_anchor + (lo_idx - (max_bins / 2)) * bin_res
  298. tick_vah := daily_anchor + (hi_idx + 1 - (max_bins / 2)) * bin_res
  299.  
  300. if ta.change(time("D"))
  301. for j = 0 to max_bins - 1
  302. array.set(tick_bins, j, 0.0)
  303.  
  304. // ═══════════════════════════════════════════════════════════════════════════════
  305. // 8. SWEEP DETECTION ENGINE
  306. // ═══════════════════════════════════════════════════════════════════════════════
  307. float vol_sma_z = ta.sma(volume, 20)
  308. float vol_std_z = ta.stdev(volume, 20)
  309. float z_vol = vol_std_z != 0 ? (volume - vol_sma_z) / vol_std_z : 0
  310. bool high_vol = z_vol >= z_threshold
  311. bool bullish_candle = close > open
  312.  
  313. var float[] s_box_tops = array.new_float(0)
  314. var float[] s_box_bottoms = array.new_float(0)
  315. var int[] s_box_dirs = array.new_int(0)
  316. var int[] s_box_births = array.new_int(0)
  317. var int[] s_box_touches = array.new_int(0)
  318. var bool[] s_box_swept = array.new_bool(0)
  319.  
  320. if high_vol
  321. array.push(s_box_tops, high)
  322. array.push(s_box_bottoms, low)
  323. array.push(s_box_dirs, bullish_candle ? 1 : -1)
  324. array.push(s_box_births, bar_index)
  325. array.push(s_box_touches, 0)
  326. array.push(s_box_swept, false)
  327. if array.size(s_box_tops) > max_tracked
  328. array.shift(s_box_tops)
  329. array.shift(s_box_bottoms)
  330. array.shift(s_box_dirs)
  331. array.shift(s_box_births)
  332. array.shift(s_box_touches)
  333. array.shift(s_box_swept)
  334.  
  335. var int last_sweep_bar = 0
  336. var int last_sweep_dir = 0
  337. var int last_sweep_touches = 0
  338. var float last_sweep_level = na
  339. var bool last_sweep_wick = false
  340. var bool sweep_displaced = false
  341. var bool sweep_has_fvg = false
  342.  
  343. float touch_zone = touch_zone_ticks * tick_size_input
  344. int n_sboxes = array.size(s_box_tops)
  345.  
  346. if n_sboxes > 0
  347. for i = n_sboxes - 1 to 0
  348. float b_top = array.get(s_box_tops, i)
  349. float b_bottom = array.get(s_box_bottoms, i)
  350. int b_dir = array.get(s_box_dirs, i)
  351. int b_birth = array.get(s_box_births, i)
  352. bool b_swept = array.get(s_box_swept, i)
  353. int b_touch = array.get(s_box_touches, i)
  354.  
  355. if bar_index - b_birth > 200 or b_swept
  356. continue
  357.  
  358. if b_dir == 1
  359. bool near_bottom = low <= (b_bottom + touch_zone) and low >= (b_bottom - touch_zone)
  360. bool closed_above = close > b_bottom
  361. bool swept_below = low < (b_bottom - touch_zone)
  362. if near_bottom and closed_above and not swept_below
  363. array.set(s_box_touches, i, b_touch + 1)
  364. if swept_below
  365. array.set(s_box_swept, i, true)
  366. last_sweep_bar := bar_index
  367. last_sweep_dir := 1
  368. last_sweep_touches := b_touch
  369. last_sweep_level := b_bottom
  370. last_sweep_wick := close > b_bottom
  371. sweep_displaced := false
  372. sweep_has_fvg := false
  373.  
  374. if b_dir == -1
  375. bool near_top = high >= (b_top - touch_zone) and high <= (b_top + touch_zone)
  376. bool closed_below = close < b_top
  377. bool swept_above = high > (b_top + touch_zone)
  378. if near_top and closed_below and not swept_above
  379. array.set(s_box_touches, i, b_touch + 1)
  380. if swept_above
  381. array.set(s_box_swept, i, true)
  382. last_sweep_bar := bar_index
  383. last_sweep_dir := -1
  384. last_sweep_touches := b_touch
  385. last_sweep_level := b_top
  386. last_sweep_wick := close < b_top
  387. sweep_displaced := false
  388. sweep_has_fvg := false
  389.  
  390. if bar_index == last_sweep_bar + 1 and last_sweep_bar > 0
  391. if last_sweep_dir == 1
  392. sweep_displaced := (close - last_sweep_level) >= displacement_pts
  393. else if last_sweep_dir == -1
  394. sweep_displaced := (last_sweep_level - close) >= displacement_pts
  395.  
  396. int sweep_age = bar_index - last_sweep_bar
  397. bool sweep_active = last_sweep_bar > 0 and sweep_age <= sweep_expire_bars
  398. bool sweep_fresh = sweep_active and sweep_age <= sweep_stale_bars
  399. bool sweep_stale = sweep_active and sweep_age > sweep_stale_bars
  400.  
  401. // ═══════════════════════════════════════════════════════════════════════════════
  402. // 9. RSI DIVERGENCE ENGINE
  403. // ═══════════════════════════════════════════════════════════════════════════════
  404. float rsi_val = ta.rsi(close, rsi_len)
  405. float price_low_1 = ta.lowest(low, div_lookback)
  406. float price_low_2 = ta.lowest(low, div_lookback)[div_lookback]
  407. float price_high_1 = ta.highest(high, div_lookback)
  408. float price_high_2 = ta.highest(high, div_lookback)[div_lookback]
  409. float rsi_low_1 = ta.lowest(rsi_val, div_lookback)
  410. float rsi_low_2 = ta.lowest(rsi_val, div_lookback)[div_lookback]
  411. float rsi_high_1 = ta.highest(rsi_val, div_lookback)
  412. float rsi_high_2 = ta.highest(rsi_val, div_lookback)[div_lookback]
  413.  
  414. bool raw_bull_div = price_low_1 < price_low_2 and rsi_low_1 > rsi_low_2
  415. bool raw_bear_div = price_high_1 > price_high_2 and rsi_high_1 < rsi_high_2
  416.  
  417. var int bull_div_count = 0
  418. var int bear_div_count = 0
  419. if raw_bull_div
  420. bull_div_count += 1
  421. else
  422. bull_div_count := 0
  423. if raw_bear_div
  424. bear_div_count += 1
  425. else
  426. bear_div_count := 0
  427.  
  428. bool bull_div_confirmed = bull_div_count >= div_confirm
  429. bool bear_div_confirmed = bear_div_count >= div_confirm
  430.  
  431. string rsi_state = ""
  432. color rsi_bg = color.new(#000000, 40)
  433. color rsi_tc = color.gray
  434.  
  435. if bull_div_confirmed and rsi_val < rsi_os
  436. rsi_state := "OS + BULL DIV"
  437. rsi_bg := color.new(color.green, 10)
  438. rsi_tc := color.white
  439. else if bear_div_confirmed and rsi_val > rsi_ob
  440. rsi_state := "OB + BEAR DIV"
  441. rsi_bg := color.new(color.red, 10)
  442. rsi_tc := color.white
  443. else if bull_div_confirmed
  444. rsi_state := "BULL DIV"
  445. rsi_bg := color.new(color.green, 40)
  446. rsi_tc := color.green
  447. else if bear_div_confirmed
  448. rsi_state := "BEAR DIV"
  449. rsi_bg := color.new(color.red, 40)
  450. rsi_tc := color.red
  451. else if rsi_val > rsi_ob
  452. rsi_state := "OB " + str.tostring(rsi_val, "#.#")
  453. rsi_bg := color.new(color.red, 60)
  454. rsi_tc := color.red
  455. else if rsi_val < rsi_os
  456. rsi_state := "OS " + str.tostring(rsi_val, "#.#")
  457. rsi_bg := color.new(color.green, 60)
  458. rsi_tc := color.green
  459. else
  460. rsi_state := str.tostring(rsi_val, "#.#")
  461.  
  462. // ═══════════════════════════════════════════════════════════════════════════════
  463. // 10. MARKET DATA LOGGER: PIVOTS, VWAP, ORDER BLOCKS, FVGs
  464. // ═══════════════════════════════════════════════════════════════════════════════
  465. [xHigh, xLow, xClose] = request.security(syminfo.tickerid, "D", [high[1], low[1], close[1]])
  466. pivot_p = (xHigh + xLow + xClose) / 3
  467. pivot_r1 = pivot_p + (pivot_p - xLow)
  468. pivot_s1 = pivot_p - (xHigh - pivot_p)
  469. pivot_r2 = pivot_p + (xHigh - xLow)
  470. pivot_s2 = pivot_p - (xHigh - xLow)
  471. pivot_r3 = xHigh + 2 * (pivot_p - xLow)
  472. pivot_s3 = xLow - 2 * (xHigh - pivot_p)
  473. settlement = xClose
  474.  
  475. [weekHigh, weekLow, weekClose] = request.security(syminfo.tickerid, "W", [high[1], low[1], close[1]])
  476. week_pivot = (weekHigh + weekLow + weekClose) / 3
  477.  
  478. [monthHigh, monthLow, monthClose] = request.security(syminfo.tickerid, "M", [high[1], low[1], close[1]])
  479. month_pivot = (monthHigh + monthLow + monthClose) / 3
  480.  
  481. // VWAP
  482. var float vwap_midnight_sum_price_vol = 0.0
  483. var float vwap_midnight_sum_vol = 0.0
  484. var float vwap_230pm_sum_price_vol = 0.0
  485. var float vwap_230pm_sum_vol = 0.0
  486.  
  487. is_in_session = not na(time(timeframe.period, sessionStart, london_tz))
  488. var bool new_session = false
  489. new_session := is_in_session and not is_in_session[1]
  490.  
  491. var float session_midnight_start_time = na
  492. var float session_230pm_start_time = na
  493.  
  494. if new_session and hour(time, london_tz) == 0 and minute(time, london_tz) == 0
  495. session_midnight_start_time := time
  496. if is_in_session and hour(time, london_tz) == 14 and minute(time, london_tz) == 30
  497. session_230pm_start_time := time
  498.  
  499. if new_session and hour(time, london_tz) == 0 and minute(time, london_tz) == 0
  500. vwap_midnight_sum_price_vol := 0.0
  501. vwap_midnight_sum_vol := 0.0
  502. if is_in_session and hour(time, london_tz) == 14 and minute(time, london_tz) == 30
  503. vwap_230pm_sum_price_vol := 0.0
  504. vwap_230pm_sum_vol := 0.0
  505.  
  506. typical_price = hlc3
  507. if not na(session_midnight_start_time) and time >= session_midnight_start_time
  508. vwap_midnight_sum_price_vol += typical_price * volume
  509. vwap_midnight_sum_vol += volume
  510. if not na(session_230pm_start_time) and time >= session_230pm_start_time
  511. vwap_230pm_sum_price_vol += typical_price * volume
  512. vwap_230pm_sum_vol += volume
  513.  
  514. vwap_8am = vwap_midnight_sum_vol > 0 ? vwap_midnight_sum_price_vol / vwap_midnight_sum_vol : na
  515. vwap_230pm = vwap_230pm_sum_vol > 0 ? vwap_230pm_sum_price_vol / vwap_230pm_sum_vol : na
  516.  
  517. // Order Blocks (5M)
  518. var float bull_ob_top = na
  519. var float bull_ob_bottom = na
  520. var float bear_ob_top = na
  521. var float bear_ob_bottom = na
  522. var int bull_ob_time = na
  523. var int bear_ob_time = na
  524.  
  525. [ob_open_5m, ob_high_5m, ob_low_5m, ob_close_5m] = request.security(syminfo.tickerid, "5", [open, high, low, close])
  526.  
  527. bullish_ob = ob_close_5m[2] < ob_open_5m[2] and ob_close_5m[1] > ob_open_5m[1] and (ob_close_5m[1] - ob_open_5m[1]) > math.abs(ob_open_5m[2] - ob_close_5m[2]) * 1.5
  528. if bullish_ob and not na(ob_low_5m[2])
  529. bull_ob_bottom := ob_low_5m[2]
  530. bull_ob_top := ob_high_5m[2]
  531. bull_ob_time := time
  532.  
  533. bearish_ob = ob_close_5m[2] > ob_open_5m[2] and ob_close_5m[1] < ob_open_5m[1] and (ob_open_5m[1] - ob_close_5m[1]) > math.abs(ob_close_5m[2] - ob_open_5m[2]) * 1.5
  534. if bearish_ob and not na(ob_high_5m[2])
  535. bear_ob_top := ob_high_5m[2]
  536. bear_ob_bottom := ob_low_5m[2]
  537. bear_ob_time := time
  538.  
  539. if not na(bull_ob_time)
  540. bool bull_ob_expired = (time - bull_ob_time) > 120 * 60 * 1000
  541. bool bull_ob_mitigated = close < bull_ob_bottom
  542. if bull_ob_expired or bull_ob_mitigated
  543. bull_ob_top := na
  544. bull_ob_bottom := na
  545.  
  546. if not na(bear_ob_time)
  547. bool bear_ob_expired = (time - bear_ob_time) > 120 * 60 * 1000
  548. bool bear_ob_mitigated = close > bear_ob_top
  549. if bear_ob_expired or bear_ob_mitigated
  550. bear_ob_top := na
  551. bear_ob_bottom := na
  552.  
  553. // ─────────────────────────────────────────────────────────────────────────────
  554. // Fair Value Gaps (5M)
  555. // FIX (from v9.9.2): birth bar stamps only once per unique FVG level.
  556. // Birth bars reset to 0 on expiry/mitigation.
  557. // ─────────────────────────────────────────────────────────────────────────────
  558. var float bull_fvg_top = na
  559. var float bull_fvg_bottom = na
  560. var float bear_fvg_top = na
  561. var float bear_fvg_bottom = na
  562. var int bull_fvg_time = na
  563. var int bear_fvg_time = na
  564. var int bull_fvg_birth_bar = 0
  565. var int bear_fvg_birth_bar = 0
  566.  
  567. bullish_fvg = ob_low_5m[0] > ob_high_5m[2] and ob_low_5m[1] > ob_high_5m[2]
  568. bool bull_fvg_is_new = bullish_fvg and not na(ob_high_5m[2]) and ob_high_5m[2] != bull_fvg_bottom
  569. if bull_fvg_is_new
  570. bull_fvg_bottom := ob_high_5m[2]
  571. bull_fvg_top := ob_low_5m[0]
  572. bull_fvg_time := time
  573. bull_fvg_birth_bar := bar_index
  574.  
  575. bearish_fvg = ob_high_5m[0] < ob_low_5m[2] and ob_high_5m[1] < ob_low_5m[2]
  576. bool bear_fvg_is_new = bearish_fvg and not na(ob_low_5m[2]) and ob_low_5m[2] != bear_fvg_top
  577. if bear_fvg_is_new
  578. bear_fvg_top := ob_low_5m[2]
  579. bear_fvg_bottom := ob_high_5m[0]
  580. bear_fvg_time := time
  581. bear_fvg_birth_bar := bar_index
  582.  
  583. if not na(bull_fvg_time)
  584. bool bull_fvg_expired = (time - bull_fvg_time) > 120 * 60 * 1000
  585. bool bull_fvg_mitigated = close < bull_fvg_bottom
  586. if bull_fvg_expired or bull_fvg_mitigated
  587. bull_fvg_top := na
  588. bull_fvg_bottom := na
  589. bull_fvg_birth_bar := 0
  590.  
  591. if not na(bear_fvg_time)
  592. bool bear_fvg_expired = (time - bear_fvg_time) > 120 * 60 * 1000
  593. bool bear_fvg_mitigated = close > bear_fvg_top
  594. if bear_fvg_expired or bear_fvg_mitigated
  595. bear_fvg_top := na
  596. bear_fvg_bottom := na
  597. bear_fvg_birth_bar := 0
  598.  
  599. // ═══════════════════════════════════════════════════════════════════════════════
  600. // 10b. FVG CONFLUENCE CHECK AT SWEEP LEVEL
  601. // ═══════════════════════════════════════════════════════════════════════════════
  602. if sweep_active and not na(last_sweep_level)
  603. float fvg_tolerance = 2.0
  604. if last_sweep_dir == 1 and not na(bull_fvg_top) and not na(bull_fvg_bottom)
  605. if last_sweep_level >= (bull_fvg_bottom - fvg_tolerance) and last_sweep_level <= (bull_fvg_top + fvg_tolerance)
  606. sweep_has_fvg := true
  607. if last_sweep_dir == -1 and not na(bear_fvg_top) and not na(bear_fvg_bottom)
  608. if last_sweep_level >= (bear_fvg_bottom - fvg_tolerance) and last_sweep_level <= (bear_fvg_top + fvg_tolerance)
  609. sweep_has_fvg := true
  610.  
  611. // ═══════════════════════════════════════════════════════════════════════════════
  612. // 10c. ALERT MESSAGE BUILDER (PAYLOAD FOR ALL ALERT TYPES)
  613. // ═══════════════════════════════════════════════════════════════════════════════
  614. encode_distance(level) =>
  615. if na(level)
  616. "NA"
  617. else
  618. dist = close - level
  619. dist >= 0 ? "+" + str.tostring(dist, "#.##") : str.tostring(dist, "#.##")
  620.  
  621. build_alert_message() =>
  622. string msg = syminfo.ticker + "|" + str.tostring(time, "#") + "|" + str.tostring(close, "#.##") + "|"
  623. msg := msg + "DailyP:" + (not na(pivot_p) ? str.tostring(pivot_p, "#.##") : "NA") + "(" + encode_distance(pivot_p) + ")|"
  624. msg := msg + "DailyR1:" + (not na(pivot_r1) ? str.tostring(pivot_r1, "#.##") : "NA") + "(" + encode_distance(pivot_r1) + ")|"
  625. msg := msg + "DailyS1:" + (not na(pivot_s1) ? str.tostring(pivot_s1, "#.##") : "NA") + "(" + encode_distance(pivot_s1) + ")|"
  626. msg := msg + "Settlement:" + (not na(settlement) ? str.tostring(settlement,"#.##") : "NA") + "(" + encode_distance(settlement) + ")|"
  627. msg := msg + "VWAP_8AM:" + (not na(vwap_8am) ? str.tostring(vwap_8am, "#.##") + "(" + encode_distance(vwap_8am) + ")" : "NA") + "|"
  628. msg := msg + "POC:" + (not na(tick_poc) ? str.tostring(tick_poc, "#.##") + "(" + encode_distance(tick_poc) + ")" : "NA") + "|"
  629. msg := msg + "VAH:" + (not na(tick_vah) ? str.tostring(tick_vah, "#.##") + "(" + encode_distance(tick_vah) + ")" : "NA") + "|"
  630. msg := msg + "VAL:" + (not na(tick_val) ? str.tostring(tick_val, "#.##") + "(" + encode_distance(tick_val) + ")" : "NA") + "|"
  631. msg := msg + "BullOB:" + (not na(bull_ob_top) ? str.tostring(bull_ob_bottom, "#.##") + "-" + str.tostring(bull_ob_top, "#.##") : "NA") + "|"
  632. msg := msg + "BearOB:" + (not na(bear_ob_top) ? str.tostring(bear_ob_bottom, "#.##") + "-" + str.tostring(bear_ob_top, "#.##") : "NA") + "|"
  633. msg := msg + "BullFVG:" + (not na(bull_fvg_top) ? str.tostring(bull_fvg_bottom, "#.##") + "-" + str.tostring(bull_fvg_top, "#.##") : "NA") + "|"
  634. msg := msg + "BearFVG:" + (not na(bear_fvg_top) ? str.tostring(bear_fvg_bottom, "#.##") + "-" + str.tostring(bear_fvg_top, "#.##") : "NA") + "|"
  635. msg := msg + "SweepType:" + (sweep_active ? (last_sweep_wick ? "WICK" : "BODY") : "NONE") + "|"
  636. msg := msg + "Displaced:" + (sweep_displaced ? "YES" : "NO") + "|"
  637. msg := msg + "FVG_Conf:" + (sweep_has_fvg ? "YES" : "NO") + "|"
  638. msg := msg + "Sync:" + str.tostring(sync_score, "#.#") + "|"
  639. msg
  640.  
  641. string alert_message = build_alert_message()
  642.  
  643. // ═══════════════════════════════════════════════════════════════════════════════
  644. // 11. TARGET CALCULATIONS & DUAL GATE AUDIT
  645. // ═══════════════════════════════════════════════════════════════════════════════
  646. float long_target = mes_c + target_pts
  647. float short_target = mes_c - target_pts
  648. float dyn_rr = target_pts / stop_pts
  649. bool is_rsw = not na(time(timeframe.period, "1430-1445", "UTC"))
  650. bool rr_fail = dyn_rr < rr_floor
  651. bool loss_limit = pl_input <= -500
  652.  
  653. bool long_trap = in_red
  654. bool long_nq_fail = nq_c < nq_c[1] and not box_broken
  655. bool long_target_blocked = not na(nearest_resistance) and nearest_resistance < long_target
  656. string long_veto = ""
  657. if loss_limit
  658. long_veto := "LOSS"
  659. else if is_rsw
  660. long_veto := "RSW"
  661. else if rr_fail
  662. long_veto := "R:R"
  663. else if long_trap
  664. long_veto := "ZONE"
  665. else if long_nq_fail
  666. long_veto := "NQ"
  667. else if long_target_blocked
  668. long_veto := "BLOCKED"
  669. bool long_pass = long_veto == ""
  670.  
  671. bool short_trap = in_grn
  672. bool short_nq_fail = nq_c > nq_c[1] and not box_broken
  673. bool short_target_blocked = not na(nearest_support) and nearest_support > short_target
  674. string short_veto = ""
  675. if loss_limit
  676. short_veto := "LOSS"
  677. else if is_rsw
  678. short_veto := "RSW"
  679. else if rr_fail
  680. short_veto := "R:R"
  681. else if short_trap
  682. short_veto := "ZONE"
  683. else if short_nq_fail
  684. short_veto := "NQ"
  685. else if short_target_blocked
  686. short_veto := "BLOCKED"
  687. bool short_pass = short_veto == ""
  688.  
  689. // ═══════════════════════════════════════════════════════════════════════════════
  690. // 12. SETUP GRADING ENGINE
  691. // ═══════════════════════════════════════════════════════════════════════════════
  692. bool sweep_long_aligned = sweep_fresh and last_sweep_dir == 1
  693. bool sweep_short_aligned = sweep_fresh and last_sweep_dir == -1
  694.  
  695. int sweep_quality = 0
  696. if sweep_active
  697. if last_sweep_wick
  698. sweep_quality += 1
  699. if sweep_displaced
  700. sweep_quality += 1
  701. if sweep_has_fvg
  702. sweep_quality += 1
  703.  
  704. bool rsi_supports_long = bull_div_confirmed or rsi_val < rsi_os
  705. bool rsi_supports_short = bear_div_confirmed or rsi_val > rsi_ob
  706. bool rsi_warns_long = bear_div_confirmed
  707. bool rsi_warns_short = bull_div_confirmed
  708. bool rsi_neutral = not rsi_supports_long and not rsi_supports_short and not rsi_warns_long and not rsi_warns_short
  709.  
  710. string long_grade = "X"
  711. if not long_pass
  712. long_grade := "X"
  713. else if sweep_long_aligned and sweep_quality >= 2 and (rsi_supports_long or (rsi_neutral and not rsi_warns_long))
  714. long_grade := "A+"
  715. else if rsi_warns_long
  716. long_grade := "C"
  717. else if sweep_long_aligned or rsi_supports_long
  718. long_grade := "B+"
  719. else
  720. long_grade := "B+"
  721.  
  722. string short_grade = "X"
  723. if not short_pass
  724. short_grade := "X"
  725. else if sweep_short_aligned and sweep_quality >= 2 and (rsi_supports_short or (rsi_neutral and not rsi_warns_short))
  726. short_grade := "A+"
  727. else if rsi_warns_short
  728. short_grade := "C"
  729. else if sweep_short_aligned or rsi_supports_short
  730. short_grade := "B+"
  731. else
  732. short_grade := "B+"
  733.  
  734. f_grade_bg(string grade) =>
  735. grade == "A+" ? color.new(#00e676, 15) :
  736. grade == "B+" ? color.new(#448aff, 25) :
  737. grade == "C" ? color.new(#ffd600, 25) :
  738. color.new(#444455, 50)
  739.  
  740. f_grade_tc(string grade) =>
  741. grade == "A+" ? color.white :
  742. grade == "B+" ? color.white :
  743. grade == "C" ? color.black :
  744. color.gray
  745.  
  746. // ═══════════════════════════════════════════════════════════════════════════════
  747. // 12b. REGIME DETECTION ENGINE
  748. // ═══════════════════════════════════════════════════════════════════════════════
  749. float sync_avg = ta.sma(sync_score, regime_window)
  750.  
  751. bool this_bar_long_active = (long_grade == "A+" or long_grade == "B+")
  752. bool this_bar_short_active = (short_grade == "A+" or short_grade == "B+")
  753. float long_active_f = this_bar_long_active ? 1.0 : 0.0
  754. float short_active_f = this_bar_short_active ? 1.0 : 0.0
  755. float long_pct = ta.sma(long_active_f, regime_window) * 100
  756. float short_pct = ta.sma(short_active_f, regime_window) * 100
  757.  
  758. bool sync_sustained = not na(sync_avg) and sync_avg >= regime_sync_thresh
  759. bool long_dominates = long_pct >= regime_bias_thresh and long_pct > short_pct
  760. bool short_dominates = short_pct >= regime_bias_thresh and short_pct > long_pct
  761.  
  762. string current_regime = "NEUTRAL"
  763. if sync_sustained and long_dominates
  764. current_regime := "BULL"
  765. else if sync_sustained and short_dominates
  766. current_regime := "BEAR"
  767.  
  768. var string confirmed_regime = "NEUTRAL"
  769. var int regime_hold_count = 0
  770. var string prev_raw_regime = "NEUTRAL"
  771.  
  772. if current_regime == prev_raw_regime and current_regime != "NEUTRAL"
  773. regime_hold_count += 1
  774. else if current_regime != prev_raw_regime
  775. regime_hold_count := current_regime != "NEUTRAL" ? 1 : 0
  776.  
  777. prev_raw_regime := current_regime
  778.  
  779. string new_confirmed = confirmed_regime
  780. if current_regime != "NEUTRAL" and regime_hold_count >= regime_min_bars
  781. new_confirmed := current_regime
  782. else if current_regime == "NEUTRAL" and confirmed_regime != "NEUTRAL"
  783. new_confirmed := "NEUTRAL"
  784.  
  785. bool regime_entered = new_confirmed != "NEUTRAL" and confirmed_regime == "NEUTRAL"
  786. bool regime_exited = new_confirmed == "NEUTRAL" and confirmed_regime != "NEUTRAL"
  787. bool regime_flipped = new_confirmed != "NEUTRAL" and confirmed_regime != "NEUTRAL" and new_confirmed != confirmed_regime
  788.  
  789. string exiting_regime = confirmed_regime
  790. confirmed_regime := new_confirmed
  791.  
  792. // ═══════════════════════════════════════════════════════════════════════════════
  793. // 12c. FVG INTERACTION HELPERS
  794. // ═══════════════════════════════════════════════════════════════════════════════
  795. bool bull_fvg_active = not na(bull_fvg_bottom) and not na(bull_fvg_top)
  796. bool bear_fvg_active = not na(bear_fvg_bottom) and not na(bear_fvg_top)
  797.  
  798. bool price_in_bull_fvg = bull_fvg_active and close >= bull_fvg_bottom and close <= bull_fvg_top
  799. bool price_in_bear_fvg = bear_fvg_active and close >= bear_fvg_bottom and close <= bear_fvg_top
  800.  
  801. bool bull_fvg_wick_overlap = bull_fvg_active and low <= bull_fvg_top and high >= bull_fvg_bottom
  802. bool bear_fvg_wick_overlap = bear_fvg_active and low <= bear_fvg_top and high >= bear_fvg_bottom
  803.  
  804. bool bull_fvg_reaction_now = price_in_bull_fvg or (bull_fvg_wick_overlap and close > open)
  805. bool bear_fvg_reaction_now = price_in_bear_fvg or (bear_fvg_wick_overlap and close < open)
  806.  
  807. int bull_fvg_age = bull_fvg_active ? bar_index - bull_fvg_birth_bar : 9999
  808. int bear_fvg_age = bear_fvg_active ? bar_index - bear_fvg_birth_bar : 9999
  809.  
  810. bool bull_fvg_fresh = bull_fvg_age <= int(regime_fvg_proximity * 1.5)
  811. bool bear_fvg_fresh = bear_fvg_age <= int(regime_fvg_proximity * 1.5)
  812.  
  813. var bool bull_fvg_touched_this_regime = false
  814. var bool bear_fvg_touched_this_regime = false
  815.  
  816. if regime_entered or regime_flipped or regime_exited or confirmed_regime == "NEUTRAL"
  817. bull_fvg_touched_this_regime := false
  818. bear_fvg_touched_this_regime := false
  819.  
  820. // ═══════════════════════════════════════════════════════════════════════════════
  821. // 12d. REGIME ALERTS (ALL TYPES CARRY FULL PAYLOAD)
  822. // ═══════════════════════════════════════════════════════════════════════════════
  823.  
  824. // TYPE 1 — Transition alerts (entry / flip) + full payload
  825. if regime_alert_on and (regime_entered or regime_flipped)
  826. string regime_dir = confirmed_regime
  827. bool should_fire = false
  828. string reason = ""
  829. string fvg_detail = ""
  830. string tag = ""
  831.  
  832. if regime_dir == "BULL" and bull_fvg_active
  833. if bull_fvg_reaction_now
  834. should_fire := true
  835. reason := "PRICE INTERACTING / REJECTING BULL FVG NOW"
  836. tag := "[FVG REACTION @ SHIFT]"
  837. fvg_detail := "Bull FVG " + str.tostring(bull_fvg_bottom, "#.##") + "–" +
  838. str.tostring(bull_fvg_top, "#.##") + " (age " +
  839. str.tostring(bull_fvg_age) + " bars)"
  840. else if bull_fvg_fresh
  841. should_fire := true
  842. reason := "FRESH BULL FVG PRESENT AT REGIME SHIFT"
  843. tag := "[FRESH FVG @ SHIFT]"
  844. fvg_detail := "Bull FVG " + str.tostring(bull_fvg_bottom, "#.##") + "–" +
  845. str.tostring(bull_fvg_top, "#.##") + " (age " +
  846. str.tostring(bull_fvg_age) + " bars)"
  847.  
  848. else if regime_dir == "BEAR" and bear_fvg_active
  849. if bear_fvg_reaction_now
  850. should_fire := true
  851. reason := "PRICE INTERACTING / REJECTING BEAR FVG NOW"
  852. tag := "[FVG REACTION @ SHIFT]"
  853. fvg_detail := "Bear FVG " + str.tostring(bear_fvg_bottom, "#.##") + "–" +
  854. str.tostring(bear_fvg_top, "#.##") + " (age " +
  855. str.tostring(bear_fvg_age) + " bars)"
  856. else if bear_fvg_fresh
  857. should_fire := true
  858. reason := "FRESH BEAR FVG PRESENT AT REGIME SHIFT"
  859. tag := "[FRESH FVG @ SHIFT]"
  860. fvg_detail := "Bear FVG " + str.tostring(bear_fvg_bottom, "#.##") + "–" +
  861. str.tostring(bear_fvg_top, "#.##") + " (age " +
  862. str.tostring(bear_fvg_age) + " bars)"
  863.  
  864. if should_fire
  865. string transition_type = regime_flipped ? "FLIP" : "ENTRY"
  866. string msg = "REGIME " + transition_type + ": " + regime_dir + " " + tag + "\n" +
  867. reason + "\n" +
  868. fvg_detail + "\n" +
  869. "Sync avg: " + str.tostring(sync_avg, "#.#") + " | " +
  870. "Bias L:" + str.tostring(long_pct, "#.#") + "% S:" +
  871. str.tostring(short_pct, "#.#") + "%\n" +
  872. alert_message
  873. alert(msg, alert.freq_once_per_bar)
  874.  
  875. // TYPE 2 — Ongoing regime: first FVG touch / reaction + full payload
  876. bool new_bull_interaction = confirmed_regime == "BULL" and not bull_fvg_touched_this_regime and bull_fvg_reaction_now
  877. bool new_bear_interaction = confirmed_regime == "BEAR" and not bear_fvg_touched_this_regime and bear_fvg_reaction_now
  878.  
  879. if new_bull_interaction and regime_alert_on
  880. bull_fvg_touched_this_regime := true
  881. string bull_msg = "BULL REGIME ACTIVE + 5M FVG TOUCH / REACTION\n" +
  882. "FVG: " + str.tostring(bull_fvg_bottom, "#.##") + "–" +
  883. str.tostring(bull_fvg_top, "#.##") +
  884. " | Age: " + str.tostring(bull_fvg_age) + " bars" +
  885. " | Sync: " + str.tostring(sync_score, "#.#") + "%\n" +
  886. alert_message
  887. alert(bull_msg, alert.freq_once_per_bar_close)
  888.  
  889. if new_bear_interaction and regime_alert_on
  890. bear_fvg_touched_this_regime := true
  891. string bear_msg = "BEAR REGIME ACTIVE + 5M FVG TOUCH / REACTION\n" +
  892. "FVG: " + str.tostring(bear_fvg_bottom, "#.##") + "–" +
  893. str.tostring(bear_fvg_top, "#.##") +
  894. " | Age: " + str.tostring(bear_fvg_age) + " bars" +
  895. " | Sync: " + str.tostring(sync_score, "#.#") + "%\n" +
  896. alert_message
  897. alert(bear_msg, alert.freq_once_per_bar_close)
  898.  
  899. // TYPE 3 — Regime breakdown + full payload
  900. if regime_alert_on and regime_exited
  901. alert("REGIME BREAKDOWN | Was: " + exiting_regime +
  902. " | Sync avg dropped below " + str.tostring(regime_sync_thresh, "#") +
  903. " or bias lost | Now NEUTRAL\n" +
  904. alert_message, alert.freq_once_per_bar)
  905.  
  906. // ═══════════════════════════════════════════════════════════════════════════════
  907. // 13. CANDLE COLOURING & VISUALS
  908. // ═══════════════════════════════════════════════════════════════════════════════
  909. color candle_col = na
  910. if long_pass and not short_pass
  911. candle_col := color.new(color.green, 60)
  912. else if short_pass and not long_pass
  913. candle_col := color.new(color.red, 60)
  914. else if long_pass and short_pass
  915. candle_col := color.new(color.yellow, 60)
  916. else
  917. candle_col := color.new(color.gray, 80)
  918. barcolor(candle_col)
  919.  
  920. plot(lon_mid, title="London Mid", color=lon_tested ? color.gray : color.aqua, linewidth=2)
  921. plot(long_target, title="Long Target", color=color.new(color.green, 70), style=plot.style_linebr, linewidth=1)
  922. plot(short_target, title="Short Target", color=color.new(color.red, 70), style=plot.style_linebr, linewidth=1)
  923. plot(show_pivots ? pivot_p : na, title="Daily Pivot", color=color.yellow, linewidth=1)
  924. plot(show_vwap and not na(vwap_8am) ? vwap_8am : na, title="VWAP 8AM", color=color.blue, linewidth=2)
  925. plot(show_tick_auction and not na(tick_poc) ? tick_poc : na, title="Tick Auction POC", color=color.orange, linewidth=2, style=plot.style_linebr)
  926. plot(show_tick_auction and not na(tick_vah) ? tick_vah : na, title="Tick Auction VAH", color=color.new(color.red, 50), linewidth=1, style=plot.style_linebr)
  927. plot(show_tick_auction and not na(tick_val) ? tick_val : na, title="Tick Auction VAL", color=color.new(color.green, 50), linewidth=1, style=plot.style_linebr)
  928.  
  929. if show_ob and not na(bull_ob_top) and not na(bull_ob_bottom)
  930. box.new(left=bar_index - 50, top=bull_ob_top, right=bar_index, bottom=bull_ob_bottom, border_color=color.green, bgcolor=color.new(color.green, 90))
  931.  
  932. // ═══════════════════════════════════════════════════════════════════════════════
  933. // 14. REGIME-GATED GRADE ALERTS — TYPE 4 (FVG REQUIRED + FULL PAYLOAD)
  934. // ═══════════════════════════════════════════════════════════════════════════════
  935. var string prev_long_grade = "X"
  936. var string prev_short_grade = "X"
  937. var int conflict_cooldown = 0
  938. var int alert_cooldown = 0
  939.  
  940. bool dual_signal = long_grade != "X" and short_grade != "X"
  941.  
  942. if dual_signal
  943. conflict_cooldown := 5
  944. if conflict_cooldown > 0 and not dual_signal
  945. conflict_cooldown -= 1
  946. if alert_cooldown > 0
  947. alert_cooldown -= 1
  948.  
  949. bool alerts_allowed = not dual_signal and conflict_cooldown == 0 and alert_cooldown == 0
  950.  
  951. // Gate: regime must be active + aligned FVG interaction required
  952. bool regime_bull_fvg = confirmed_regime == "BULL" and bull_fvg_reaction_now
  953. bool regime_bear_fvg = confirmed_regime == "BEAR" and bear_fvg_reaction_now
  954.  
  955. bool long_grade_valid = (long_grade == "A+" or long_grade == "B+")
  956. bool short_grade_valid = (short_grade == "A+" or short_grade == "B+")
  957.  
  958. bool long_alert_new = long_grade_valid and prev_long_grade == "X" and alerts_allowed and regime_bull_fvg
  959. bool short_alert_new = short_grade_valid and prev_short_grade == "X" and alerts_allowed and regime_bear_fvg
  960.  
  961. if long_alert_new and regime_alert_on
  962. alert(long_grade + " LONG | BULL REGIME + FVG | " + alert_message, alert.freq_once_per_bar)
  963. alert_cooldown := 3
  964.  
  965. if short_alert_new and regime_alert_on
  966. alert(short_grade + " SHORT | BEAR REGIME + FVG | " + alert_message, alert.freq_once_per_bar)
  967. alert_cooldown := 3
  968.  
  969. prev_long_grade := long_grade
  970. prev_short_grade := short_grade
  971.  
  972. // ═══════════════════════════════════════════════════════════════════════════════
  973. // 15. MASTER DASHBOARD UI
  974. // ═══════════════════════════════════════════════════════════════════════════════
  975. var table phidias_tbl = table.new(position.top_left, columns=2, rows=8,
  976. bgcolor=color.new(color.black, 15),
  977. border_color=color.new(color.gray, 50),
  978. border_width=1)
  979.  
  980. if barstate.islast
  981. table.cell(phidias_tbl, 0, 0, "PHIDIAS ENGINE v9.9.4",
  982. text_color=color.white, text_size=size.small, text_halign=text.align_center)
  983. table.merge_cells(phidias_tbl, 0, 0, 1, 0)
  984.  
  985. color regime_col = confirmed_regime == "BULL" ? color.new(color.green, 20) :
  986. confirmed_regime == "BEAR" ? color.new(color.red, 20) :
  987. color.new(color.gray, 50)
  988. table.cell(phidias_tbl, 0, 1, "REGIME:",
  989. text_color=color.white, text_halign=text.align_left)
  990. table.cell(phidias_tbl, 1, 1, confirmed_regime,
  991. bgcolor=regime_col, text_color=color.white, text_halign=text.align_center)
  992.  
  993. color sync_col = sync_score > 75 ? color.green :
  994. sync_score > 60 ? color.orange : color.red
  995. table.cell(phidias_tbl, 0, 2, "INST. SYNC:",
  996. text_color=color.white, text_halign=text.align_left)
  997. table.cell(phidias_tbl, 1, 2, str.tostring(sync_score, "#.#") + "%",
  998. text_color=sync_col, text_halign=text.align_center)
  999.  
  1000. table.cell(phidias_tbl, 0, 3, "LONG GRADE:",
  1001. text_color=color.white, text_halign=text.align_left)
  1002. table.cell(phidias_tbl, 1, 3, long_grade,
  1003. bgcolor=f_grade_bg(long_grade), text_color=f_grade_tc(long_grade), text_halign=text.align_center)
  1004.  
  1005. table.cell(phidias_tbl, 0, 4, "SHORT GRADE:",
  1006. text_color=color.white, text_halign=text.align_left)
  1007. table.cell(phidias_tbl, 1, 4, short_grade,
  1008. bgcolor=f_grade_bg(short_grade), text_color=f_grade_tc(short_grade), text_halign=text.align_center)
  1009.  
  1010. table.cell(phidias_tbl, 0, 5, "LONG VETO:",
  1011. text_color=color.white, text_halign=text.align_left)
  1012. table.cell(phidias_tbl, 1, 5, long_pass ? "PASS" : long_veto,
  1013. bgcolor=long_pass ? color.new(color.green, 70) : color.new(color.red, 70),
  1014. text_halign=text.align_center)
  1015.  
  1016. table.cell(phidias_tbl, 0, 6, "SHORT VETO:",
  1017. text_color=color.white, text_halign=text.align_left)
  1018. table.cell(phidias_tbl, 1, 6, short_pass ? "PASS" : short_veto,
  1019. bgcolor=short_pass ? color.new(color.green, 70) : color.new(color.red, 70),
  1020. text_halign=text.align_center)
  1021.  
  1022. string fvg_status = bull_fvg_active or bear_fvg_active ? "[5M FVG ACTIVE]" : "[NO ACTIVE 5M FVG]"
  1023. table.cell(phidias_tbl, 0, 7, fvg_status,
  1024. text_color=color.gray, text_size=size.tiny, text_halign=text.align_center)
  1025. table.merge_cells(phidias_tbl, 0, 7, 1, 7)
RAW Gist Data Copied