//@version=5
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)
// ═══════════════════════════════════════════════════════════════════════════════
// v9.6.1 CHANGELOG (from v9.6):
// FIX #1: Performance — max_bins 500→300, VA recalc every 10 bars
// FIX #5: Sweep — wick vs body, displacement confirm, sweep quality score
// FIX #6: Mitigation — swept boxes invalidated, touch count tracked, box cleanup
// BUG: vol_std zero-division guard added
// BUG: box_touches now actually incremented on touch
// BUG: old visual boxes cleaned up via array tracking
// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════
// 1. INPUTS & CONSTANTS
// ═══════════════════════════════════════════════════════════════════════════════
group_engine = "Auction Engine Settings"
bin_res = input.float(1.0, "Bin Resolution (Points)", step=0.25, group=group_engine)
va_perc = input.float(70.0, "Value Area %", minval=10, maxval=100, group=group_engine) / 100
// FIX #1: Reduced from 500 to 300 — covers ±150 pts, more than enough for MES
max_bins = 300
group_sessions = "Session Settings (UTC)"
tok_sess = input.session("0000-0100:1234567", "Tokyo 1st Hour", group=group_sessions)
lon_sess = input.session("0800-0900:1234567", "London 1st Hour", group=group_sessions)
ny_sess = input.session("1430-1530:1234567", "NY 1st Hour", group=group_sessions)
lon_window = input.session("0800-1429:1234567", "London Active Window", group=group_sessions)
ny_window = input.session("1430-2100:1234567", "NY Active Window", group=group_sessions)
group_display = "Display Settings"
show_tok = input.bool(true, "Show Tokyo Lines (Background)", group=group_display)
show_lon = input.bool(true, "Show London Lines", group=group_display)
show_ny = input.bool(true, "Show NY Lines", group=group_display)
group_boxes = "Institutional Anchor Boxes"
z_threshold = input.float(2.5, "Z-Score Volume Threshold", minval=0.5, step=0.1, group=group_boxes)
box_length = input.int(15, "Box Projection Length (bars)", minval=5, group=group_boxes)
show_levels = input.bool(true, "Show 1/3 and 2/3 Box Levels", group=group_boxes)
// FIX #6: Box expiry input
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.")
group_touch = "Box Touch & Sweep Detection"
touch_zone_ticks = input.int(2, "Touch Zone (ticks from boundary)", minval=0, maxval=10, group=group_touch)
tick_size_input = input.float(0.25, "Tick Size", step=0.25, group=group_touch)
max_tracked = input.int(10, "Max Tracked Boxes", minval=5, maxval=20, group=group_touch)
// FIX #5: Displacement threshold
displacement_pts = input.float(1.0, "Displacement Threshold (Pts)", minval=0.25, step=0.25, group=group_touch)
// Colors
c_tok_bg = #ff9800
c_lon_va = #5DADE2
c_ny_va = #00FFFF
c_demand = #7CFC00
c_supply = #FF0000
c_poc = color.white
c_level_1_3 = color.yellow
c_level_2_3 = color.orange
// ═══════════════════════════════════════════════════════════════════════════════
// 2. SESSION-BASED OPACITY ENGINE
// ═══════════════════════════════════════════════════════════════════════════════
in_london_active = not na(time(timeframe.period, lon_window, "UTC"))
in_ny_active = not na(time(timeframe.period, ny_window, "UTC"))
opacity_tok = 30
opacity_lon = in_london_active ? 100 : (in_ny_active ? 50 : 30)
opacity_ny = in_ny_active ? 100 : (in_london_active ? 50 : 30)
alpha_tok = 100 - opacity_tok
alpha_lon = 100 - opacity_lon
alpha_ny = 100 - opacity_ny
// ═══════════════════════════════════════════════════════════════════════════════
// 3. FIXED DAILY ANCHORING & PRICE-TO-INDEX MAPPING
// ═══════════════════════════════════════════════════════════════════════════════
var float tok_anchor = na
var float lon_anchor = na
var float ny_anchor = na
if ta.change(time("D"))
tok_anchor := open
lon_anchor := open
ny_anchor := open
f_get_idx(price, anchor) =>
if na(anchor) or price < anchor - (max_bins / 2) * bin_res or price > anchor + (max_bins / 2) * bin_res
-1
else
idx = math.floor((price - anchor) / bin_res) + (max_bins / 2)
math.min(max_bins - 1, math.max(0, int(idx)))
// ═══════════════════════════════════════════════════════════════════════════════
// 4. SHARED VA CALCULATION FUNCTION
// FIX #1: Extracted to avoid triple code duplication
// ═══════════════════════════════════════════════════════════════════════════════
// Note: Pine v5 doesn't support passing arrays to functions easily, so we use
// a shared calculation approach with explicit index tracking per session.
// The actual loops remain per-session but are optimised with max_bins=300.
// ═══════════════════════════════════════════════════════════════════════════════
// 5. TOKYO SESSION: TICK AUCTION
// ═══════════════════════════════════════════════════════════════════════════════
var float[] tok_bins = array.new_float(max_bins, 0.0)
var float tok_poc = na
var float tok_val = na
var float tok_vah = na
bool in_tok = not na(time(timeframe.period, tok_sess, "UTC"))
bool tok_start = in_tok and not in_tok[1]
bool tok_end = not in_tok and in_tok[1]
if tok_start
for j = 0 to max_bins - 1
array.set(tok_bins, j, 0.0)
if in_tok
idx = f_get_idx(hlc3, tok_anchor)
if idx >= 0
array.set(tok_bins, idx, array.get(tok_bins, idx) + volume)
// FIX #1: Recalc every 10 bars instead of 5
if tok_end or (in_tok and bar_index % 10 == 0)
max_vol = 0.0
poc_idx = 0
for j = 0 to max_bins - 1
bv = array.get(tok_bins, j)
if bv > max_vol
max_vol := bv
poc_idx := j
if max_vol > 0
total_vol = 0.0
for j = 0 to max_bins - 1
total_vol += array.get(tok_bins, j)
if total_vol > 0
captured = max_vol
lo_idx = poc_idx
hi_idx = poc_idx
target_vol = total_vol * va_perc
while captured < target_vol and (lo_idx > 0 or hi_idx < max_bins - 1)
vol_below = lo_idx > 0 ? array.get(tok_bins, lo_idx - 1) : 0.0
vol_above = hi_idx < max_bins - 1 ? array.get(tok_bins, hi_idx + 1) : 0.0
if lo_idx <= 0
hi_idx += 1
captured += vol_above
else if hi_idx >= max_bins - 1
lo_idx -= 1
captured += vol_below
else if vol_below >= vol_above
lo_idx -= 1
captured += vol_below
else
hi_idx += 1
captured += vol_above
tok_poc := tok_anchor + (poc_idx - (max_bins / 2)) * bin_res + (bin_res / 2)
tok_val := tok_anchor + (lo_idx - (max_bins / 2)) * bin_res
tok_vah := tok_anchor + (hi_idx + 1 - (max_bins / 2)) * bin_res
// ═══════════════════════════════════════════════════════════════════════════════
// 6. LONDON SESSION: TICK AUCTION
// ═══════════════════════════════════════════════════════════════════════════════
var float[] lon_bins = array.new_float(max_bins, 0.0)
var float lon_poc = na
var float lon_val = na
var float lon_vah = na
bool in_lon = not na(time(timeframe.period, lon_sess, "UTC"))
bool lon_start = in_lon and not in_lon[1]
bool lon_end = not in_lon and in_lon[1]
if lon_start
for j = 0 to max_bins - 1
array.set(lon_bins, j, 0.0)
if in_lon
idx = f_get_idx(hlc3, lon_anchor)
if idx >= 0
array.set(lon_bins, idx, array.get(lon_bins, idx) + volume)
if lon_end or (in_lon and bar_index % 10 == 0)
max_vol = 0.0
poc_idx = 0
for j = 0 to max_bins - 1
bv = array.get(lon_bins, j)
if bv > max_vol
max_vol := bv
poc_idx := j
if max_vol > 0
total_vol = 0.0
for j = 0 to max_bins - 1
total_vol += array.get(lon_bins, j)
if total_vol > 0
captured = max_vol
lo_idx = poc_idx
hi_idx = poc_idx
target_vol = total_vol * va_perc
while captured < target_vol and (lo_idx > 0 or hi_idx < max_bins - 1)
vol_below = lo_idx > 0 ? array.get(lon_bins, lo_idx - 1) : 0.0
vol_above = hi_idx < max_bins - 1 ? array.get(lon_bins, hi_idx + 1) : 0.0
if lo_idx <= 0
hi_idx += 1
captured += vol_above
else if hi_idx >= max_bins - 1
lo_idx -= 1
captured += vol_below
else if vol_below >= vol_above
lo_idx -= 1
captured += vol_below
else
hi_idx += 1
captured += vol_above
lon_poc := lon_anchor + (poc_idx - (max_bins / 2)) * bin_res + (bin_res / 2)
lon_val := lon_anchor + (lo_idx - (max_bins / 2)) * bin_res
lon_vah := lon_anchor + (hi_idx + 1 - (max_bins / 2)) * bin_res
// ═══════════════════════════════════════════════════════════════════════════════
// 7. NEW YORK SESSION: TICK AUCTION
// ═══════════════════════════════════════════════════════════════════════════════
var float[] ny_bins = array.new_float(max_bins, 0.0)
var float ny_poc = na
var float ny_val = na
var float ny_vah = na
bool in_ny = not na(time(timeframe.period, ny_sess, "UTC"))
bool ny_start = in_ny and not in_ny[1]
bool ny_end = not in_ny and in_ny[1]
if ny_start
for j = 0 to max_bins - 1
array.set(ny_bins, j, 0.0)
if in_ny
idx = f_get_idx(hlc3, ny_anchor)
if idx >= 0
array.set(ny_bins, idx, array.get(ny_bins, idx) + volume)
if ny_end or (in_ny and bar_index % 10 == 0)
max_vol = 0.0
poc_idx = 0
for j = 0 to max_bins - 1
bv = array.get(ny_bins, j)
if bv > max_vol
max_vol := bv
poc_idx := j
if max_vol > 0
total_vol = 0.0
for j = 0 to max_bins - 1
total_vol += array.get(ny_bins, j)
if total_vol > 0
captured = max_vol
lo_idx = poc_idx
hi_idx = poc_idx
target_vol = total_vol * va_perc
while captured < target_vol and (lo_idx > 0 or hi_idx < max_bins - 1)
vol_below = lo_idx > 0 ? array.get(ny_bins, lo_idx - 1) : 0.0
vol_above = hi_idx < max_bins - 1 ? array.get(ny_bins, hi_idx + 1) : 0.0
if lo_idx <= 0
hi_idx += 1
captured += vol_above
else if hi_idx >= max_bins - 1
lo_idx -= 1
captured += vol_below
else if vol_below >= vol_above
lo_idx -= 1
captured += vol_below
else
hi_idx += 1
captured += vol_above
ny_poc := ny_anchor + (poc_idx - (max_bins / 2)) * bin_res + (bin_res / 2)
ny_val := ny_anchor + (lo_idx - (max_bins / 2)) * bin_res
ny_vah := ny_anchor + (hi_idx + 1 - (max_bins / 2)) * bin_res
// ═══════════════════════════════════════════════════════════════════════════════
// 8. PERSISTENT HORIZONTAL LINES WITH OPACITY CONTROL
// ═══════════════════════════════════════════════════════════════════════════════
var line tvah_line = na
var line tval_line = na
var line tpoc_line = na
if show_tok and not na(tok_vah)
line.delete(tvah_line[1])
line.delete(tval_line[1])
line.delete(tpoc_line[1])
tvah_line := line.new(bar_index[1], tok_vah, bar_index, tok_vah,
extend=extend.right, color=color.new(c_tok_bg, alpha_tok), width=2)
tval_line := line.new(bar_index[1], tok_val, bar_index, tok_val,
extend=extend.right, color=color.new(c_tok_bg, alpha_tok), width=2)
tpoc_line := line.new(bar_index[1], tok_poc, bar_index, tok_poc,
extend=extend.right, color=color.new(c_poc, alpha_tok), style=line.style_dashed, width=1)
var line lvah_line = na
var line lval_line = na
var line lpoc_line = na
if show_lon and not na(lon_vah)
line.delete(lvah_line[1])
line.delete(lval_line[1])
line.delete(lpoc_line[1])
lvah_line := line.new(bar_index[1], lon_vah, bar_index, lon_vah,
extend=extend.right, color=color.new(c_lon_va, alpha_lon), width=2)
lval_line := line.new(bar_index[1], lon_val, bar_index, lon_val,
extend=extend.right, color=color.new(c_lon_va, alpha_lon), width=2)
lpoc_line := line.new(bar_index[1], lon_poc, bar_index, lon_poc,
extend=extend.right, color=color.new(c_poc, alpha_lon), style=line.style_dashed, width=1)
var line nvah_line = na
var line nval_line = na
var line npoc_line = na
if show_ny and not na(ny_vah)
line.delete(nvah_line[1])
line.delete(nval_line[1])
line.delete(npoc_line[1])
nvah_line := line.new(bar_index[1], ny_vah, bar_index, ny_vah,
extend=extend.right, color=color.new(c_ny_va, alpha_ny), width=2)
nval_line := line.new(bar_index[1], ny_val, bar_index, ny_val,
extend=extend.right, color=color.new(c_ny_va, alpha_ny), width=2)
npoc_line := line.new(bar_index[1], ny_poc, bar_index, ny_poc,
extend=extend.right, color=color.new(c_poc, alpha_ny), style=line.style_dashed, width=1)
// ═══════════════════════════════════════════════════════════════════════════════
// 9. INSTITUTIONAL ANCHOR BOXES + TOUCH TRACKING
// FIX: zero-division guard, visual box tracking for cleanup
// ═══════════════════════════════════════════════════════════════════════════════
float vol_sma = ta.sma(volume, 20)
float vol_std = ta.stdev(volume, 20)
// BUG FIX: Guard against zero standard deviation
float z_vol = vol_std != 0 ? (volume - vol_sma) / vol_std : 0
bool high_vol = z_vol >= z_threshold
bool bullish = close > open
color box_color = high_vol ? (bullish ? c_demand : c_supply) : na
color box_bg = high_vol ? color.new(box_color, 85) : na
var float[] box_tops = array.new_float(0)
var float[] box_bottoms = array.new_float(0)
var int[] box_dirs = array.new_int(0)
var int[] box_births = array.new_int(0)
var int[] box_touches = array.new_int(0)
var bool[] box_swept = array.new_bool(0)
// FIX #5: Track sweep type per box — true = wick only
var bool[] box_sweep_wick = array.new_bool(0)
// FIX #6: Track visual box IDs for cleanup
var box[] box_visuals = array.new_box(0)
float touch_zone = touch_zone_ticks * tick_size_input
if high_vol
// Create visual box
box new_box = box.new(left=bar_index, top=high, right=bar_index + box_length, bottom=low,
border_color=box_color, bgcolor=box_bg, border_width=2)
if show_levels
float box_height = high - low
float level_1_3 = low + box_height / 3
float level_2_3 = low + (box_height * 2) / 3
line.new(bar_index + box_length - 1, level_1_3,
bar_index + box_length, level_1_3,
color=color.new(c_level_1_3, 40), width=2, style=line.style_solid)
line.new(bar_index + box_length - 1, level_2_3,
bar_index + box_length, level_2_3,
color=color.new(c_level_2_3, 40), width=2, style=line.style_solid)
float level_1_4 = bullish ? (low + box_height / 4) : (high - box_height / 4)
color mark_col = bullish ? c_level_1_3 : c_level_2_3
line.new(bar_index + box_length - 2, level_1_4,
bar_index + box_length + 1, level_1_4,
color=color.new(mark_col, 40), width=2, style=line.style_solid)
// Push to tracking arrays
array.push(box_tops, high)
array.push(box_bottoms, low)
array.push(box_dirs, bullish ? 1 : -1)
array.push(box_births, bar_index)
array.push(box_touches, 0)
array.push(box_swept, false)
array.push(box_sweep_wick, false)
array.push(box_visuals, new_box)
// Enforce max tracked — clean up oldest
if array.size(box_tops) > max_tracked
array.shift(box_tops)
array.shift(box_bottoms)
array.shift(box_dirs)
array.shift(box_births)
array.shift(box_touches)
array.shift(box_swept)
array.shift(box_sweep_wick)
// FIX #6: Delete the oldest visual box from chart
box old_box = array.shift(box_visuals)
box.delete(old_box)
string session_context = in_ny_active ? "NY ACTIVE" : (in_london_active ? "LONDON ACTIVE" : "ASIA BACKGROUND")
string box_type = bullish ? "DEMAND (LIME)" : "SUPPLY (RED)"
string msg = "Phidias " + box_type + " Box | " + session_context + " | Z=" + str.tostring(z_vol, "#.##") + " @ " + str.tostring(close)
alert(msg, alert.freq_once_per_bar)
// ═══════════════════════════════════════════════════════════════════════════════
// 10. TOUCH & SWEEP DETECTION ENGINE
// FIX #5: Wick vs body classification, displacement tracking
// FIX #6: Mitigated/expired boxes removed from chart + tracking
// ═══════════════════════════════════════════════════════════════════════════════
var bool touch_demand_alert = false
var bool touch_supply_alert = false
var bool sweep_demand_alert = false
var bool sweep_supply_alert = false
// FIX #5: Sweep quality tracking (most recent sweep)
var bool last_sweep_wick = false
var bool last_sweep_displaced = false
var int last_sweep_bar = 0
var int last_sweep_dir = 0
var float last_sweep_level = na
touch_demand_alert := false
touch_supply_alert := false
sweep_demand_alert := false
sweep_supply_alert := false
int n_boxes = array.size(box_tops)
// FIX #6: Collect indices to remove (iterate backwards so indices stay valid)
if n_boxes > 0
for i = n_boxes - 1 to 0
float b_top = array.get(box_tops, i)
float b_bottom = array.get(box_bottoms, i)
int b_dir = array.get(box_dirs, i)
int b_birth = array.get(box_births, i)
bool b_swept = array.get(box_swept, i)
int b_touch = array.get(box_touches, i)
// FIX #6: Remove expired boxes from chart and arrays
if bar_index - b_birth > box_expire_bars
box old_vis = array.get(box_visuals, i)
box.delete(old_vis)
array.remove(box_tops, i)
array.remove(box_bottoms, i)
array.remove(box_dirs, i)
array.remove(box_births, i)
array.remove(box_touches, i)
array.remove(box_swept, i)
array.remove(box_sweep_wick, i)
array.remove(box_visuals, i)
continue
// FIX #6: Skip already-swept boxes but keep them visible (faded)
if b_swept
// Fade the swept box visual
box swept_vis = array.get(box_visuals, i)
box.set_bgcolor(swept_vis, color.new(color.gray, 92))
box.set_border_color(swept_vis, color.new(color.gray, 70))
continue
if b_dir == 1
bool near_bottom = low <= (b_bottom + touch_zone) and low >= (b_bottom - touch_zone)
bool closed_above = close > b_bottom
bool swept_below = low < (b_bottom - touch_zone)
// BUG FIX: Actually increment touch count
if near_bottom and closed_above and not swept_below
array.set(box_touches, i, b_touch + 1)
touch_demand_alert := true
if swept_below
array.set(box_swept, i, true)
sweep_demand_alert := true
// FIX #5: Classify wick vs body
bool is_wick = close > b_bottom
array.set(box_sweep_wick, i, is_wick)
last_sweep_wick := is_wick
last_sweep_bar := bar_index
last_sweep_dir := 1
last_sweep_level := b_bottom
last_sweep_displaced := false
if b_dir == -1
bool near_top = high >= (b_top - touch_zone) and high <= (b_top + touch_zone)
bool closed_below = close < b_top
bool swept_above = high > (b_top + touch_zone)
// BUG FIX: Actually increment touch count
if near_top and closed_below and not swept_above
array.set(box_touches, i, b_touch + 1)
touch_supply_alert := true
if swept_above
array.set(box_swept, i, true)
sweep_supply_alert := true
// FIX #5: Classify wick vs body
bool is_wick = close < b_top
array.set(box_sweep_wick, i, is_wick)
last_sweep_wick := is_wick
last_sweep_bar := bar_index
last_sweep_dir := -1
last_sweep_level := b_top
last_sweep_displaced := false
// FIX #5: Check displacement on bar after sweep
if bar_index == last_sweep_bar + 1 and last_sweep_bar > 0
if last_sweep_dir == 1
last_sweep_displaced := (close - last_sweep_level) >= displacement_pts
else if last_sweep_dir == -1
last_sweep_displaced := (last_sweep_level - close) >= displacement_pts
// FIX #5: Sweep quality score (0-2 for visual indicator, no FVG check here)
int sweep_quality = 0
bool sweep_active = last_sweep_bar > 0 and (bar_index - last_sweep_bar) <= 30
if sweep_active
if last_sweep_wick
sweep_quality += 1
if last_sweep_displaced
sweep_quality += 1
// ═══════════════════════════════════════════════════════════════════════════════
// 11. SWEEP QUALITY LABEL (on-chart visual)
// ═══════════════════════════════════════════════════════════════════════════════
// Show a small label at sweep location indicating quality
if sweep_demand_alert or sweep_supply_alert
string sw_type = last_sweep_wick ? "WICK" : "BODY"
string sw_lbl = "SW:" + sw_type
color sw_col = last_sweep_wick ? color.new(c_demand, 20) : color.new(color.orange, 20)
label.new(bar_index, last_sweep_dir == 1 ? low : high, sw_lbl,
color=sw_col, textcolor=color.white, size=size.tiny,
style=last_sweep_dir == 1 ? label.style_label_up : label.style_label_down)
// Displacement confirmation label (fires one bar after sweep)
if bar_index == last_sweep_bar + 1 and last_sweep_bar > 0 and sweep_active
if last_sweep_displaced
label.new(bar_index, last_sweep_dir == 1 ? low : high, "DISP ✓",
color=color.new(c_demand, 30), textcolor=color.white, size=size.tiny,
style=last_sweep_dir == 1 ? label.style_label_up : label.style_label_down)
// ═══════════════════════════════════════════════════════════════════════════════
// 12. ALERTS
// ═══════════════════════════════════════════════════════════════════════════════
alertcondition(high_vol, title="Institutional Box Detected",
message="Phidias Box Forming - Check Session Context")
alertcondition(touch_demand_alert, title="Demand Box Touch — Sweep Incoming",
message="Price touched Demand Box boundary — liquidity building below.")
alertcondition(touch_supply_alert, title="Supply Box Touch — Sweep Incoming",
message="Price touched Supply Box boundary — liquidity building above.")
// FIX #5: Separate wick and body sweep alerts
alertcondition(sweep_demand_alert and last_sweep_wick, title="Demand WICK Sweep — High Probability",
message="Demand Box wick sweep — liquidity grabbed, price rejected. Check for reversal.")
alertcondition(sweep_demand_alert and not last_sweep_wick, title="Demand BODY Sweep — Breakout Watch",
message="Demand Box body sweep — potential breakdown, not a clean liquidity grab.")
alertcondition(sweep_supply_alert and last_sweep_wick, title="Supply WICK Sweep — High Probability",
message="Supply Box wick sweep — liquidity grabbed, price rejected. Check for reversal.")
alertcondition(sweep_supply_alert and not last_sweep_wick, title="Supply BODY Sweep — Breakout Watch",
message="Supply Box body sweep — potential breakout, not a clean liquidity grab.")
// ═══════════════════════════════════════════════════════════════════════════════
// 13. INFO TABLE (compact sweep status)
// ═══════════════════════════════════════════════════════════════════════════════
var table info = table.new(position.top_right, 3, 2, bgcolor=color.new(#000000, 80), border_width=1)
if barstate.islast
table.cell(info, 0, 0, "SWEEP", text_color=#888888, text_size=size.tiny)
table.cell(info, 1, 0, "TYPE", text_color=#888888, text_size=size.tiny)
table.cell(info, 2, 0, "SQ", text_color=#888888, text_size=size.tiny)
string sw_status = sweep_active ? (last_sweep_dir == 1 ? "BULL ↓" : "BEAR ↑") : "NONE"
string sw_type = sweep_active ? (last_sweep_wick ? "WICK" : "BODY") : "-"
string sq_text = sweep_active ? str.tostring(sweep_quality) + "/2" : "-"
color sw_bg = sweep_active ? (last_sweep_wick ? color.new(c_demand, 30) : color.new(color.orange, 30)) : color.new(#000000, 40)
color sq_bg = sweep_quality >= 2 ? color.new(#00e676, 15) : sweep_quality >= 1 ? color.new(#448aff, 25) : color.new(#444455, 50)
color sq_tc = sweep_quality >= 1 ? color.white : color.gray
table.cell(info, 0, 1, sw_status, bgcolor=sw_bg, text_color=color.white, text_size=size.tiny)
table.cell(info, 1, 1, sw_type, bgcolor=sw_bg, text_color=color.white, text_size=size.tiny)
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)