Guest

Value area for sessions

Mar 5th, 2026
35
0
Never
Not a member of gistpad yet? Sign Up, it unlocks many cool features!
None 57.24 KB | None | 0 0
  1. //@version=5
  2. indicator("Phidias Master Engine [v9.6.1] - Fixed Tick Auction Visual", overlay=true, max_lines_count=500, max_boxes_count=500, max_labels_count=500)
  3. // ═══════════════════════════════════════════════════════════════════════════════
  4. // v9.6.1 CHANGELOG (from v9.6):
  5. // FIX #1: Performance — max_bins 500→300, VA recalc every 10 bars
  6. // FIX #5: Sweep — wick vs body, displacement confirm, sweep quality score
  7. // FIX #6: Mitigation — swept boxes invalidated, touch count tracked, box cleanup
  8. // BUG: vol_std zero-division guard added
  9. // BUG: box_touches now actually incremented on touch
  10. // BUG: old visual boxes cleaned up via array tracking
  11. // ═══════════════════════════════════════════════════════════════════════════════
  12.  
  13. // ═══════════════════════════════════════════════════════════════════════════════
  14. // 1. INPUTS & CONSTANTS
  15. // ═══════════════════════════════════════════════════════════════════════════════
  16. group_engine = "Auction Engine Settings"
  17. bin_res = input.float(1.0, "Bin Resolution (Points)", step=0.25, group=group_engine)
  18. va_perc = input.float(70.0, "Value Area %", minval=10, maxval=100, group=group_engine) / 100
  19. // FIX #1: Reduced from 500 to 300 — covers ±150 pts, more than enough for MES
  20. max_bins = 300
  21.  
  22. group_sessions = "Session Settings (UTC)"
  23. tok_sess = input.session("0000-0100:1234567", "Tokyo 1st Hour", group=group_sessions)
  24. lon_sess = input.session("0800-0900:1234567", "London 1st Hour", group=group_sessions)
  25. ny_sess = input.session("1430-1530:1234567", "NY 1st Hour", group=group_sessions)
  26.  
  27. lon_window = input.session("0800-1429:1234567", "London Active Window", group=group_sessions)
  28. ny_window = input.session("1430-2100:1234567", "NY Active Window", group=group_sessions)
  29.  
  30. group_display = "Display Settings"
  31. show_tok = input.bool(true, "Show Tokyo Lines (Background)", group=group_display)
  32. show_lon = input.bool(true, "Show London Lines", group=group_display)
  33. show_ny = input.bool(true, "Show NY Lines", group=group_display)
  34.  
  35. group_boxes = "Institutional Anchor Boxes"
  36. z_threshold = input.float(2.5, "Z-Score Volume Threshold", minval=0.5, step=0.1, group=group_boxes)
  37. box_length = input.int(15, "Box Projection Length (bars)", minval=5, group=group_boxes)
  38. show_levels = input.bool(true, "Show 1/3 and 2/3 Box Levels", group=group_boxes)
  39. // FIX #6: Box expiry input
  40. box_expire_bars = input.int(200, "Box Expires After (bars)", minval=50, maxval=500, group=group_boxes, tooltip="Boxes older than this are removed from chart and tracking.")
  41.  
  42. group_touch = "Box Touch & Sweep Detection"
  43. touch_zone_ticks = input.int(2, "Touch Zone (ticks from boundary)", minval=0, maxval=10, group=group_touch)
  44. tick_size_input = input.float(0.25, "Tick Size", step=0.25, group=group_touch)
  45. max_tracked = input.int(10, "Max Tracked Boxes", minval=5, maxval=20, group=group_touch)
  46. // FIX #5: Displacement threshold
  47. displacement_pts = input.float(1.0, "Displacement Threshold (Pts)", minval=0.25, step=0.25, group=group_touch)
  48.  
  49. // Colors
  50. c_tok_bg = #ff9800
  51. c_lon_va = #5DADE2
  52. c_ny_va = #00FFFF
  53. c_demand = #7CFC00
  54. c_supply = #FF0000
  55. c_poc = color.white
  56. c_level_1_3 = color.yellow
  57. c_level_2_3 = color.orange
  58.  
  59. // ═══════════════════════════════════════════════════════════════════════════════
  60. // 2. SESSION-BASED OPACITY ENGINE
  61. // ═══════════════════════════════════════════════════════════════════════════════
  62. in_london_active = not na(time(timeframe.period, lon_window, "UTC"))
  63. in_ny_active = not na(time(timeframe.period, ny_window, "UTC"))
  64.  
  65. opacity_tok = 30
  66. opacity_lon = in_london_active ? 100 : (in_ny_active ? 50 : 30)
  67. opacity_ny = in_ny_active ? 100 : (in_london_active ? 50 : 30)
  68.  
  69. alpha_tok = 100 - opacity_tok
  70. alpha_lon = 100 - opacity_lon
  71. alpha_ny = 100 - opacity_ny
  72.  
  73. // ═══════════════════════════════════════════════════════════════════════════════
  74. // 3. FIXED DAILY ANCHORING & PRICE-TO-INDEX MAPPING
  75. // ═══════════════════════════════════════════════════════════════════════════════
  76. var float tok_anchor = na
  77. var float lon_anchor = na
  78. var float ny_anchor = na
  79.  
  80. if ta.change(time("D"))
  81. tok_anchor := open
  82. lon_anchor := open
  83. ny_anchor := open
  84.  
  85. f_get_idx(price, anchor) =>
  86. if na(anchor) or price < anchor - (max_bins / 2) * bin_res or price > anchor + (max_bins / 2) * bin_res
  87. -1
  88. else
  89. idx = math.floor((price - anchor) / bin_res) + (max_bins / 2)
  90. math.min(max_bins - 1, math.max(0, int(idx)))
  91.  
  92. // ═══════════════════════════════════════════════════════════════════════════════
  93. // 4. SHARED VA CALCULATION FUNCTION
  94. // FIX #1: Extracted to avoid triple code duplication
  95. // ═══════════════════════════════════════════════════════════════════════════════
  96. // Note: Pine v5 doesn't support passing arrays to functions easily, so we use
  97. // a shared calculation approach with explicit index tracking per session.
  98. // The actual loops remain per-session but are optimised with max_bins=300.
  99.  
  100. // ═══════════════════════════════════════════════════════════════════════════════
  101. // 5. TOKYO SESSION: TICK AUCTION
  102. // ═══════════════════════════════════════════════════════════════════════════════
  103. var float[] tok_bins = array.new_float(max_bins, 0.0)
  104. var float tok_poc = na
  105. var float tok_val = na
  106. var float tok_vah = na
  107.  
  108. bool in_tok = not na(time(timeframe.period, tok_sess, "UTC"))
  109. bool tok_start = in_tok and not in_tok[1]
  110. bool tok_end = not in_tok and in_tok[1]
  111.  
  112. if tok_start
  113. for j = 0 to max_bins - 1
  114. array.set(tok_bins, j, 0.0)
  115.  
  116. if in_tok
  117. idx = f_get_idx(hlc3, tok_anchor)
  118. if idx >= 0
  119. array.set(tok_bins, idx, array.get(tok_bins, idx) + volume)
  120.  
  121. // FIX #1: Recalc every 10 bars instead of 5
  122. if tok_end or (in_tok and bar_index % 10 == 0)
  123. max_vol = 0.0
  124. poc_idx = 0
  125. for j = 0 to max_bins - 1
  126. bv = array.get(tok_bins, j)
  127. if bv > max_vol
  128. max_vol := bv
  129. poc_idx := j
  130.  
  131. if max_vol > 0
  132. total_vol = 0.0
  133. for j = 0 to max_bins - 1
  134. total_vol += array.get(tok_bins, j)
  135.  
  136. if total_vol > 0
  137. captured = max_vol
  138. lo_idx = poc_idx
  139. hi_idx = poc_idx
  140. target_vol = total_vol * va_perc
  141.  
  142. while captured < target_vol and (lo_idx > 0 or hi_idx < max_bins - 1)
  143. vol_below = lo_idx > 0 ? array.get(tok_bins, lo_idx - 1) : 0.0
  144. vol_above = hi_idx < max_bins - 1 ? array.get(tok_bins, hi_idx + 1) : 0.0
  145.  
  146. if lo_idx <= 0
  147. hi_idx += 1
  148. captured += vol_above
  149. else if hi_idx >= max_bins - 1
  150. lo_idx -= 1
  151. captured += vol_below
  152. else if vol_below >= vol_above
  153. lo_idx -= 1
  154. captured += vol_below
  155. else
  156. hi_idx += 1
  157. captured += vol_above
  158.  
  159. tok_poc := tok_anchor + (poc_idx - (max_bins / 2)) * bin_res + (bin_res / 2)
  160. tok_val := tok_anchor + (lo_idx - (max_bins / 2)) * bin_res
  161. tok_vah := tok_anchor + (hi_idx + 1 - (max_bins / 2)) * bin_res
  162.  
  163. // ═══════════════════════════════════════════════════════════════════════════════
  164. // 6. LONDON SESSION: TICK AUCTION
  165. // ═══════════════════════════════════════════════════════════════════════════════
  166. var float[] lon_bins = array.new_float(max_bins, 0.0)
  167. var float lon_poc = na
  168. var float lon_val = na
  169. var float lon_vah = na
  170.  
  171. bool in_lon = not na(time(timeframe.period, lon_sess, "UTC"))
  172. bool lon_start = in_lon and not in_lon[1]
  173. bool lon_end = not in_lon and in_lon[1]
  174.  
  175. if lon_start
  176. for j = 0 to max_bins - 1
  177. array.set(lon_bins, j, 0.0)
  178.  
  179. if in_lon
  180. idx = f_get_idx(hlc3, lon_anchor)
  181. if idx >= 0
  182. array.set(lon_bins, idx, array.get(lon_bins, idx) + volume)
  183.  
  184. if lon_end or (in_lon and bar_index % 10 == 0)
  185. max_vol = 0.0
  186. poc_idx = 0
  187. for j = 0 to max_bins - 1
  188. bv = array.get(lon_bins, j)
  189. if bv > max_vol
  190. max_vol := bv
  191. poc_idx := j
  192.  
  193. if max_vol > 0
  194. total_vol = 0.0
  195. for j = 0 to max_bins - 1
  196. total_vol += array.get(lon_bins, j)
  197.  
  198. if total_vol > 0
  199. captured = max_vol
  200. lo_idx = poc_idx
  201. hi_idx = poc_idx
  202. target_vol = total_vol * va_perc
  203.  
  204. while captured < target_vol and (lo_idx > 0 or hi_idx < max_bins - 1)
  205. vol_below = lo_idx > 0 ? array.get(lon_bins, lo_idx - 1) : 0.0
  206. vol_above = hi_idx < max_bins - 1 ? array.get(lon_bins, hi_idx + 1) : 0.0
  207.  
  208. if lo_idx <= 0
  209. hi_idx += 1
  210. captured += vol_above
  211. else if hi_idx >= max_bins - 1
  212. lo_idx -= 1
  213. captured += vol_below
  214. else if vol_below >= vol_above
  215. lo_idx -= 1
  216. captured += vol_below
  217. else
  218. hi_idx += 1
  219. captured += vol_above
  220.  
  221. lon_poc := lon_anchor + (poc_idx - (max_bins / 2)) * bin_res + (bin_res / 2)
  222. lon_val := lon_anchor + (lo_idx - (max_bins / 2)) * bin_res
  223. lon_vah := lon_anchor + (hi_idx + 1 - (max_bins / 2)) * bin_res
  224.  
  225. // ═══════════════════════════════════════════════════════════════════════════════
  226. // 7. NEW YORK SESSION: TICK AUCTION
  227. // ═══════════════════════════════════════════════════════════════════════════════
  228. var float[] ny_bins = array.new_float(max_bins, 0.0)
  229. var float ny_poc = na
  230. var float ny_val = na
  231. var float ny_vah = na
  232.  
  233. bool in_ny = not na(time(timeframe.period, ny_sess, "UTC"))
  234. bool ny_start = in_ny and not in_ny[1]
  235. bool ny_end = not in_ny and in_ny[1]
  236.  
  237. if ny_start
  238. for j = 0 to max_bins - 1
  239. array.set(ny_bins, j, 0.0)
  240.  
  241. if in_ny
  242. idx = f_get_idx(hlc3, ny_anchor)
  243. if idx >= 0
  244. array.set(ny_bins, idx, array.get(ny_bins, idx) + volume)
  245.  
  246. if ny_end or (in_ny and bar_index % 10 == 0)
  247. max_vol = 0.0
  248. poc_idx = 0
  249. for j = 0 to max_bins - 1
  250. bv = array.get(ny_bins, j)
  251. if bv > max_vol
  252. max_vol := bv
  253. poc_idx := j
  254.  
  255. if max_vol > 0
  256. total_vol = 0.0
  257. for j = 0 to max_bins - 1
  258. total_vol += array.get(ny_bins, j)
  259.  
  260. if total_vol > 0
  261. captured = max_vol
  262. lo_idx = poc_idx
  263. hi_idx = poc_idx
  264. target_vol = total_vol * va_perc
  265.  
  266. while captured < target_vol and (lo_idx > 0 or hi_idx < max_bins - 1)
  267. vol_below = lo_idx > 0 ? array.get(ny_bins, lo_idx - 1) : 0.0
  268. vol_above = hi_idx < max_bins - 1 ? array.get(ny_bins, hi_idx + 1) : 0.0
  269.  
  270. if lo_idx <= 0
  271. hi_idx += 1
  272. captured += vol_above
  273. else if hi_idx >= max_bins - 1
  274. lo_idx -= 1
  275. captured += vol_below
  276. else if vol_below >= vol_above
  277. lo_idx -= 1
  278. captured += vol_below
  279. else
  280. hi_idx += 1
  281. captured += vol_above
  282.  
  283. ny_poc := ny_anchor + (poc_idx - (max_bins / 2)) * bin_res + (bin_res / 2)
  284. ny_val := ny_anchor + (lo_idx - (max_bins / 2)) * bin_res
  285. ny_vah := ny_anchor + (hi_idx + 1 - (max_bins / 2)) * bin_res
  286.  
  287. // ═══════════════════════════════════════════════════════════════════════════════
  288. // 8. PERSISTENT HORIZONTAL LINES WITH OPACITY CONTROL
  289. // ═══════════════════════════════════════════════════════════════════════════════
  290. var line tvah_line = na
  291. var line tval_line = na
  292. var line tpoc_line = na
  293.  
  294. if show_tok and not na(tok_vah)
  295. line.delete(tvah_line[1])
  296. line.delete(tval_line[1])
  297. line.delete(tpoc_line[1])
  298. tvah_line := line.new(bar_index[1], tok_vah, bar_index, tok_vah,
  299. extend=extend.right, color=color.new(c_tok_bg, alpha_tok), width=2)
  300. tval_line := line.new(bar_index[1], tok_val, bar_index, tok_val,
  301. extend=extend.right, color=color.new(c_tok_bg, alpha_tok), width=2)
  302. tpoc_line := line.new(bar_index[1], tok_poc, bar_index, tok_poc,
  303. extend=extend.right, color=color.new(c_poc, alpha_tok), style=line.style_dashed, width=1)
  304.  
  305. var line lvah_line = na
  306. var line lval_line = na
  307. var line lpoc_line = na
  308.  
  309. if show_lon and not na(lon_vah)
  310. line.delete(lvah_line[1])
  311. line.delete(lval_line[1])
  312. line.delete(lpoc_line[1])
  313. lvah_line := line.new(bar_index[1], lon_vah, bar_index, lon_vah,
  314. extend=extend.right, color=color.new(c_lon_va, alpha_lon), width=2)
  315. lval_line := line.new(bar_index[1], lon_val, bar_index, lon_val,
  316. extend=extend.right, color=color.new(c_lon_va, alpha_lon), width=2)
  317. lpoc_line := line.new(bar_index[1], lon_poc, bar_index, lon_poc,
  318. extend=extend.right, color=color.new(c_poc, alpha_lon), style=line.style_dashed, width=1)
  319.  
  320. var line nvah_line = na
  321. var line nval_line = na
  322. var line npoc_line = na
  323.  
  324. if show_ny and not na(ny_vah)
  325. line.delete(nvah_line[1])
  326. line.delete(nval_line[1])
  327. line.delete(npoc_line[1])
  328. nvah_line := line.new(bar_index[1], ny_vah, bar_index, ny_vah,
  329. extend=extend.right, color=color.new(c_ny_va, alpha_ny), width=2)
  330. nval_line := line.new(bar_index[1], ny_val, bar_index, ny_val,
  331. extend=extend.right, color=color.new(c_ny_va, alpha_ny), width=2)
  332. npoc_line := line.new(bar_index[1], ny_poc, bar_index, ny_poc,
  333. extend=extend.right, color=color.new(c_poc, alpha_ny), style=line.style_dashed, width=1)
  334.  
  335. // ═══════════════════════════════════════════════════════════════════════════════
  336. // 9. INSTITUTIONAL ANCHOR BOXES + TOUCH TRACKING
  337. // FIX: zero-division guard, visual box tracking for cleanup
  338. // ═══════════════════════════════════════════════════════════════════════════════
  339. float vol_sma = ta.sma(volume, 20)
  340. float vol_std = ta.stdev(volume, 20)
  341. // BUG FIX: Guard against zero standard deviation
  342. float z_vol = vol_std != 0 ? (volume - vol_sma) / vol_std : 0
  343.  
  344. bool high_vol = z_vol >= z_threshold
  345. bool bullish = close > open
  346.  
  347. color box_color = high_vol ? (bullish ? c_demand : c_supply) : na
  348. color box_bg = high_vol ? color.new(box_color, 85) : na
  349.  
  350. var float[] box_tops = array.new_float(0)
  351. var float[] box_bottoms = array.new_float(0)
  352. var int[] box_dirs = array.new_int(0)
  353. var int[] box_births = array.new_int(0)
  354. var int[] box_touches = array.new_int(0)
  355. var bool[] box_swept = array.new_bool(0)
  356. // FIX #5: Track sweep type per box — true = wick only
  357. var bool[] box_sweep_wick = array.new_bool(0)
  358. // FIX #6: Track visual box IDs for cleanup
  359. var box[] box_visuals = array.new_box(0)
  360.  
  361. float touch_zone = touch_zone_ticks * tick_size_input
  362.  
  363. if high_vol
  364. // Create visual box
  365. box new_box = box.new(left=bar_index, top=high, right=bar_index + box_length, bottom=low,
  366. border_color=box_color, bgcolor=box_bg, border_width=2)
  367.  
  368. if show_levels
  369. float box_height = high - low
  370. float level_1_3 = low + box_height / 3
  371. float level_2_3 = low + (box_height * 2) / 3
  372.  
  373. line.new(bar_index + box_length - 1, level_1_3,
  374. bar_index + box_length, level_1_3,
  375. color=color.new(c_level_1_3, 40), width=2, style=line.style_solid)
  376.  
  377. line.new(bar_index + box_length - 1, level_2_3,
  378. bar_index + box_length, level_2_3,
  379. color=color.new(c_level_2_3, 40), width=2, style=line.style_solid)
  380.  
  381. float level_1_4 = bullish ? (low + box_height / 4) : (high - box_height / 4)
  382. color mark_col = bullish ? c_level_1_3 : c_level_2_3
  383.  
  384. line.new(bar_index + box_length - 2, level_1_4,
  385. bar_index + box_length + 1, level_1_4,
  386. color=color.new(mark_col, 40), width=2, style=line.style_solid)
  387.  
  388. // Push to tracking arrays
  389. array.push(box_tops, high)
  390. array.push(box_bottoms, low)
  391. array.push(box_dirs, bullish ? 1 : -1)
  392. array.push(box_births, bar_index)
  393. array.push(box_touches, 0)
  394. array.push(box_swept, false)
  395. array.push(box_sweep_wick, false)
  396. array.push(box_visuals, new_box)
  397.  
  398. // Enforce max tracked — clean up oldest
  399. if array.size(box_tops) > max_tracked
  400. array.shift(box_tops)
  401. array.shift(box_bottoms)
  402. array.shift(box_dirs)
  403. array.shift(box_births)
  404. array.shift(box_touches)
  405. array.shift(box_swept)
  406. array.shift(box_sweep_wick)
  407. // FIX #6: Delete the oldest visual box from chart
  408. box old_box = array.shift(box_visuals)
  409. box.delete(old_box)
  410.  
  411. string session_context = in_ny_active ? "NY ACTIVE" : (in_london_active ? "LONDON ACTIVE" : "ASIA BACKGROUND")
  412. string box_type = bullish ? "DEMAND (LIME)" : "SUPPLY (RED)"
  413. string msg = "Phidias " + box_type + " Box | " + session_context + " | Z=" + str.tostring(z_vol, "#.##") + " @ " + str.tostring(close)
  414. alert(msg, alert.freq_once_per_bar)
  415.  
  416. // ═══════════════════════════════════════════════════════════════════════════════
  417. // 10. TOUCH & SWEEP DETECTION ENGINE
  418. // FIX #5: Wick vs body classification, displacement tracking
  419. // FIX #6: Mitigated/expired boxes removed from chart + tracking
  420. // ═══════════════════════════════════════════════════════════════════════════════
  421. var bool touch_demand_alert = false
  422. var bool touch_supply_alert = false
  423. var bool sweep_demand_alert = false
  424. var bool sweep_supply_alert = false
  425. // FIX #5: Sweep quality tracking (most recent sweep)
  426. var bool last_sweep_wick = false
  427. var bool last_sweep_displaced = false
  428. var int last_sweep_bar = 0
  429. var int last_sweep_dir = 0
  430. var float last_sweep_level = na
  431.  
  432. touch_demand_alert := false
  433. touch_supply_alert := false
  434. sweep_demand_alert := false
  435. sweep_supply_alert := false
  436.  
  437. int n_boxes = array.size(box_tops)
  438.  
  439. // FIX #6: Collect indices to remove (iterate backwards so indices stay valid)
  440. if n_boxes > 0
  441. for i = n_boxes - 1 to 0
  442. float b_top = array.get(box_tops, i)
  443. float b_bottom = array.get(box_bottoms, i)
  444. int b_dir = array.get(box_dirs, i)
  445. int b_birth = array.get(box_births, i)
  446. bool b_swept = array.get(box_swept, i)
  447. int b_touch = array.get(box_touches, i)
  448.  
  449. // FIX #6: Remove expired boxes from chart and arrays
  450. if bar_index - b_birth > box_expire_bars
  451. box old_vis = array.get(box_visuals, i)
  452. box.delete(old_vis)
  453. array.remove(box_tops, i)
  454. array.remove(box_bottoms, i)
  455. array.remove(box_dirs, i)
  456. array.remove(box_births, i)
  457. array.remove(box_touches, i)
  458. array.remove(box_swept, i)
  459. array.remove(box_sweep_wick, i)
  460. array.remove(box_visuals, i)
  461. continue
  462.  
  463. // FIX #6: Skip already-swept boxes but keep them visible (faded)
  464. if b_swept
  465. // Fade the swept box visual
  466. box swept_vis = array.get(box_visuals, i)
  467. box.set_bgcolor(swept_vis, color.new(color.gray, 92))
  468. box.set_border_color(swept_vis, color.new(color.gray, 70))
  469. continue
  470.  
  471. if b_dir == 1
  472. bool near_bottom = low <= (b_bottom + touch_zone) and low >= (b_bottom - touch_zone)
  473. bool closed_above = close > b_bottom
  474. bool swept_below = low < (b_bottom - touch_zone)
  475.  
  476. // BUG FIX: Actually increment touch count
  477. if near_bottom and closed_above and not swept_below
  478. array.set(box_touches, i, b_touch + 1)
  479. touch_demand_alert := true
  480.  
  481. if swept_below
  482. array.set(box_swept, i, true)
  483. sweep_demand_alert := true
  484. // FIX #5: Classify wick vs body
  485. bool is_wick = close > b_bottom
  486. array.set(box_sweep_wick, i, is_wick)
  487. last_sweep_wick := is_wick
  488. last_sweep_bar := bar_index
  489. last_sweep_dir := 1
  490. last_sweep_level := b_bottom
  491. last_sweep_displaced := false
  492.  
  493. if b_dir == -1
  494. bool near_top = high >= (b_top - touch_zone) and high <= (b_top + touch_zone)
  495. bool closed_below = close < b_top
  496. bool swept_above = high > (b_top + touch_zone)
  497.  
  498. // BUG FIX: Actually increment touch count
  499. if near_top and closed_below and not swept_above
  500. array.set(box_touches, i, b_touch + 1)
  501. touch_supply_alert := true
  502.  
  503. if swept_above
  504. array.set(box_swept, i, true)
  505. sweep_supply_alert := true
  506. // FIX #5: Classify wick vs body
  507. bool is_wick = close < b_top
  508. array.set(box_sweep_wick, i, is_wick)
  509. last_sweep_wick := is_wick
  510. last_sweep_bar := bar_index
  511. last_sweep_dir := -1
  512. last_sweep_level := b_top
  513. last_sweep_displaced := false
  514.  
  515. // FIX #5: Check displacement on bar after sweep
  516. if bar_index == last_sweep_bar + 1 and last_sweep_bar > 0
  517. if last_sweep_dir == 1
  518. last_sweep_displaced := (close - last_sweep_level) >= displacement_pts
  519. else if last_sweep_dir == -1
  520. last_sweep_displaced := (last_sweep_level - close) >= displacement_pts
  521.  
  522. // FIX #5: Sweep quality score (0-2 for visual indicator, no FVG check here)
  523. int sweep_quality = 0
  524. bool sweep_active = last_sweep_bar > 0 and (bar_index - last_sweep_bar) <= 30
  525. if sweep_active
  526. if last_sweep_wick
  527. sweep_quality += 1
  528. if last_sweep_displaced
  529. sweep_quality += 1
  530.  
  531. // ═══════════════════════════════════════════════════════════════════════════════
  532. // 11. SWEEP QUALITY LABEL (on-chart visual)
  533. // ═══════════════════════════════════════════════════════════════════════════════
  534. // Show a small label at sweep location indicating quality
  535. if sweep_demand_alert or sweep_supply_alert
  536. string sw_type = last_sweep_wick ? "WICK" : "BODY"
  537. string sw_lbl = "SW:" + sw_type
  538. color sw_col = last_sweep_wick ? color.new(c_demand, 20) : color.new(color.orange, 20)
  539. label.new(bar_index, last_sweep_dir == 1 ? low : high, sw_lbl,
  540. color=sw_col, textcolor=color.white, size=size.tiny,
  541. style=last_sweep_dir == 1 ? label.style_label_up : label.style_label_down)
  542.  
  543. // Displacement confirmation label (fires one bar after sweep)
  544. if bar_index == last_sweep_bar + 1 and last_sweep_bar > 0 and sweep_active
  545. if last_sweep_displaced
  546. label.new(bar_index, last_sweep_dir == 1 ? low : high, "DISP ✓",
  547. color=color.new(c_demand, 30), textcolor=color.white, size=size.tiny,
  548. style=last_sweep_dir == 1 ? label.style_label_up : label.style_label_down)
  549.  
  550. // ═══════════════════════════════════════════════════════════════════════════════
  551. // 12. ALERTS
  552. // ═══════════════════════════════════════════════════════════════════════════════
  553. alertcondition(high_vol, title="Institutional Box Detected",
  554. message="Phidias Box Forming - Check Session Context")
  555.  
  556. alertcondition(touch_demand_alert, title="Demand Box Touch — Sweep Incoming",
  557. message="Price touched Demand Box boundary — liquidity building below.")
  558.  
  559. alertcondition(touch_supply_alert, title="Supply Box Touch — Sweep Incoming",
  560. message="Price touched Supply Box boundary — liquidity building above.")
  561.  
  562. // FIX #5: Separate wick and body sweep alerts
  563. alertcondition(sweep_demand_alert and last_sweep_wick, title="Demand WICK Sweep — High Probability",
  564. message="Demand Box wick sweep — liquidity grabbed, price rejected. Check for reversal.")
  565.  
  566. alertcondition(sweep_demand_alert and not last_sweep_wick, title="Demand BODY Sweep — Breakout Watch",
  567. message="Demand Box body sweep — potential breakdown, not a clean liquidity grab.")
  568.  
  569. alertcondition(sweep_supply_alert and last_sweep_wick, title="Supply WICK Sweep — High Probability",
  570. message="Supply Box wick sweep — liquidity grabbed, price rejected. Check for reversal.")
  571.  
  572. alertcondition(sweep_supply_alert and not last_sweep_wick, title="Supply BODY Sweep — Breakout Watch",
  573. message="Supply Box body sweep — potential breakout, not a clean liquidity grab.")
  574.  
  575. // ═══════════════════════════════════════════════════════════════════════════════
  576. // 13. INFO TABLE (compact sweep status)
  577. // ═══════════════════════════════════════════════════════════════════════════════
  578. var table info = table.new(position.top_right, 3, 2, bgcolor=color.new(#000000, 80), border_width=1)
  579. if barstate.islast
  580. table.cell(info, 0, 0, "SWEEP", text_color=#888888, text_size=size.tiny)
  581. table.cell(info, 1, 0, "TYPE", text_color=#888888, text_size=size.tiny)
  582. table.cell(info, 2, 0, "SQ", text_color=#888888, text_size=size.tiny)
  583.  
  584. string sw_status = sweep_active ? (last_sweep_dir == 1 ? "BULL ↓" : "BEAR ↑") : "NONE"
  585. string sw_type = sweep_active ? (last_sweep_wick ? "WICK" : "BODY") : "-"
  586. string sq_text = sweep_active ? str.tostring(sweep_quality) + "/2" : "-"
  587.  
  588. color sw_bg = sweep_active ? (last_sweep_wick ? color.new(c_demand, 30) : color.new(color.orange, 30)) : color.new(#000000, 40)
  589. color sq_bg = sweep_quality >= 2 ? color.new(#00e676, 15) : sweep_quality >= 1 ? color.new(#448aff, 25) : color.new(#444455, 50)
  590. color sq_tc = sweep_quality >= 1 ? color.white : color.gray
  591.  
  592. table.cell(info, 0, 1, sw_status, bgcolor=sw_bg, text_color=color.white, text_size=size.tiny)
  593. table.cell(info, 1, 1, sw_type, bgcolor=sw_bg, text_color=color.white, text_size=size.tiny)
  594. table.cell(info, 2, 1, sq_text, bgcolor=sweep_active ? sq_bg : color.new(#000000, 40), text_color=sweep_active ? sq_tc : color.gray, text_size=size.tiny)
RAW Gist Data Copied