This overlay indicator draws KOLA pivot support and resistance, order blocks, optional change-of-character and break-of-structure marks, SIDO swing chart patterns, and Fibonacci or OTE levels on the price chart.
1Copy the scriptPress Copy script above, or the copy button on the code below.
2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.
Source Code
1,717 lines of Pine Script v6 from mt5/GOM_KOLA_script.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.
GOM_KOLA_script.pine
//@version=6
indicator("GOM KOLA SIDO — Full Integration", shorttitle="GOM SIDO", overlay=true,
max_lines_count=500, max_labels_count=200, max_boxes_count=580)
// ═══════════════════════════════════════════════════════════════
// VISUAL IDENTITY — GHOST PRO PALETTE
// ═══════════════════════════════════════════════════════════════
BRAND_PRIMARY = #00D5FE // Cyan signature GOM KOLA SIDO
BRAND_SECONDARY = #FF6B35 // Orange alertes / spikes
BRAND_ACCENT = #FFD700 // Gold excellence / qualité premium
BULL = #00FF80 // Green électrique (BUY confirmé)
BEAR = #FF3366 // Red moderne (SELL confirmé)
WAIT = #FFB800 // Orange neutre (attente / WAIT)
BG_MAIN = color.new(#0A0E1A, 85) // Background principal
BG_HEADER = color.new(#1A1F2E, 60) // Headers tables
BG_CELL = color.new(#14172B, 80) // Cellules alternées
BORDER = color.new(#2E3650, 40) // Séparateurs
TEXT_PRIMARY = color.white // Valeurs importantes
TEXT_SECONDARY = color.new(#B0B8D0, 0) // Labels
TEXT_MUTED = color.new(#6B7280, 0) // Notes
// ── GHOST OrderFlow palette (déclarée ici pour éviter CE10 doublon) ──
OF_BULL = #2962ff
OF_BEAR = #f23645
OF_NEUT = #787b86
OF_GOLD = #ffd700
OF_BG = color.new(#0c0e16, 80)
OF_HDR = color.new(#191c2a, 60)
OF_SEP = color.new(#282d40, 10)
// ═══════════════════════════════════════════════════════════════
// INPUTS
// ═══════════════════════════════════════════════════════════════
grp_kola = "KOLA LEVELS (pivot S/R + touches)"
kola_lb = input.int(3, "Line Break Period", minval=1, maxval=10, group=grp_kola)
kola_bars = input.int(120, "Max bars analyse", minval=40, maxval=300, group=grp_kola)
kola_show = input.bool(true,"Afficher niveaux KOLA", group=grp_kola)
kola_touch = input.bool(true,"Touch detection", group=grp_kola)
kola_touch_zone_pct = input.float(30.0, "Touch zone ATR%", minval=5, maxval=80, group=grp_kola)
kola_touch_bars = input.int(150, "Bars for touch count", minval=20, maxval=500, group=grp_kola)
grp_ob = "ORDER BLOCKS"
ob_lookback = input.int(10, "Lookback OB", minval=3, maxval=50, group=grp_ob)
ob_show = input.bool(true,"Afficher OBs", group=grp_ob)
grp_struct = "STRUCTURE (CHoCH / BOS)"
show_choch = input.bool(false,"Afficher CHoCH / BOS (masqué par défaut)", group=grp_struct)
struct_lb = input.int(8, "Lookback pivots", minval=3, maxval=30, group=grp_struct)
grp_sido = "SIDO (FIGURES CHARTISTES)"
sido_enable = input.bool(true,"Activer SIDO", group=grp_sido)
sido_lb = input.int(3, "Pivot lookback SIDO", minval=2, maxval=10, group=grp_sido)
sido_bars = input.int(300, "Bars to analyze", minval=50, maxval=500, group=grp_sido)
sido_max_gap= input.int(80, "Max bars between swings", minval=10,maxval=200,group=grp_sido)
sido_tol_pct= input.float(35.0,"Tolerance ATR%", minval=5, maxval=80, group=grp_sido)
grp_fib = "FIBONACCI OTE"
show_fib = input.bool(true, "Afficher Fibonacci / OTE", group=grp_fib)
fib_lb = input.int(50, "Lookback swing", minval=20, maxval=200, group=grp_fib)
grp_spike = "SPIKE DETECTOR"
spike_lb = input.int(25, "Lookback spike", minval=10, maxval=60, group=grp_spike)
spike_min = input.float(0.62,"Seuil alerte spike", minval=0.40,maxval=0.95,step=0.01, group=grp_spike)
spike_blink = input.float(0.52,"Seuil alerte precoce",minval=0.40,maxval=0.95,step=0.01, group=grp_spike)
grp_spike_bc = "BOOM/CRASH — PRE-SPIKE"
spike_bc_en = input.bool(true, "Activer pre-spike Boom/Crash", group=grp_spike_bc)
spike_freq_ov= input.int(0, "Frequence spike (0=auto)", minval=0, maxval=2000, group=grp_spike_bc)
spike_wake = input.float(0.30,"Reveil cycle (30%)", minval=0.10, maxval=0.50, step=0.05, group=grp_spike_bc)
spike_watch = input.float(0.55,"Zone watch (55%)", minval=0.40, maxval=0.70, step=0.05, group=grp_spike_bc)
spike_entry = input.float(0.65,"Zone entree (65%)", minval=0.55, maxval=0.80, step=0.05, group=grp_spike_bc)
spike_overdue= input.float(0.85,"Reset cycle (85%)", minval=0.75, maxval=0.95, step=0.05, group=grp_spike_bc)
spike_min_prob=input.int(55, "Prob min tradable %", minval=40, maxval=85, group=grp_spike_bc)
spike_z_min = input.float(2.0, "Z-score spike min", minval=1.0, maxval=4.0, step=0.1, group=grp_spike_bc)
spike_stair_b= input.int(5, "Barres stair", minval=3, maxval=10, group=grp_spike_bc)
spike_show_bc= input.bool(true, "Tableau pre-spike", group=grp_spike_bc)
spike_show_lbl=input.bool(true,"Labels spike chart", group=grp_spike_bc)
spike_sl_atr = input.float(0.8,"SL spike (x ATR)", minval=0.3, maxval=2.0, step=0.1, group=grp_spike_bc)
spike_tp_atr = input.float(1.2,"TP spike (x ATR)", minval=0.5, maxval=3.0, step=0.1, group=grp_spike_bc)
grp_ui = "INTERFACE — SIGNAL CLAIR"
ui_signal_panel = input.bool(true, "Panneau SIGNAL unifié (recommandé Boom/Crash)", group=grp_ui)
ui_compact_bc = input.bool(true, "Mode compact Boom/Crash (moins de bruit)", group=grp_ui)
ui_show_arrow = input.bool(true, "Flèche clignotante entrée pre-spike", group=grp_ui)
path_show_bc = input.bool(false, "Chemin predictif sur Boom/Crash", group=grp_ui)
grp_ind = "INDICATEURS"
show_vwap = input.bool(true,"VWAP session", group=grp_ind)
show_bb = input.bool(true,"Bollinger Bands", group=grp_ind)
bb_period = input.int(20, "Periode BB", group=grp_ind)
bb_dev = input.float(2.0,"Deviation BB", group=grp_ind)
show_st = input.bool(true,"Supertrend", group=grp_ind)
st_atr_per = input.int(10, "ATR Supertrend", group=grp_ind)
st_mult = input.float(3.0,"Mult Supertrend", group=grp_ind)
show_kc = input.bool(true,"Keltner Channel", group=grp_ind)
kc_ema_per = input.int(20, "EMA Keltner", group=grp_ind)
kc_atr_mult = input.float(1.5,"Mult Keltner", group=grp_ind)
show_dc = input.bool(true,"Donchian Channel", group=grp_ind)
dc_period = input.int(20, "Periode Donchian", group=grp_ind)
grp_mtf = "TABLEAU MTF"
show_mtf = input.bool(true,"Afficher tableau MTF", group=grp_mtf)
tf1 = input.timeframe("1", "TF1", group=grp_mtf)
tf2 = input.timeframe("5", "TF2", group=grp_mtf)
tf3 = input.timeframe("15", "TF3", group=grp_mtf)
tf4 = input.timeframe("60", "TF4", group=grp_mtf)
tf5 = input.timeframe("240","TF5", group=grp_mtf)
tf6 = input.timeframe("D", "TF6", group=grp_mtf)
tf7 = input.timeframe("W", "TF7", group=grp_mtf)
grp_verdict = "VERDICT"
show_verdict= input.bool(true,"Afficher verdict", group=grp_verdict)
verdict_gap_th = input.float(0.45, "Gap threshold", minval=0.1, maxval=2.0, step=0.05, group=grp_verdict)
verdict_coherence = input.bool(true, "Coherence gate", group=grp_verdict)
verdict_bb_vwap_weight = input.float(1.0, "BB/VWAP weight", minval=0.0, maxval=3.0, step=0.1, group=grp_verdict)
verdict_adv_weight = input.float(0.8, "Advanced indic weight", minval=0.0, maxval=3.0, step=0.1, group=grp_verdict)
grp_quality = "ENTRY QUALITY"
show_quality = input.bool(true, "Afficher quality score", group=grp_quality)
grp_setup = "TABLEAU SETUP (ordre limite)"
show_setup = input.bool(true, "Afficher tableau SETUP", group=grp_setup)
setup_rr_mult = input.float(1.0, "TP1 = R x mult", minval=0.5, maxval=3.0, step=0.1, group=grp_setup)
setup_rr2_mult= input.float(1.5, "TP2 = R x mult", minval=0.5, maxval=4.0, step=0.1, group=grp_setup)
grp_path = "CHEMIN PREDICTIF (200 bougies)"
path_horizon = input.int(200, "Horizon bougies", minval=20, maxval=200, group=grp_path)
path_show = input.bool(true, "Segments direction futurs", group=grp_path)
path_show_candles = input.bool(true, "Bougies predites (fantomes)", group=grp_path)
path_candle_vis = input.int(200, "Nb bougies fantomes visibles", minval=15, maxval=200, group=grp_path)
path_width = input.int(5, "Epaisseur segments", minval=2, maxval=10, group=grp_path)
path_step = input.float(0.16, "Pas (x ATR)", minval=0.05, maxval=0.45, step=0.01, group=grp_path)
path_in_alert= input.bool(true, "Envoyer chemin → webhook", group=grp_path)
// ═══════════════════════════════════════════════════════════════
// GHOST — ORDER FLOW (inputs)
// ═══════════════════════════════════════════════════════════════
grp_of = "GHOST — ORDER FLOW"
of_hm_show = input.bool(true, "Heatmap liquidité", group=grp_of)
of_hm_lb = input.int(100, "Heatmap lookback bougies", group=grp_of, minval=20, maxval=300)
of_hm_zones = input.int(24, "Heatmap tranches", group=grp_of, minval=8, maxval=40)
of_dlt_show = input.bool(true, "Delta + CVD", group=grp_of)
of_dlt_bars = input.int(12, "Barres histogramme Delta", group=grp_of, minval=6, maxval=20)
of_cmp_show = input.bool(true, "Boussole momentum", group=grp_of)
of_snt_show = input.bool(true, "Sentiment BUY/SELL%", group=grp_of)
of_snt_per = input.int(20, "Sentiment période", group=grp_of, minval=5, maxval=100)
// ═══════════════════════════════════════════════════════════════
// VWAP SESSION
// ═══════════════════════════════════════════════════════════════
var float vwap_sum = 0.0
var float vwap_vol = 0.0
is_new_session = ta.change(time("D")) != 0
if is_new_session
vwap_sum := 0.0
vwap_vol := 0.0
typical = (high + low + close) / 3
vwap_sum += typical * volume
vwap_vol += volume
vwap_val = vwap_vol > 0 ? vwap_sum / vwap_vol : close
plot(show_vwap ? vwap_val : na, "vwap", color=color.new(color.yellow, 0), linewidth=2)
// ═══════════════════════════════════════════════════════════════
// BOLLINGER BANDS
// ═══════════════════════════════════════════════════════════════
[bb_mid, bb_up, bb_dn] = ta.bb(close, bb_period, bb_dev)
plot(show_bb ? bb_up : na, "bb_up", color=color.new(color.blue, 40), linewidth=1)
plot(show_bb ? bb_mid : na, "bb_mid", color=color.new(color.silver, 50), linewidth=1, style=plot.style_circles)
plot(show_bb ? bb_dn : na, "bb_dn", color=color.new(color.blue, 40), linewidth=1)
bb_width = bb_up - bb_dn
bb_squeeze = bb_width < ta.sma(bb_width, 20) * 0.85
bb_pctb = bb_width > 0 ? (close - bb_dn) / bb_width : 0.5
// ═══════════════════════════════════════════════════════════════
// SUPERTREND
// ═══════════════════════════════════════════════════════════════
atr_st = ta.atr(st_atr_per)
up_band = hl2 - st_mult * atr_st
dn_band = hl2 + st_mult * atr_st
var float st_up = na
var float st_dn = na
var int st_dir = 1
st_up := math.max(up_band, nz(st_up[1], up_band))
st_dn := math.min(dn_band, nz(st_dn[1], dn_band))
if close > nz(st_dn[1])
st_dir := 1
else if close < nz(st_up[1])
st_dir := -1
st_line = st_dir == 1 ? st_up : st_dn
plot(show_st ? st_line : na, "st_line",
color=st_dir==1 ? color.new(color.lime,30) : color.new(color.red,30), linewidth=2)
// ═══════════════════════════════════════════════════════════════
// KELTNER CHANNEL (sans plot — limite 64 TradingView)
// ═══════════════════════════════════════════════════════════════
kc_mid = ta.ema(close, kc_ema_per)
kc_atr = ta.atr(kc_ema_per)
kc_upper = kc_mid + kc_atr_mult * kc_atr
kc_lower = kc_mid - kc_atr_mult * kc_atr
kc_pos = kc_upper > kc_lower ? ((close - kc_lower) / (kc_upper - kc_lower)) * 2.0 - 1.0 : 0.0
// ═══════════════════════════════════════════════════════════════
// DONCHIAN CHANNEL (sans plot — limite 64 TradingView)
// ═══════════════════════════════════════════════════════════════
dc_high = ta.highest(high, dc_period)
dc_low = ta.lowest(low, dc_period)
dc_mid_val = (dc_high + dc_low) / 2
dc_sig = close > dc_high - atr_st * 0.05 ? 1.0 : close < dc_low + atr_st * 0.05 ? -1.0 : 0.0
// ═══════════════════════════════════════════════════════════════
// KOLA LEVELS — Pivot S/R avec Touch Counting (MTF)
// ═══════════════════════════════════════════════════════════════
// Logique portee du MQL5: on cherche le meilleur pivot low (BUY) sous le prix
// et le meilleur pivot high (SELL) au-dessus du prix, ponderes par touches.
atr14 = ta.atr(14)
touch_zone = kola_touch ? atr14 * (kola_touch_zone_pct / 100.0) : 0.0
// Fonction pour trouver les niveaux KOLA sur le TF actuel
is_pivot_high(src, lb, idx) =>
result = true
for k = 1 to lb
if idx - k >= 0
if high[idx - k] >= src
result := false
break
if high[idx + k] >= src
result := false
break
result
is_pivot_low(src, lb, idx) =>
result = true
for k = 1 to lb
if idx - k >= 0
if low[idx - k] <= src
result := false
break
if low[idx + k] <= src
result := false
break
result
// Calcul touches
calc_touches(level, zone_size, max_bars) =>
touches = 0
for i = 0 to math.min(max_bars, 499)
h = high[i]
l = low[i]
if math.abs(h - level) <= zone_size or math.abs(l - level) <= zone_size or (l <= level and h >= level)
touches += 1
touches
// Trouver niveaux KOLA sur le timeframe courant
var float kola_buy_level = na
var float kola_sell_level = na
var int kola_buy_touches = 0
var int kola_sell_touches = 0
if barstate.islast and kola_show
best_buy = 0.0
best_sell = 0.0
best_buy_t = 0
best_sell_t = 0
max_idx = math.min(bar_index, kola_bars)
float lvl = na
int t = 0
for i = kola_lb + 1 to max_idx
// Pivot low → BUY level
is_pl = true
for k = 1 to kola_lb
if low[i - k] <= low[i] or low[i + k] < low[i]
is_pl := false
break
if is_pl
lvl := low[i]
t := kola_touch ? calc_touches(lvl, touch_zone, kola_touch_bars) : 1
if lvl < close
if t > best_buy_t or (t == best_buy_t and lvl > best_buy)
best_buy := lvl
best_buy_t := t
// Pivot high → SELL level
is_ph = true
for k = 1 to kola_lb
if high[i - k] >= high[i] or high[i + k] > high[i]
is_ph := false
break
if is_ph
lvl := high[i]
t := kola_touch ? calc_touches(lvl, touch_zone, kola_touch_bars) : 1
if lvl > close
if t > best_sell_t or (t == best_sell_t and (best_sell == 0.0 or lvl < best_sell))
best_sell := lvl
best_sell_t := t
kola_buy_level := best_buy > 0 ? best_buy : na
kola_sell_level := best_sell > 0 ? best_sell : na
kola_buy_touches := best_buy_t
kola_sell_touches := best_sell_t
// Dessiner les niveaux KOLA
var line kola_buy_ln = na
var line kola_sell_ln = na
var label kola_buy_lbl = na
var label kola_sell_lbl = na
if barstate.islast and kola_show
if not na(kola_buy_ln)
line.delete(kola_buy_ln)
if not na(kola_sell_ln)
line.delete(kola_sell_ln)
if not na(kola_buy_lbl)
label.delete(kola_buy_lbl)
if not na(kola_sell_lbl)
label.delete(kola_sell_lbl)
if not na(kola_buy_level)
kola_buy_ln := line.new(bar_index - kola_bars, kola_buy_level, bar_index, kola_buy_level,
color=color.new(BULL, 0), width=2, style=line.style_solid, extend=extend.right)
kola_buy_lbl := label.new(bar_index, kola_buy_level,
"BUY " + str.tostring(math.round(kola_buy_level, 2)) + " (" + str.tostring(kola_buy_touches) + "t)",
color=color.new(color.black,100), textcolor=color.new(BULL, 0), size=size.small, style=label.style_label_left)
if not na(kola_sell_level)
kola_sell_ln := line.new(bar_index - kola_bars, kola_sell_level, bar_index, kola_sell_level,
color=color.new(BEAR, 0), width=2, style=line.style_solid, extend=extend.right)
kola_sell_lbl := label.new(bar_index, kola_sell_level,
"SELL " + str.tostring(math.round(kola_sell_level, 2)) + " (" + str.tostring(kola_sell_touches) + "t)",
color=color.new(color.black,100), textcolor=color.new(BEAR, 0), size=size.small, style=label.style_label_left)
// ═══════════════════════════════════════════════════════════════
// SIDO — DOUBLE TOP / DOUBLE BOTTOM
// ═══════════════════════════════════════════════════════════════
var float sido_dt_level = na
var float sido_db_level = na
var line sido_dt_ln = na
var line sido_db_ln = na
var label sido_dt_lbl = na
var label sido_db_lbl = na
if barstate.islast and sido_enable
// Cleanup
if not na(sido_dt_ln)
line.delete(sido_dt_ln)
if not na(sido_db_ln)
line.delete(sido_db_ln)
if not na(sido_dt_lbl)
label.delete(sido_dt_lbl)
if not na(sido_db_lbl)
label.delete(sido_db_lbl)
sido_dt_level := na
sido_db_level := na
tol = atr14 * (sido_tol_pct / 100.0)
max_idx_sido = math.min(bar_index, sido_bars)
// Trouver les 2 derniers pivot highs et lows
var int ph1_idx = -1
var int ph2_idx = -1
var int pl1_idx = -1
var int pl2_idx = -1
ph1_idx := -1
ph2_idx := -1
pl1_idx := -1
pl2_idx := -1
for i = sido_lb + 1 to max_idx_sido
// Pivot high
is_ph_s = true
for k = 1 to sido_lb
if high[i - k] >= high[i] or high[i + k] > high[i]
is_ph_s := false
break
if is_ph_s
if ph1_idx == -1
ph1_idx := i
else if ph2_idx == -1
ph2_idx := i
break
for i = sido_lb + 1 to max_idx_sido
// Pivot low
is_pl_s = true
for k = 1 to sido_lb
if low[i - k] <= low[i] or low[i + k] < low[i]
is_pl_s := false
break
if is_pl_s
if pl1_idx == -1
pl1_idx := i
else if pl2_idx == -1
pl2_idx := i
break
// Double Top
if ph1_idx >= 0 and ph2_idx >= 0
bars_gap_h = math.abs(ph1_idx - ph2_idx)
h1 = high[ph1_idx]
h2 = high[ph2_idx]
if bars_gap_h <= sido_max_gap and math.abs(h1 - h2) <= tol
dt_lvl = (h1 + h2) / 2.0
sido_dt_level := dt_lvl
sido_dt_ln := line.new(bar_index - ph2_idx, dt_lvl, bar_index, dt_lvl,
color=color.new(BRAND_SECONDARY, 20), width=2, style=line.style_dashed, extend=extend.right)
sido_dt_lbl := label.new(bar_index, dt_lvl, "DT",
color=color.new(BRAND_SECONDARY, 20), textcolor=color.white, size=size.small, style=label.style_label_left)
// Double Bottom
if pl1_idx >= 0 and pl2_idx >= 0
bars_gap_l = math.abs(pl1_idx - pl2_idx)
l1 = low[pl1_idx]
l2 = low[pl2_idx]
if bars_gap_l <= sido_max_gap and math.abs(l1 - l2) <= tol
db_lvl = (l1 + l2) / 2.0
sido_db_level := db_lvl
sido_db_ln := line.new(bar_index - pl2_idx, db_lvl, bar_index, db_lvl,
color=color.new(BRAND_PRIMARY, 20), width=2, style=line.style_dashed, extend=extend.right)
sido_db_lbl := label.new(bar_index, db_lvl, "DB",
color=color.new(BRAND_PRIMARY, 20), textcolor=color.white, size=size.small, style=label.style_label_left)
// ═══════════════════════════════════════════════════════════════
// ORDER BLOCKS — pivot-based avec invalidation
// ═══════════════════════════════════════════════════════════════
pivot_h = ta.pivothigh(high, ob_lookback, ob_lookback)
pivot_l = ta.pivotlow(low, ob_lookback, ob_lookback)
var float ob_bear_top = na
var float ob_bear_bot = na
var int ob_bear_bar = na
var float ob_bull_top = na
var float ob_bull_bot = na
var int ob_bull_bar = na
if not na(pivot_h)
ob_bear_top := high[ob_lookback + 1]
ob_bear_bot := math.min(open[ob_lookback + 1], close[ob_lookback + 1])
ob_bear_bar := bar_index - ob_lookback - 1
if not na(pivot_l)
ob_bull_top := math.max(open[ob_lookback + 1], close[ob_lookback + 1])
ob_bull_bot := low[ob_lookback + 1]
ob_bull_bar := bar_index - ob_lookback - 1
if not na(ob_bear_top) and high >= ob_bear_bot
ob_bear_top := na
ob_bear_bot := na
if not na(ob_bull_bot) and low <= ob_bull_top
ob_bull_top := na
ob_bull_bot := na
var box ob_bear_box = na
var box ob_bull_box = na
if ob_show and not na(ob_bear_top) and not na(ob_bear_bar)
if not na(ob_bear_box)
box.delete(ob_bear_box)
ob_bear_box := box.new(ob_bear_bar, ob_bear_top, bar_index, ob_bear_bot,
border_color=color.new(BEAR, 0), border_width=2, bgcolor=color.new(BEAR, 88),
extend=extend.right, text="OB Bear", text_color=color.new(BEAR, 0), text_size=size.small)
if ob_show and not na(ob_bull_bot) and not na(ob_bull_bar)
if not na(ob_bull_box)
box.delete(ob_bull_box)
ob_bull_box := box.new(ob_bull_bar, ob_bull_top, bar_index, ob_bull_bot,
border_color=color.new(BULL, 0), border_width=2, bgcolor=color.new(BULL, 88),
extend=extend.right, text="OB Bull", text_color=color.new(BULL, 0), text_size=size.small)
// ═══════════════════════════════════════════════════════════════
// CHoCH / BOS — pivot structure
// ═══════════════════════════════════════════════════════════════
ph = ta.pivothigh(high, struct_lb, struct_lb)
pl = ta.pivotlow(low, struct_lb, struct_lb)
var float last_ph = na
var float last_pl = na
var float prev_ph = na
var float prev_pl = na
if not na(ph)
prev_ph := last_ph
last_ph := ph
if not na(pl)
prev_pl := last_pl
last_pl := pl
choch_bear = not na(last_ph) and not na(prev_ph) and not na(prev_pl) and close < prev_pl and last_ph < prev_ph
choch_bull = not na(last_pl) and not na(prev_pl) and not na(prev_ph) and close > prev_ph and last_pl > prev_pl
bos_bear = not na(last_pl) and close < last_pl and not choch_bear
bos_bull = not na(last_ph) and close > last_ph and not choch_bull
if show_choch
if choch_bear
label.new(bar_index, high, "CHoCH v",
color=color.new(BEAR,15), textcolor=color.white,
style=label.style_label_down, size=size.small)
if choch_bull
label.new(bar_index, low, "CHoCH ^",
color=color.new(BULL,15), textcolor=color.white,
style=label.style_label_up, size=size.small)
if bos_bear
label.new(bar_index, high, "BOS v",
color=color.new(BRAND_SECONDARY,15), textcolor=color.white,
style=label.style_label_down, size=size.tiny)
if bos_bull
label.new(bar_index, low, "BOS ^",
color=color.new(BRAND_PRIMARY,15), textcolor=color.white,
style=label.style_label_up, size=size.tiny)
// ═══════════════════════════════════════════════════════════════
// FIBONACCI OTE
// ═══════════════════════════════════════════════════════════════
fib_high = ta.highest(high, fib_lb)
fib_low = ta.lowest(low, fib_lb)
fib_rng = fib_high - fib_low
f0 = fib_high
f236 = fib_high - 0.236 * fib_rng
f382 = fib_high - 0.382 * fib_rng
f500 = fib_high - 0.500 * fib_rng
f618 = fib_high - 0.618 * fib_rng
f786 = fib_high - 0.786 * fib_rng
f100 = fib_low
var line[] fib_lines = array.new_line()
var label[] fib_labels = array.new_label()
var box[] fib_boxes = array.new_box()
if barstate.islast and show_fib
int nl = array.size(fib_lines)
if nl > 0
for i = 0 to nl - 1
line.delete(array.get(fib_lines, i))
array.clear(fib_lines)
int nlb = array.size(fib_labels)
if nlb > 0
for i = 0 to nlb - 1
label.delete(array.get(fib_labels, i))
array.clear(fib_labels)
int nb = array.size(fib_boxes)
if nb > 0
for i = 0 to nb - 1
box.delete(array.get(fib_boxes, i))
array.clear(fib_boxes)
t_now = bar_index
t_back = bar_index - fib_lb
fib_prices = array.from(f0, f236, f382, f500, f618, f786, f100)
fib_names = array.from("0%", "23.6%", "38.2%", "50%", "61.8% OTE", "78.6% OTE", "100%")
for i = 0 to 6
_p = array.get(fib_prices, i)
_n = array.get(fib_names, i)
_ote = i == 4 or i == 5
_col = _ote ? color.new(BRAND_PRIMARY, 0) : i == 3 ? color.new(WAIT, 0) : color.new(TEXT_MUTED, 0)
_lw = _ote ? 2 : i == 3 ? 2 : 1
_sty = _ote ? line.style_solid : line.style_dashed
_l = line.new(t_back, _p, t_now, _p, color=_col, width=_lw, style=_sty, extend=extend.right)
array.push(fib_lines, _l)
_lb = label.new(t_now, _p, " " + _n + " " + str.tostring(math.round(_p, 2)),
color=color.new(color.black, 100), textcolor=_col,
size=size.tiny, style=label.style_label_left)
array.push(fib_labels, _lb)
// Zone OTE (61.8–78.6%) — surface colorée visible + extend
_ote_col = st_dir == -1 ? color.new(BEAR, 75) : color.new(BULL, 75)
_ote_bord = st_dir == -1 ? color.new(BEAR, 0) : color.new(BULL, 0)
_b = box.new(t_back, f618, t_now, f786,
border_color=_ote_bord, bgcolor=_ote_col, border_width=2, extend=extend.right)
array.push(fib_boxes, _b)
// Ligne SL/TP (atr14 defini globalement ligne 147)
_sl_price = st_dir == -1 ? f0 + atr14 * 0.5 : f100 - atr14 * 0.5
_tp_price = st_dir == -1 ? f100 - atr14 * 0.2 : f0 + atr14 * 0.2
_sl_col = color.new(BEAR, 20)
_tp_col = color.new(BULL, 20)
_sl_l = line.new(t_back, _sl_price, t_now, _sl_price,
color=_sl_col, width=2, style=line.style_dashed, extend=extend.right)
_tp_l = line.new(t_back, _tp_price, t_now, _tp_price,
color=_tp_col, width=2, style=line.style_dashed, extend=extend.right)
_sl_lb = label.new(t_now, _sl_price, " SL " + str.tostring(math.round(_sl_price, 2)),
color=color.new(color.black, 100), textcolor=_sl_col, size=size.tiny, style=label.style_label_left)
_tp_lb = label.new(t_now, _tp_price, " TP " + str.tostring(math.round(_tp_price, 2)),
color=color.new(color.black, 100), textcolor=_tp_col, size=size.tiny, style=label.style_label_left)
array.push(fib_lines, _sl_l)
array.push(fib_lines, _tp_l)
array.push(fib_labels, _sl_lb)
array.push(fib_labels, _tp_lb)
// ═══════════════════════════════════════════════════════════════
// SPIKE DETECTOR — multi-facteur + PRE-SPIKE BOOM/CRASH
// ═══════════════════════════════════════════════════════════════
candle_body = math.abs(close - open)
candle_range = high - low
body_ratio = candle_range > 0 ? candle_body / candle_range : 0.0
avg_range = ta.sma(candle_range, spike_lb)
avg_volume = ta.sma(volume, spike_lb)
// ATR compression (du MQL5: ratio < 1 → compression)
atr_comp_ratio = nz(atr14[1]) > 0 ? atr14 / nz(atr14[1]) : 1.0
atr_compression = atr_comp_ratio < 1.0 ? math.min((1.0 - atr_comp_ratio) / 0.6, 1.0) : 0.0
atr14_old = ta.sma(atr14, 14)
atr_comp_deep = atr14_old > 0 ? math.max(0.0, math.min((atr14_old - atr14) / atr14_old / 0.4, 1.0)) : 0.0
// Acceleration (du MQL5)
change1 = close[1] != 0 ? (close - close[1]) / close[1] : 0.0
change2 = close[3] != 0 ? (close[2] - close[3]) / close[3] : 0.0
accel = math.min(math.abs(change1 - change2) / 0.003, 1.0)
// Volume surge
vol_ratio = avg_volume > 0 ? volume / avg_volume : 1.0
vol_surge = vol_ratio > 1.0 ? math.min((vol_ratio - 1.0) / 1.5, 1.0) : 0.0
// Score composite comme MQL5: 40% ATR compression + 35% accel + 25% vol
spike_prob_mql = 0.40 * atr_compression + 0.35 * accel + 0.25 * vol_surge
spike_prob_mql := math.max(0.0, math.min(1.0, spike_prob_mql))
// Score Pine enrichi (momentum + body + BB squeeze + ST)
momentum = math.abs(close - close[spike_lb]) / (avg_range * spike_lb + 0.0001)
body_score = body_ratio > 0.65 ? 0.4 : body_ratio > 0.45 ? 0.2 : 0.0
st_score_sp = (close > st_line and st_dir == 1) or (close < st_line and st_dir == -1) ? 0.3 : -0.1
bb_sq_score = bb_squeeze ? 0.3 : 0.0
spike_raw = momentum * 0.25 + spike_prob_mql * 0.40 + body_score * 0.15 + bb_sq_score * 0.10 + st_score_sp * 0.10
spike_prob = math.min(math.max(spike_raw, 0.0), 1.0)
spike_bull = close > open and close > vwap_val and st_dir == 1
spike_bear = close < open and close < vwap_val and st_dir == -1
spike_dir_num = spike_bull ? 1.0 : spike_bear ? -1.0 : 0.0
// ── Boom / Crash symbol detection ─────────────────────────────
_ticker_lc = str.lower(syminfo.ticker)
is_boom = str.contains(_ticker_lc, "boom")
is_crash = str.contains(_ticker_lc, "crash")
is_bc = is_boom or is_crash
bc_dir = is_boom ? 1 : is_crash ? -1 : 0
path_show_eff = path_show and (not is_bc or path_show_bc or not ui_compact_bc)
path_candles_eff = path_show_candles and path_show_eff
of_hm_show_eff = of_hm_show and (not is_bc or not ui_compact_bc)
// Frequence spike auto (300/500/600/900/1000 ticks)
_spike_freq_auto =
str.contains(syminfo.ticker, "1000") ? 1000 :
str.contains(syminfo.ticker, "900") ? 900 :
str.contains(syminfo.ticker, "600") ? 600 :
str.contains(syminfo.ticker, "500") ? 500 :
str.contains(syminfo.ticker, "300") ? 300 : 500
spike_freq_bars = spike_freq_ov > 0 ? spike_freq_ov : _spike_freq_auto
// Z-score mouvement (DetectSpike MQL5)
_move_cur = math.abs(close - close[1])
_spike_mean = ta.sma(_move_cur, spike_lb)
_spike_sd = ta.stdev(_move_cur, spike_lb)
_spike_sd := _spike_sd > 0 ? _spike_sd : _spike_mean * 0.1
_body_closed = math.abs(close[1] - open[1])
_body_live = math.abs(close - open)
_spike_move = math.max(_body_closed, _body_live)
spike_z = _spike_sd > 0 ? (_spike_move - _spike_mean) / _spike_sd : 0.0
// Detection spike en cours (barre confirmee)
_body_bar_move = math.abs(close - open)
_spike_bar_now = _body_bar_move >= atr14 * 0.32
_spike_dir_ok = is_boom ? close > open : is_crash ? close < open : false
_spike_z_ok = spike_z >= spike_z_min or _spike_move >= _spike_mean * 2.5
bc_spike_now = spike_bc_en and is_bc and barstate.isconfirmed and _spike_bar_now and _spike_dir_ok
// Cycle inter-spike (barres depuis dernier spike)
var int bars_since_spike = 0
if barstate.isconfirmed
if bc_spike_now
bars_since_spike := 0
else
bars_since_spike += 1
spike_progress = spike_freq_bars > 0 ? math.min(bars_since_spike / spike_freq_bars, 1.0) : 0.0
// Reset cycle si depasse (evaluateSpikeImminence)
if spike_bc_en and is_bc and barstate.isconfirmed and spike_progress >= spike_overdue and not bc_spike_now
bars_since_spike := 0
// Stair score (corps alignes Boom/Crash)
f_stair_count(int isBoomSide, int n) =>
int cnt = 0
for i = 1 to n
float body = close[i] - open[i]
if isBoomSide == 1 and body > 0
cnt += 1
else if isBoomSide == 0 and body < 0
cnt += 1
cnt
_stair_aligned = is_boom ? f_stair_count(1, spike_stair_b) : is_crash ? f_stair_count(0, spike_stair_b) : 0
stair_score = is_bc and spike_stair_b > 0 ? _stair_aligned / spike_stair_b : 0.0
// Pre-spike compression (corps de plus en plus petits)
_body0 = math.abs(close[5] - open[5])
_body1 = math.abs(close[4] - open[4])
_body2 = math.abs(close[3] - open[3])
_body3 = math.abs(close[2] - open[2])
_body4 = math.abs(close[1] - open[1])
_body5 = math.abs(close - open)
_avg_body6 = (_body0 + _body1 + _body2 + _body3 + _body4 + _body5) / 6.0
_is_compressing = _body4 < _body1 and _body5 < _body2
_dir_ok_bodies = is_boom ? (close >= open and close[1] >= open[1] and close[2] >= open[2]) :
is_crash ? (close <= open and close[1] <= open[1] and close[2] <= open[2]) : false
f_pre_spike = _is_compressing and _dir_ok_bodies and _avg_body6 > 0 ? 0.8 :
_is_compressing ? 0.4 : 0.0
// Momentum aligne Boom/Crash
_avg5 = ta.sma(close, 5)
_avg15 = ta.sma(close, 15)
_mom_raw = _avg15 != 0 ? (_avg5 - _avg15) / _avg15 : 0.0
_mom_aligned = is_boom ? _mom_raw > 0 : is_crash ? _mom_raw < 0 : false
f_momentum_bc = _mom_aligned ? math.min(math.abs(_mom_raw) / 0.0015, 1.0) : 0.0
// Acceleration alignee
_prev5 = ta.sma(close, 5)[5]
_prev_mom = _avg15 != 0 ? (_prev5 - _avg15) / _avg15 : 0.0
_accel_raw = _mom_raw - _prev_mom
f_accel_bc = (is_boom ? _accel_raw > 0 : is_crash ? _accel_raw < 0 : false) ?
math.min(math.abs(_accel_raw) / 0.001, 1.0) : 0.0
// Cycle factor (zone optimale 60-80%)
f_cycle = spike_progress >= 0.80 ? 0.2 :
spike_progress >= 0.60 ? 1.0 :
spike_progress >= 0.45 ? 0.6 :
spike_progress >= spike_wake ? 0.2 : 0.0
// BB squeeze factor
_bb_width_pct = bb_mid > 0 ? bb_width / bb_mid : 0.0
f_bb_squeeze_bc = _bb_width_pct < 0.008 ? 1.0 : _bb_width_pct < 0.015 ? 0.5 : 0.0
// Pattern pin bar / engulfing
_rng_p = high - low
_lw_p = math.min(open, close) - low
_up_p = high - math.max(open, close)
_pin_bull = _rng_p > 0 and _lw_p / _rng_p >= 0.55 and candle_body / _rng_p <= 0.35
_pin_bear = _rng_p > 0 and _up_p / _rng_p >= 0.55 and candle_body / _rng_p <= 0.35
_engulf_bull = close > open and close > open[1] and open < close[1]
_engulf_bear = close < open and close < open[1] and open > close[1]
f_pattern_bc = is_boom and (_pin_bull or _engulf_bull) ? 0.8 :
is_crash and (_pin_bear or _engulf_bear) ? 0.8 : 0.0
// Alignement directionnel (ST + VWAP) — pas de dependance verdict
f_st_bc = is_boom and st_dir == 1 and close > vwap_val ? 0.8 :
is_crash and st_dir == -1 and close < vwap_val ? 0.8 :
is_bc ? 0.0 : 0.2
// RSI14 local (evite forward-ref si besoin)
_rsi_bc = ta.rsi(close, 14)
f_rsi_bc = 0.0
if is_boom and _rsi_bc < 30
f_rsi_bc := 1.0
else if is_boom and _rsi_bc < 40
f_rsi_bc := 0.5
else if is_crash and _rsi_bc > 70
f_rsi_bc := 1.0
else if is_crash and _rsi_bc > 60
f_rsi_bc := 0.5
else if _rsi_bc >= 40 and _rsi_bc <= 60
f_rsi_bc := 0.1
_rsi_entry_ok = is_boom ? _rsi_bc < 65 : is_crash ? _rsi_bc > 46 : false
// Score composite predicteur (modele deriv_ea_pro)
_bc_raw =
f_cycle * 0.22 +
f_momentum_bc * 0.15 +
f_accel_bc * 0.08 +
atr_comp_deep * 0.08 +
f_rsi_bc * 0.10 +
f_bb_squeeze_bc * 0.06 +
f_pattern_bc * 0.06 +
f_st_bc * 0.10 +
f_pre_spike * 0.10 +
spike_prob_mql* 0.05
spike_pred_prob = is_bc ? math.round(math.min(_bc_raw / 0.7, 1.0) * 100) : math.round(spike_prob * 100)
// Imminence MQL5 simplifiee (0-100) — bonus GOM ajoute apres calcul verdict
_imminence_cnt = spike_freq_bars > 0 and spike_progress >= 0.25 ?
math.min((spike_progress - 0.25) / 0.75, 1.0) * 30.0 : 0.0
_imminence_comp = atr_comp_deep * 20.0
_imminence_stair = stair_score * 15.0
_imminence_rsi = is_boom ? (_rsi_bc <= 35 ? 10.0 : _rsi_bc < 45 ? (45 - _rsi_bc) / 10.0 * 10.0 : 0.0) :
is_crash ? (_rsi_bc >= 65 ? 10.0 : _rsi_bc > 55 ? (_rsi_bc - 55) / 10.0 * 10.0 : 0.0) : 0.0
float spike_imminence_base = is_bc ? math.min(100.0, _imminence_cnt + _imminence_comp + _imminence_stair + _imminence_rsi + spike_pred_prob * 0.15) : spike_prob * 100
float spike_imminence = spike_imminence_base
// Niveau spike: 0=none 1=early 2=watch 3=imminent 4=spike
int spike_level_num = 0
bool spike_tradable = false
string spike_level_txt = "—"
string spike_reason_txt = ""
if spike_bc_en and is_bc
if bc_spike_now
spike_level_num := 4
spike_level_txt := "SPIKE"
spike_reason_txt := is_boom ? "Spike Boom en cours" : "Spike Crash en cours"
else if spike_progress < spike_wake
spike_level_num := 0
spike_level_txt := "ATTENTE"
spike_reason_txt := "Cycle " + str.tostring(math.round(spike_progress * 100)) + "% — reveil a " + str.tostring(math.round(spike_wake * 100)) + "%"
else if spike_progress >= spike_entry and _rsi_entry_ok
spike_tradable := spike_pred_prob >= spike_min_prob
spike_level_num := 3
spike_level_txt := spike_tradable ? "IMMINENT" : "IMMINENT?"
spike_reason_txt := spike_tradable ?
"Fenetre " + (is_boom ? "BUY" : "SELL") + " " + str.tostring(math.round(spike_progress * 100)) + "% — prob " + str.tostring(spike_pred_prob) + "%" :
"Prob " + str.tostring(spike_pred_prob) + "% < " + str.tostring(spike_min_prob) + "%"
else if spike_progress >= spike_watch
spike_level_num := 2
spike_level_txt := "WATCH"
spike_reason_txt := "Approche spike " + str.tostring(math.round(spike_progress * 100)) + "% — prob " + str.tostring(spike_pred_prob) + "%"
else
spike_level_num := 1
spike_level_txt := "EARLY"
spike_reason_txt := "Cycle " + str.tostring(math.round(spike_progress * 100)) + "% — attente " + str.tostring(math.round(spike_watch * 100)) + "%"
// Couleurs chart
color _bc_bar_col = na
if spike_level_num == 4
_bc_bar_col := is_boom ? color.new(BRAND_ACCENT, 20) : color.new(BRAND_ACCENT, 20)
else if spike_level_num == 3 and spike_tradable
_bc_bar_col := color.new(BRAND_SECONDARY, 30)
else if spike_level_num == 2
_bc_bar_col := color.new(WAIT, 55)
else if spike_prob >= spike_blink
_bc_bar_col := color.new(BRAND_SECONDARY, 50)
barcolor(is_bc and spike_bc_en and not na(_bc_bar_col) ? _bc_bar_col :
spike_prob >= spike_min and spike_bull ? color.new(BULL, 30) :
spike_prob >= spike_min and spike_bear ? color.new(BEAR, 30) :
spike_prob >= spike_blink ? color.new(BRAND_SECONDARY, 50) : na)
// Labels spike (désactivés si flèches clignotantes actives)
var label spike_sig_lbl = na
if spike_show_lbl and not ui_show_arrow and spike_bc_en and barstate.isconfirmed
if not na(spike_sig_lbl)
label.delete(spike_sig_lbl)
spike_sig_lbl := na
if is_boom and spike_level_num == 3 and spike_tradable
spike_sig_lbl := label.new(bar_index, low, "PRE-SPIKE ▲",
style=label.style_label_up, color=color.new(BULL, 15),
textcolor=color.white, size=size.large)
else if is_crash and spike_level_num == 3 and spike_tradable
spike_sig_lbl := label.new(bar_index, high, "PRE-SPIKE ▼",
style=label.style_label_down, color=color.new(BEAR, 15),
textcolor=color.white, size=size.large)
else if bc_spike_now
spike_sig_lbl := label.new(bar_index, high, "SPIKE",
style=label.style_label_down, color=color.new(BRAND_ACCENT, 15),
textcolor=color.white, size=size.normal)
bgcolor(spike_bc_en and is_bc and spike_level_num == 3 and spike_tradable ? color.new(BRAND_SECONDARY, 92) : na)
// ═══════════════════════════════════════════════════════════════
// EMA RIBBON (scoring comme MQL5)
// ═══════════════════════════════════════════════════════════════
ema9 = ta.ema(close, 9)
ema13 = ta.ema(close, 13)
ema21 = ta.ema(close, 21)
ema50 = ta.ema(close, 50)
ema_above_count = (close > ema9 ? 1 : 0) + (close > ema13 ? 1 : 0) + (close > ema21 ? 1 : 0) + (close > ema50 ? 1 : 0)
// ═══════════════════════════════════════════════════════════════
// SCORE VERDICT ENRICHI (porte du MQL5)
// ═══════════════════════════════════════════════════════════════
rsi14 = ta.rsi(close, 14)
macd_line = ta.ema(close, 12) - ta.ema(close, 26)
macd_sig = ta.ema(macd_line, 9)
adx_val = ta.rma(math.abs(ta.change(high) - ta.change(low)), 14)
score_buy = 0.0
score_sell = 0.0
// Base scores (meme que avant)
score_buy += st_dir == 1 ? 1.5 : 0.0
score_sell += st_dir == -1 ? 1.5 : 0.0
score_buy += close > vwap_val ? 1.0 : 0.0
score_sell += close < vwap_val ? 1.0 : 0.0
score_buy += close > bb_mid ? 0.5 : 0.0
score_sell += close < bb_mid ? 0.5 : 0.0
score_buy += rsi14 > 50 and rsi14 < 70 ? 1.0 : rsi14 <= 35 ? 0.5 : 0.0
score_sell += rsi14 < 50 and rsi14 > 30 ? 1.0 : rsi14 >= 65 ? 0.5 : 0.0
score_buy += macd_line > macd_sig ? 0.8 : 0.0
score_sell += macd_line < macd_sig ? 0.8 : 0.0
score_buy += not na(ob_bull_bot) and close >= ob_bull_bot and close <= ob_bull_top * 1.003 ? 1.5 : 0.0
score_sell += not na(ob_bear_top) and close <= ob_bear_top and close >= ob_bear_bot * 0.997 ? 1.5 : 0.0
score_buy += spike_prob >= spike_min and spike_bull ? 2.0 : 0.0
score_sell += spike_prob >= spike_min and spike_bear ? 2.0 : 0.0
score_buy += spike_bc_en and is_boom and spike_level_num >= 2 and spike_pred_prob >= 50 ? 1.5 : 0.0
score_sell += spike_bc_en and is_crash and spike_level_num >= 2 and spike_pred_prob >= 50 ? 1.5 : 0.0
score_buy += spike_bc_en and is_boom and spike_tradable ? 2.5 : 0.0
score_sell += spike_bc_en and is_crash and spike_tradable ? 2.5 : 0.0
// BB/VWAP bias (du MQL5: GOM_AddBollingerVwapVerdictBias)
vwap_dist_pct = vwap_val > 0 ? (close - vwap_val) / vwap_val : 0.0
vwap_mag = math.min(1.0, math.abs(vwap_dist_pct) / 0.0025)
score_buy += vwap_dist_pct > 0.00025 ? 0.24 * verdict_bb_vwap_weight * vwap_mag : 0.0
score_sell += vwap_dist_pct < -0.00025 ? 0.24 * verdict_bb_vwap_weight * vwap_mag : 0.0
score_buy += bb_pctb < 0.22 ? 0.16 * verdict_bb_vwap_weight : 0.0
score_sell += bb_pctb > 0.78 ? 0.16 * verdict_bb_vwap_weight : 0.0
score_buy += bb_squeeze ? 0.06 * verdict_bb_vwap_weight : 0.0
score_sell += bb_squeeze ? 0.06 * verdict_bb_vwap_weight : 0.0
// Advanced indicators bias (du MQL5: GOM_AddAdvancedIndicatorsVerdictBias)
// Supertrend direction
st_adv = st_dir == 1 ? 0.20 * verdict_adv_weight : st_dir == -1 ? 0.0 : 0.0
score_buy += st_dir == 1 ? 0.20 * verdict_adv_weight : 0.0
score_sell += st_dir == -1 ? 0.20 * verdict_adv_weight : 0.0
// Keltner position
score_buy += kc_pos > 0.10 ? 0.22 * verdict_adv_weight * math.min(1.0, math.abs(kc_pos)) : 0.0
score_sell += kc_pos < -0.10 ? 0.22 * verdict_adv_weight * math.min(1.0, math.abs(kc_pos)) : 0.0
// Donchian signal
score_buy += dc_sig > 0 ? 0.24 * verdict_adv_weight : 0.0
score_sell += dc_sig < 0 ? 0.24 * verdict_adv_weight : 0.0
// EMA Ribbon bias (du MQL5: GOM_AddEmaRibbonScriptTfBias)
score_buy += ema_above_count * 0.15
score_sell += (4 - ema_above_count) * 0.15
score_buy += ema_above_count >= 4 ? 0.25 : 0.0
score_sell += ema_above_count <= 0 ? 0.25 : 0.0
// KOLA proximity bonus
kola_near_buy = not na(kola_buy_level) and math.abs(close - kola_buy_level) <= atr14 * 1.5
kola_near_sell = not na(kola_sell_level) and math.abs(close - kola_sell_level) <= atr14 * 1.5
score_buy += kola_near_buy ? 1.5 : 0.0
score_sell += kola_near_sell ? 1.5 : 0.0
// SIDO bonus
score_sell += not na(sido_dt_level) and close >= sido_dt_level * 0.998 ? 1.2 : 0.0
score_buy += not na(sido_db_level) and close <= sido_db_level * 1.002 ? 1.2 : 0.0
// BOS/CHoCH influence (du MQL5 SMC)
score_buy += bos_bull ? 1.38 : 0.0
score_sell += bos_bear ? 1.38 : 0.0
score_buy -= bos_bear ? 0.58 : 0.0
score_sell -= bos_bull ? 0.58 : 0.0
// Verdict gap et classification
verdict_gap = math.abs(score_buy - score_sell)
// Coherence gate (du MQL5: VerdictCoherenceGateEnabled)
filter_count = 6.0
pc1 = st_dir == 1 and score_buy > score_sell ? 1.0 : st_dir == -1 and score_sell > score_buy ? 1.0 : 0.0
pc2 = close > vwap_val and score_buy > score_sell ? 1.0 : close < vwap_val and score_sell > score_buy ? 1.0 : 0.0
pc3 = macd_line > macd_sig and score_buy > score_sell ? 1.0 : macd_line < macd_sig and score_sell > score_buy ? 1.0 : 0.0
pc4 = rsi14 > 50 and score_buy > score_sell ? 1.0 : rsi14 < 50 and score_sell > score_buy ? 1.0 : 0.0
pc5 = kc_pos > 0 and score_buy > score_sell ? 1.0 : kc_pos < 0 and score_sell > score_buy ? 1.0 : 0.0
pc6 = dc_sig > 0 and score_buy > score_sell ? 1.0 : dc_sig < 0 and score_sell > score_buy ? 1.0 : 0.0
pass_count = pc1 + pc2 + pc3 + pc4 + pc5 + pc6
filter_ratio = pass_count / filter_count
coherence_ok = not verdict_coherence or filter_ratio >= 0.40 or verdict_gap >= (verdict_gap_th + 0.24)
// ── VÉRIFICATION COHÉRENCE AVEC DIRECTION RÉELLE DU PRIX ──
// Calculer direction réelle du prix sur 5 bougies
price_change_5b = close - close[4]
avg_price_5b = (close + close[4]) / 2.0
price_direction = avg_price_5b > 0 ? price_change_5b / avg_price_5b : 0.0
// Seuil de rejet: 0.001 (0.1%) de mouvement
price_strong_up = price_direction > 0.001
price_strong_down = price_direction < -0.001
// Niveaux de conviction (avec vérification cohérence prix)
is_perfect_sell = score_sell > score_buy and verdict_gap >= 4.0 and coherence_ok and not price_strong_up
is_good_sell = score_sell > score_buy and verdict_gap >= 2.5 and not is_perfect_sell and coherence_ok and not price_strong_up
is_sell = score_sell > score_buy and verdict_gap >= 1.2 and not is_good_sell and not is_perfect_sell and coherence_ok and not price_strong_up
is_perfect_buy = score_buy > score_sell and verdict_gap >= 4.0 and coherence_ok and not price_strong_down
is_good_buy = score_buy > score_sell and verdict_gap >= 2.5 and not is_perfect_buy and coherence_ok and not price_strong_down
is_buy = score_buy > score_sell and verdict_gap >= 1.2 and not is_good_buy and not is_perfect_buy and coherence_ok and not price_strong_down
verdict_num = is_perfect_sell ? -3 : is_good_sell ? -2 : is_sell ? -1 : is_perfect_buy ? 3 : is_good_buy ? 2 : is_buy ? 1 : 0
verdict_txt = is_perfect_sell ? "PERFECT SELL" :
is_good_sell ? "GOOD SELL" :
is_sell ? "SELL" :
is_perfect_buy ? "PERFECT BUY" :
is_good_buy ? "GOOD BUY" :
is_buy ? "BUY" : "WAIT"
verdict_col = str.contains(verdict_txt, "SELL") ? color.new(BEAR, 0) :
str.contains(verdict_txt, "BUY") ? color.new(BULL, 0) : color.new(WAIT, 0)
rsi_oversold = rsi14 < 28
rsi_overbought = rsi14 > 72
bgcolor(show_verdict ? (is_buy or is_good_buy or is_perfect_buy ? color.new(BULL, 97) :
is_sell or is_good_sell or is_perfect_sell ? color.new(BEAR, 97) : na) : na)
// ═══════════════════════════════════════════════════════════════
// ENTRY QUALITY SCORE (du MQL5: GOM_ComputeEntryQualityScore)
// ═══════════════════════════════════════════════════════════════
dir_for_eq = score_buy > score_sell ? "BUY" : score_sell > score_buy ? "SELL" : "WAIT"
// Gap normalisé
gap_n = 0.0
if dir_for_eq == "BUY" and verdict_gap > verdict_gap_th
gap_n := math.min(1.0, (verdict_gap - verdict_gap_th) / (verdict_gap_th * 2.5))
if dir_for_eq == "SELL" and verdict_gap > verdict_gap_th
gap_n := math.min(1.0, (verdict_gap - verdict_gap_th) / (verdict_gap_th * 2.5))
// Spike aligné
spike_aligned = (dir_for_eq == "BUY" and spike_bull) or (dir_for_eq == "SELL" and spike_bear)
sp_n = spike_aligned ? math.min(1.0, spike_prob / 0.72) : spike_prob > 0 ? 0.14 * math.min(1.0, spike_prob / 0.55) : 0.0
// Level confluence (KOLA proximity)
lc_n = 0.0
if dir_for_eq == "BUY" and kola_near_buy
lc_n := 0.8
if dir_for_eq == "SELL" and kola_near_sell
lc_n := 0.8
// Filter quality
fq_n = filter_ratio
// Score composite
entry_quality = 0.20 * gap_n + 0.30 * sp_n + 0.24 * lc_n + 0.16 * fq_n + 0.10 * filter_ratio
entry_quality := math.max(0.0, math.min(1.0, entry_quality))
// Bonus GOM sur imminence spike (apres verdict)
_gom_spike_bonus = 0.0
if spike_bc_en and is_boom and score_buy > score_sell and verdict_gap >= 1.2
_gom_spike_bonus := math.min(12.0, verdict_gap * 2.0)
else if spike_bc_en and is_crash and score_sell > score_buy and verdict_gap >= 1.2
_gom_spike_bonus := math.min(12.0, verdict_gap * 2.0)
spike_imminence := math.min(100.0, spike_imminence_base + _gom_spike_bonus)
if spike_bc_en and is_bc and spike_level_num == 3
float _prob_adj = spike_pred_prob + _gom_spike_bonus * 0.5
spike_tradable := _prob_adj >= spike_min_prob and _rsi_entry_ok
// Niveaux entree spike Boom/Crash (pour tableau SETUP)
float spike_entry_px = na
float spike_sl_px = na
float spike_tp_px = na
if spike_bc_en and is_bc and spike_tradable and not bc_spike_now
spike_entry_px := close
spike_sl_px := is_boom ? close - atr14 * spike_sl_atr : close + atr14 * spike_sl_atr
spike_tp_px := is_boom ? close + atr14 * spike_tp_atr : close - atr14 * spike_tp_atr
// ── SETUP (OB ou PRE-SPIKE) — calculé avant affichage / webhook ──
_rng_s = high - low
_body_s = math.abs(close - open)
_lw_s = math.min(open, close) - low
_up_s = high - math.max(open, close)
pin_bull = _rng_s > 0 and _lw_s / _rng_s >= 0.52 and _body_s / _rng_s <= 0.38 and close > open
pin_bear = _rng_s > 0 and _up_s / _rng_s >= 0.52 and _body_s / _rng_s <= 0.38 and close < open
var string setup_type = ""
var string setup_confirm = ""
var int setup_dir = 0
var float setup_entry = na
var float setup_sl = na
var float setup_tp1 = na
var float setup_tp2 = na
var float setup_rr = na
setup_type := ""
setup_confirm := ""
setup_dir := 0
setup_entry := na
setup_sl := na
setup_tp1 := na
setup_tp2 := na
setup_rr := na
if spike_bc_en and is_bc and spike_tradable and not bc_spike_now and not na(spike_entry_px)
setup_type := is_boom ? "SPIKE_BOOM" : "SPIKE_CRASH"
setup_confirm := spike_level_txt
setup_dir := bc_dir
setup_entry := spike_entry_px
setup_sl := spike_sl_px
setup_tp1 := spike_tp_px
setup_tp2 := is_boom ? spike_entry_px + atr14 * spike_tp_atr * 1.5 : spike_entry_px - atr14 * spike_tp_atr * 1.5
setup_rr := spike_sl_atr > 0 ? spike_tp_atr / spike_sl_atr : 0.0
else if not na(ob_bull_bot) and not na(ob_bull_top) and score_buy >= score_sell and verdict_gap >= 0.8
setup_type := "OB_BULL"
setup_confirm := pin_bull ? "PIN_BAR_BULL" : "—"
setup_dir := 1
setup_entry := ob_bull_top
setup_sl := ob_bull_bot - atr14 * 0.12
float risk_b = setup_entry - setup_sl
if risk_b > syminfo.mintick * 10
setup_tp1 := setup_entry + risk_b * setup_rr_mult
setup_tp2 := setup_entry + risk_b * setup_rr2_mult
setup_rr := setup_rr_mult
else if not na(ob_bear_top) and not na(ob_bear_bot) and score_sell > score_buy and verdict_gap >= 0.8
setup_type := "OB_BEAR"
setup_confirm := pin_bear ? "PIN_BAR_BEAR" : "—"
setup_dir := -1
setup_entry := ob_bear_bot
setup_sl := ob_bear_top + atr14 * 0.12
float risk_s = setup_sl - setup_entry
if risk_s > syminfo.mintick * 10
setup_tp1 := setup_entry - risk_s * setup_rr_mult
setup_tp2 := setup_entry - risk_s * setup_rr2_mult
setup_rr := setup_rr_mult
// Override verdict quand pre-spike tradable → signal clair pour EA / alertes
// Condition: GOM doit déjà confirmer la direction (≥ BUY/SELL) — pas de promotion depuis WAIT
bool _gom_confirms_bc = (bc_dir == 1 and verdict_num > 0) or (bc_dir == -1 and verdict_num < 0)
if spike_bc_en and is_bc and spike_tradable and not bc_spike_now and _gom_confirms_bc
verdict_num := bc_dir == 1 ? 2 : -2
verdict_txt := bc_dir == 1 ? "PRE-SPIKE BUY" : "PRE-SPIKE SELL"
verdict_col := bc_dir == 1 ? color.new(BULL, 0) : color.new(BEAR, 0)
// Flèches clignotantes pre-spike (2 états = effet blink)
_spike_blink_on = bar_index % 2 == 0
_spike_arr_up = ui_show_arrow and spike_bc_en and is_bc and spike_tradable and is_boom and not bc_spike_now
_spike_arr_dn = ui_show_arrow and spike_bc_en and is_bc and spike_tradable and is_crash and not bc_spike_now
plotshape(_spike_arr_up and _spike_blink_on, title="PRE-SPIKE BUY ON", style=shape.triangleup,
location=location.belowbar, color=color.new(BULL, 0), size=size.huge)
plotshape(_spike_arr_up and not _spike_blink_on, title="PRE-SPIKE BUY OFF", style=shape.triangleup,
location=location.belowbar, color=color.new(BULL, 78), size=size.large)
plotshape(_spike_arr_dn and _spike_blink_on, title="PRE-SPIKE SELL ON", style=shape.triangledown,
location=location.abovebar, color=color.new(BEAR, 0), size=size.huge)
plotshape(_spike_arr_dn and not _spike_blink_on, title="PRE-SPIKE SELL OFF", style=shape.triangledown,
location=location.abovebar, color=color.new(BEAR, 78), size=size.large)
// ═══════════════════════════════════════════════════════════════
// TABLEAU MTF DIRECTION — top_right
// ═══════════════════════════════════════════════════════════════
get_dir(tf) =>
[_ef, _es, _eh, _c, _rsi, _atr, _hl2] = request.security(syminfo.tickerid, tf,
[ta.ema(close,9), ta.ema(close,21), ta.ema(close,50), close, ta.rsi(close,14), ta.atr(10), hl2],
lookahead=barmerge.lookahead_off)
_st_bull = _c > (_hl2 + 3.0 * _atr)
_bull = (_ef > _es ? 1 : 0) + (_c > _eh ? 1 : 0) + (_rsi > 52 ? 1 : 0) + (_st_bull ? 1 : 0)
_bear = (_ef < _es ? 1 : 0) + (_c < _eh ? 1 : 0) + (_rsi < 48 ? 1 : 0) + (not _st_bull ? 1 : 0)
_dir = _bull >= 3 ? 1 : _bear >= 3 ? -1 : 0
[_dir, _rsi]
[d1, r1] = get_dir(tf1)
[d2, r2] = get_dir(tf2)
[d3, r3] = get_dir(tf3)
[d4, r4] = get_dir(tf4)
[d5, r5] = get_dir(tf5)
[d6, r6] = get_dir(tf6)
[d7, r7] = get_dir(tf7)
tb = (d1==1?1:0)+(d2==1?1:0)+(d3==1?1:0)+(d4==1?1:0)+(d5==1?1:0)+(d6==1?1:0)+(d7==1?1:0)
ts = (d1==-1?1:0)+(d2==-1?1:0)+(d3==-1?1:0)+(d4==-1?1:0)+(d5==-1?1:0)+(d6==-1?1:0)+(d7==-1?1:0)
gd = tb >= 5 ? 1 : ts >= 5 ? -1 : tb > ts ? 1 : ts > tb ? -1 : 0
tf_lbl(tf) =>
tf == "1" ? "M1" : tf == "3" ? "M3" : tf == "5" ? "M5" : tf == "15" ? "M15" : tf == "30" ? "M30" : tf == "60" ? "H1" : tf == "120" ? "H2" : tf == "240" ? "H4" : tf == "D" ? "D1" : tf == "W" ? "W1" : tf == "M" ? "MN" : tf
dir_txt(d) => d==1 ? "BULL" : d==-1 ? "BEAR" : "NEUT"
dir_col(d) => d==1 ? color.new(BULL, 0) : d==-1 ? color.new(BEAR, 0) : color.new(WAIT, 0)
bg_cell(d) => d==1 ? color.new(BULL,85) : d==-1 ? color.new(BEAR,85) : BG_CELL
var table mtf_tbl = table.new(position.top_left, 3, 10,
bgcolor=BG_MAIN, border_color=BORDER,
border_width=1, frame_color=BORDER, frame_width=2)
if show_mtf and barstate.islast
table.cell(mtf_tbl,0,0,"TF", text_color=color.new(BRAND_PRIMARY, 0), text_size=size.small, bgcolor=BG_HEADER)
table.cell(mtf_tbl,1,0,"DIR", text_color=color.new(BRAND_PRIMARY, 0), text_size=size.small, bgcolor=BG_HEADER)
table.cell(mtf_tbl,2,0,"RSI", text_color=color.new(BRAND_PRIMARY, 0), text_size=size.small, bgcolor=BG_HEADER)
tfs = array.from(tf1,tf2,tf3,tf4,tf5,tf6,tf7)
dirs = array.from(d1,d2,d3,d4,d5,d6,d7)
rsis = array.from(r1,r2,r3,r4,r5,r6,r7)
for i = 0 to 6
_tf = array.get(tfs,i)
_d = array.get(dirs,i)
_r = array.get(rsis,i)
_bg = bg_cell(_d)
table.cell(mtf_tbl,0,i+1, tf_lbl(_tf), text_color=TEXT_PRIMARY, text_size=size.small, bgcolor=_bg)
table.cell(mtf_tbl,1,i+1, dir_txt(_d), text_color=dir_col(_d), text_size=size.small, bgcolor=_bg)
table.cell(mtf_tbl,2,i+1, str.tostring(math.round(_r)),
text_color=_r>60?color.new(BULL,0):_r<40?color.new(BEAR,0):TEXT_SECONDARY, text_size=size.small, bgcolor=_bg)
_gbg = gd==1?color.new(BULL,60):gd==-1?color.new(BEAR,60):color.new(WAIT,70)
_gtxt = gd==1?"BULL "+str.tostring(tb)+"B":gd==-1?"BEAR "+str.tostring(ts)+"S":"MIXED"
table.cell(mtf_tbl,0,8,"GLOBAL", text_color=TEXT_PRIMARY, text_size=size.small, bgcolor=_gbg)
table.cell(mtf_tbl,1,8,_gtxt, text_color=gd==1?color.new(BULL,0):gd==-1?color.new(BEAR,0):color.new(WAIT,0), text_size=size.small, bgcolor=_gbg)
table.cell(mtf_tbl,2,8,str.tostring(7-tb-ts)+" N", text_color=color.new(WAIT,0), text_size=size.small, bgcolor=_gbg)
// ═══════════════════════════════════════════════════════════════
// PANNEAU SIGNAL UNIFIÉ — bottom_left (Boom/Crash)
// ═══════════════════════════════════════════════════════════════
// SIGNAL PANEL SUPPRIMÉ — utilise le label GHOST à la place
// ═══════════════════════════════════════════════════════════════
// TABLEAU VERDICT GOM — bottom_left (classique, si panneau unifié off)
// ═══════════════════════════════════════════════════════════════
var table vtbl = table.new(position.bottom_left, 2, 12,
bgcolor=BG_MAIN,
border_color=BORDER, border_width=1, frame_width=1)
if show_verdict and barstate.islast
table.cell(vtbl,0,0,"GOM VERDICT", text_color=color.new(BRAND_PRIMARY, 0), text_size=size.small, bgcolor=BG_HEADER)
table.cell(vtbl,1,0, verdict_txt, text_color=verdict_col, text_size=size.normal, bgcolor=BG_HEADER)
table.cell(vtbl,0,1,"BUY score", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(vtbl,1,1, str.tostring(math.round(score_buy *10)/10), text_color=color.new(BULL, 0), text_size=size.small)
table.cell(vtbl,0,2,"SELL score", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(vtbl,1,2, str.tostring(math.round(score_sell *10)/10), text_color=color.new(BEAR, 0), text_size=size.small)
table.cell(vtbl,0,3,"Spike %", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(vtbl,1,3, str.tostring(math.round(spike_prob*100))+"%",
text_color=spike_prob>=spike_min?color.new(BRAND_ACCENT,0):TEXT_MUTED, text_size=size.small)
table.cell(vtbl,0,4,"RSI / ST", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(vtbl,1,4, str.tostring(math.round(rsi14))+" / "+(st_dir==1?"UP":"DN"),
text_color=st_dir==1?color.new(BULL,0):color.new(BEAR,0), text_size=size.small)
table.cell(vtbl,0,5,"RSI Alert", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(vtbl,1,5, rsi_oversold ? "SURVENTE!" : rsi_overbought ? "SURACHAT!" : "normal",
text_color=rsi_oversold ? color.new(BRAND_SECONDARY,0) : rsi_overbought ? color.new(BRAND_SECONDARY,0) : TEXT_MUTED,
text_size=size.small)
table.cell(vtbl,0,6,"Force", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(vtbl,1,6, str.tostring(math.round(verdict_gap*10)/10)+" pts",
text_color=verdict_gap>=4?color.new(BRAND_ACCENT,0):verdict_gap>=2.5?color.new(BULL,0):TEXT_SECONDARY,
text_size=size.small)
// Nouvelles lignes
table.cell(vtbl,0,7,"Coherence", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(vtbl,1,7, str.tostring(math.round(filter_ratio*100))+"%",
text_color=filter_ratio>=0.6?color.new(BULL,0):filter_ratio>=0.4?color.new(WAIT,0):color.new(BEAR,0),
text_size=size.small)
table.cell(vtbl,0,8,"Quality", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(vtbl,1,8, str.tostring(math.round(entry_quality*100))+"%",
text_color=entry_quality>=0.6?color.new(BULL,0):entry_quality>=0.3?color.new(WAIT,0):color.new(BEAR,0),
text_size=size.small)
table.cell(vtbl,0,9,"KOLA", text_color=TEXT_SECONDARY, text_size=size.small)
_kola_txt = kola_near_buy ? "NEAR BUY" : kola_near_sell ? "NEAR SELL" : "---"
_kola_col = kola_near_buy ? color.new(BULL, 0) : kola_near_sell ? color.new(BEAR, 0) : TEXT_MUTED
table.cell(vtbl,1,9, _kola_txt, text_color=_kola_col, text_size=size.small)
table.cell(vtbl,0,10,"SIDO", text_color=TEXT_SECONDARY, text_size=size.small)
_sido_txt = not na(sido_dt_level) ? "DT " + str.tostring(math.round(sido_dt_level, 2)) : not na(sido_db_level) ? "DB " + str.tostring(math.round(sido_db_level, 2)) : "---"
_sido_col = not na(sido_dt_level) ? color.new(BEAR, 0) : not na(sido_db_level) ? color.new(BULL, 0) : TEXT_MUTED
table.cell(vtbl,1,10, _sido_txt, text_color=_sido_col, text_size=size.small)
table.cell(vtbl,0,11,"vnum", text_color=TEXT_SECONDARY, text_size=size.tiny, bgcolor=BG_CELL)
table.cell(vtbl,1,11, str.tostring(verdict_num),
text_color=verdict_num>=3?color.new(BRAND_ACCENT,0):verdict_num>0?color.new(BULL,0):verdict_num<0?color.new(BEAR,0):TEXT_MUTED,
text_size=size.tiny, bgcolor=BG_CELL)
// ═══════════════════════════════════════════════════════════════
// TABLEAU PRE-SPIKE BOOM/CRASH — top_center
// ═══════════════════════════════════════════════════════════════
var table spike_tbl = table.new(position.bottom_right, 2, 8,
bgcolor=BG_MAIN, border_color=color.new(BRAND_SECONDARY, 40),
border_width=1, frame_color=color.new(BRAND_SECONDARY, 30), frame_width=2)
_spike_hdr_col = spike_level_num == 4 ? color.new(BRAND_ACCENT, 30) :
spike_level_num == 3 and spike_tradable ? color.new(BRAND_SECONDARY, 20) :
spike_level_num == 2 ? color.new(WAIT, 50) :
is_boom ? color.new(BULL, 70) : is_crash ? color.new(BEAR, 70) : BG_CELL
if show_verdict and spike_show_bc and barstate.islast and spike_bc_en and is_bc
table.cell(spike_tbl, 0, 0, "PRE-SPIKE", text_color=color.new(BRAND_SECONDARY, 0), text_size=size.small, bgcolor=_spike_hdr_col)
table.cell(spike_tbl, 1, 0, is_boom ? "BOOM ▲" : "CRASH ▼",
text_color=is_boom ? color.new(BULL, 0) : color.new(BEAR, 0), text_size=size.normal, bgcolor=_spike_hdr_col)
table.cell(spike_tbl, 0, 1, "Niveau", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(spike_tbl, 1, 1, spike_level_txt,
text_color=spike_level_num == 3 and spike_tradable ? color.new(BRAND_SECONDARY, 0) : spike_level_num == 4 ? color.new(BRAND_ACCENT, 0) : TEXT_PRIMARY,
text_size=size.small)
table.cell(spike_tbl, 0, 2, "Cycle", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(spike_tbl, 1, 2, str.tostring(bars_since_spike) + "/" + str.tostring(spike_freq_bars) +
" (" + str.tostring(math.round(spike_progress * 100)) + "%)",
text_color=spike_progress >= spike_entry ? color.new(BRAND_SECONDARY, 0) : TEXT_MUTED, text_size=size.small)
table.cell(spike_tbl, 0, 3, "Prob / Imm", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(spike_tbl, 1, 3, str.tostring(spike_pred_prob) + "% / " + str.tostring(math.round(spike_imminence)) + "%",
text_color=spike_pred_prob >= spike_min_prob ? color.new(BULL, 0) : TEXT_MUTED, text_size=size.small)
table.cell(spike_tbl, 0, 4, "Z-score", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(spike_tbl, 1, 4, str.tostring(math.round(spike_z * 10) / 10),
text_color=spike_z >= spike_z_min ? color.new(BRAND_ACCENT, 0) : TEXT_MUTED, text_size=size.small)
table.cell(spike_tbl, 0, 5, "Pre-compress", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(spike_tbl, 1, 5, str.tostring(math.round(f_pre_spike * 100)) + "%",
text_color=f_pre_spike >= 0.4 ? color.new(BRAND_SECONDARY, 0) : TEXT_MUTED, text_size=size.small)
table.cell(spike_tbl, 0, 6, "Tradable", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(spike_tbl, 1, 6, spike_tradable ? "OUI — ENTREE" : bc_spike_now ? "NON (spike)" : "NON",
text_color=spike_tradable ? color.new(BULL, 0) : TEXT_MUTED, text_size=size.small)
table.cell(spike_tbl, 0, 7, "Info", text_color=TEXT_SECONDARY, text_size=size.tiny)
table.cell(spike_tbl, 1, 7, spike_reason_txt, text_color=TEXT_MUTED, text_size=size.tiny)
// ═══════════════════════════════════════════════════════════════
// TABLEAU SETUP — déjà calculé plus haut (OB / PRE-SPIKE)
// ═══════════════════════════════════════════════════════════════
var table setup_tbl = table.new(position.bottom_center, 2, 7,
bgcolor=BG_MAIN, border_color=BORDER, border_width=1)
if show_setup and barstate.islast
color hdr = setup_dir == 1 ? color.new(BULL, 30) : setup_dir == -1 ? color.new(BEAR, 30) : BG_CELL
table.cell(setup_tbl, 0, 0, "SETUP", text_color=color.new(BRAND_PRIMARY, 0), text_size=size.small, bgcolor=hdr)
table.cell(setup_tbl, 1, 0, setup_type != "" ? setup_type : "—", text_color=setup_dir==1?color.new(BULL,0):setup_dir==-1?color.new(BEAR,0):TEXT_MUTED, text_size=size.normal, bgcolor=hdr)
table.cell(setup_tbl, 0, 1, "Confirm", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(setup_tbl, 1, 1, setup_confirm, text_color=pin_bull or pin_bear ? color.new(BULL, 0) : TEXT_MUTED, text_size=size.small)
table.cell(setup_tbl, 0, 2, "Entry", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(setup_tbl, 1, 2, na(setup_entry) ? "—" : str.tostring(setup_entry, format.mintick), text_color=TEXT_PRIMARY, text_size=size.small)
table.cell(setup_tbl, 0, 3, "SL", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(setup_tbl, 1, 3, na(setup_sl) ? "—" : str.tostring(setup_sl, format.mintick), text_color=color.new(BEAR, 0), text_size=size.small)
table.cell(setup_tbl, 0, 4, "TP1", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(setup_tbl, 1, 4, na(setup_tp1) ? "—" : str.tostring(setup_tp1, format.mintick), text_color=color.new(BULL, 0), text_size=size.small)
table.cell(setup_tbl, 0, 5, "TP2", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(setup_tbl, 1, 5, na(setup_tp2) ? "—" : str.tostring(setup_tp2, format.mintick), text_color=color.new(BRAND_PRIMARY, 0), text_size=size.small)
table.cell(setup_tbl, 0, 6, "R/R", text_color=TEXT_SECONDARY, text_size=size.small)
table.cell(setup_tbl, 1, 6, na(setup_rr) ? "—" : str.tostring(setup_rr, "#.##"), text_color=color.new(BRAND_SECONDARY, 0), text_size=size.small)
// ═══════════════════════════════════════════════════════════════
// CHEMIN PREDICTIF — 200 segments (direction bougie par bougie)
// ═══════════════════════════════════════════════════════════════
path_base_score = 0.0
path_base_score += gd * 0.32
path_base_score += (score_buy > score_sell ? 1.0 : score_sell > score_buy ? -1.0 : 0.0) * math.min(1.0, verdict_gap / 3.5) * 0.28
path_base_score += st_dir * 0.14
path_base_score += bos_bull ? 0.10 : bos_bear ? -0.10 : 0.0
path_base_score += choch_bull ? 0.08 : choch_bear ? -0.08 : 0.0
path_base_score += (tb - ts) / 7.0 * 0.12
path_base_score += dc_sig * 0.06
path_base_score += kc_pos > 0.20 ? 0.05 : kc_pos < -0.20 ? -0.05 : 0.0
path_base_score += ema_above_count >= 3 ? 0.06 : ema_above_count <= 1 ? -0.06 : 0.0
path_base_score := math.max(-1.0, math.min(1.0, path_base_score))
var line[] path_lines = array.new_line()
var box[] path_boxes = array.new_box()
var line[] path_wicks = array.new_line()
var string path_encoded = ""
var int path_bull_n = 0
var int path_bear_n = 0
var int path_neut_n = 0
var float path_net_n = 0.0
// ── Inputs dérivés pour la projection ────────────────────────────────────────
// Pente BB mid sur 5 bougies (normalisée par ATR)
_bb_slope5 = atr14 > 0 ? (bb_mid - bb_mid[5]) / (5.0 * atr14) : 0.0
// ROC des 3 dernières bougies (momentum instantané, normalisé ATR)
_roc3 = atr14 > 0 ? (close - close[3]) / (3.0 * atr14) : 0.0
// Écart prix / BB mid (force de reversion)
_bb_dev = bb_mid > 0 and atr14 > 0 ? (close - bb_mid) / atr14 : 0.0
// Pente EMA9 sur 3 bougies (court terme)
_ema9_slope = atr14 > 0 ? (ema9 - ema9[3]) / (3.0 * atr14) : 0.0
// Ratio ATR courant vs ATR moyen 14 bougies (compression / expansion)
_atr_sma14 = ta.sma(atr14, 14)
_atr_ratio = _atr_sma14 > 0 ? atr14 / _atr_sma14 : 1.0
if barstate.islast
if path_show_eff
int npl = array.size(path_lines)
if npl > 0
for i = 0 to npl - 1
line.delete(array.get(path_lines, i))
array.clear(path_lines)
if path_candles_eff
int npb = array.size(path_boxes)
if npb > 0
for i = 0 to npb - 1
box.delete(array.get(path_boxes, i))
array.clear(path_boxes)
int npw = array.size(path_wicks)
if npw > 0
for i = 0 to npw - 1
line.delete(array.get(path_wicks, i))
array.clear(path_wicks)
float y_path = close
float y_open = close
float y_bb = bb_mid // BB mid simulé (suit le prix projeté)
int x0 = bar_index
int vis_n = math.min(path_candle_vis, path_horizon)
path_encoded := ""
path_bull_n := 0
path_bear_n := 0
path_neut_n := 0
// Snapshot des inputs au bar courant (immuables pendant la boucle)
float _snap_bb_slope = _bb_slope5
float _snap_roc3 = _roc3
float _snap_bb_dev = _bb_dev
float _snap_ema_slope = _ema9_slope
float _snap_atr_r = math.max(0.5, math.min(2.0, _atr_ratio))
float _snap_base = path_base_score
int _snap_st = st_dir
float _snap_atr = atr14
// Taille d'une bougie projetée = ATR * step * ratio (compression/expansion)
float _step_atr = _snap_atr * path_step * _snap_atr_r
// Bruit déterministe : cycle des vrais corps des 40 dernières bougies
// Évite toute sinusoïde arbitraire — reproduit la texture réelle du marché
int _noise_len = 40
float _noise_sum = 0.0
for _ni = 0 to _noise_len - 1
_noise_sum += math.abs(close[_ni] - open[_ni])
float _avg_body = _noise_sum / _noise_len
for step = 1 to path_horizon
// ── 1. Momentum : les 3 dernières bougies projettent leur élan ──────
float _mom = _snap_roc3 * math.max(0.0, 1.0 - step / 12.0)
// ── 2. Tendance structurelle (score GOM + BB slope + EMA slope) ──────
float _trend = (_snap_base * 0.50
+ _snap_bb_slope * 0.25
+ _snap_ema_slope * 0.25)
// Atténuation progressive : tendance s'estompe sur l'horizon
float _trend_decay = math.max(0.0, 1.0 - step / float(path_horizon))
_trend *= _trend_decay
// ── 3. Reversion BB mid : si prix trop loin de BB mid, rappel ────────
float _bb_dev_cur = _snap_atr > 0 ? (y_path - y_bb) / _snap_atr : 0.0
float _revert = -_bb_dev_cur * 0.18 // force de rappel 18%
// ── 4. Bruit déterministe (cycle corps réels, pas sinusoïde) ─────────
int _nidx = step % _noise_len
float _body_i = math.abs(close[_nidx] - open[_nidx])
float _sign_i = (close[_nidx] >= open[_nidx]) ? 1.0 : -1.0
// Bruit atténué après horizon/3 — projection de court terme plus fiable
float _noise_w = math.max(0.0, 1.0 - step / (float(path_horizon) * 0.6))
float _noise = _avg_body > 0 ? (_body_i / _avg_body - 1.0) * _sign_i * 0.08 * _noise_w : 0.0
// ── 5. Score composite ────────────────────────────────────────────────
float sc = _mom + _trend + _revert + _noise
// SuperTrend verrouille la direction si fort signal
if _snap_st == 1 and sc < -0.10
sc := sc * 0.4 // amortir les signaux contraires à ST haussier
if _snap_st == -1 and sc > 0.10
sc := sc * 0.4 // amortir les signaux contraires à ST baissier
// Seuil de décision : zone neutre réduite pour ne pas sur-neutraliser
int pdir = sc > 0.04 ? 1 : sc < -0.04 ? -1 : 0
// ── 6. Prix projeté ───────────────────────────────────────────────────
float dy = pdir * _step_atr
float y1 = y_path + dy
// BB mid simulé suit lentement le prix (EMA ~10 périodes)
y_bb := y_bb + (y1 - y_bb) * 0.10
// ── 7. Dessin ─────────────────────────────────────────────────────────
if path_show_eff
color seg_col = pdir == 1 ? color.new(BRAND_PRIMARY, 15) :
pdir == -1 ? color.new(BRAND_SECONDARY, 15) : color.new(WAIT, 35)
line seg = line.new(x0 + step - 1, y_path, x0 + step, y1,
xloc=xloc.bar_index, color=seg_col, width=path_width, style=line.style_solid)
array.push(path_lines, seg)
if path_candles_eff and step <= vis_n
float y_h = math.max(y_open, y1) + _step_atr * 0.12
float y_l = math.min(y_open, y1) - _step_atr * 0.08
color bc = pdir == 1 ? color.new(BRAND_PRIMARY, 75) :
pdir == -1 ? color.new(BRAND_SECONDARY, 75) : color.new(WAIT, 80)
box pb = box.new(x0 + step - 1, y_h, x0 + step, y_l,
xloc=xloc.bar_index, bgcolor=bc, border_color=bc, border_width=1)
array.push(path_boxes, pb)
line pw = line.new(x0 + step - 1, y_open, x0 + step - 1, y1,
xloc=xloc.bar_index, color=color.new(color.white, 65), width=1)
array.push(path_wicks, pw)
y_open := y1
y_path := y1
path_encoded += pdir == 1 ? "U" : pdir == -1 ? "D" : "N"
path_bull_n += pdir == 1 ? 1 : 0
path_bear_n += pdir == -1 ? 1 : 0
path_neut_n += pdir == 0 ? 1 : 0
path_net_n := path_bull_n - path_bear_n
// ═══════════════════════════════════════════════════════════════
// GHOST — ORDER FLOW (calculs + dessin)
// Réutilise : atr14 (l.161), ema9/ema21 (l.581-583) déjà calculés
// ═══════════════════════════════════════════════════════════════
// ── Delta Volume ─────────────────────────────────────────────
of_f_delta(h, l, c, v) =>
rng = h - l
rng > 1e-10 ? float(v) * (2.0 * (c - l) / rng - 1.0) : 0.0
of_dv_cur = of_f_delta(high, low, close, volume)
// ── CVD (reset chaque session) ───────────────────────────────
var float of_cvd = 0.0
if ta.change(time("D")) != 0
of_cvd := 0.0
of_cvd += of_dv_cur
// ── Sentiment pondéré volume ─────────────────────────────────
of_bull_v = close >= open ? volume : 0
of_bear_v = close < open ? volume : 0
of_sum_b = math.sum(float(of_bull_v), of_snt_per)
of_sum_s = math.sum(float(of_bear_v), of_snt_per)
of_tot_sv = of_sum_b + of_sum_s
of_buy_pct = of_tot_sv > 0 ? of_sum_b / of_tot_sv * 100.0 : 50.0
of_sel_pct = 100.0 - of_buy_pct
// ── Boussole (réutilise ema9/ema21/atr14 du script GOM) ──────
of_slope = atr14 > 0 ? (ema9 - ema9[4]) / (4.0 * atr14) : 0.0
of_diff = atr14 > 0 ? (ema9 - ema21) / atr14 : 0.0
_of_raw = math.atan(of_slope / (math.abs(of_diff) + 0.001)) * 180.0 / math.pi
_of_adj = of_diff < 0 ? _of_raw + (of_slope >= 0 ? 180.0 : -180.0) : _of_raw
of_angle = _of_adj < 0.0 ? _of_adj + 360.0 : _of_adj
of_oct = int(((of_angle + 22.5) % 360.0) / 45.0)
of_bull_d = of_oct == 0 or of_oct == 1 or of_oct == 2 or of_oct == 7
of_bear_d = not of_bull_d
of_cmp_col = of_bull_d ? color.new(OF_BULL, 0) : color.new(OF_BEAR, 0)
// ── Heatmap (boxes overlay, palette Bookmap) ─────────────────
var box[] of_bxs = array.new_box()
of_hm_color(p) =>
p < 0.30 ? color.new(#001a5e, 78) : p < 0.50 ? color.new(#0052a3, 68) : p < 0.70 ? color.new(#00b4c8, 55) : p < 0.88 ? color.new(#00c882, 42) : color.new(#d45a0a, 30)
// Calculs hors du bloc if barstate.islast pour cohérence Pine v6
of_hi = ta.highest(high, of_hm_lb)
of_lo = ta.lowest(low, of_hm_lb)
if barstate.islast and of_hm_show_eff
sz_of = array.size(of_bxs)
if sz_of > 0
for i_of = 0 to sz_of - 1
box.delete(array.get(of_bxs, i_of))
array.clear(of_bxs)
of_rng = of_hi - of_lo
if of_rng > 0.0
of_zh = of_rng / of_hm_zones
float[] of_sc = array.new_float(of_hm_zones, 0.0)
float z_lo = na
float z_hi = na
int of_max_back = math.min(of_hm_lb, bar_index + 1)
for i_b = 0 to of_max_back - 1
b_r = high[i_b] - low[i_b]
if b_r > 0.0
v_f = float(volume[i_b])
for z_i = 0 to of_hm_zones - 1
z_lo := of_lo + z_i * of_zh
z_hi := z_lo + of_zh
ovlp = math.min(high[i_b], z_hi) - math.max(low[i_b], z_lo)
if ovlp > 0.0
array.set(of_sc, z_i, array.get(of_sc, z_i) + v_f * (ovlp / b_r))
of_mx = array.max(of_sc)
if of_mx > 0.0
of_t0 = bar_index - of_hm_lb
of_t1 = bar_index + 8
for z_i = 0 to of_hm_zones - 1
of_pct = array.get(of_sc, z_i) / of_mx
if of_pct >= 0.12
z_lo := of_lo + z_i * of_zh
z_hi := z_lo + of_zh
of_bc = of_hm_color(of_pct)
of_bx = box.new(of_t0, z_hi, of_t1, z_lo, xloc=xloc.bar_index, bgcolor=of_bc, border_color=color.new(of_bc, 92), border_width=0)
array.push(of_bxs, of_bx)
// ── COMPASS DESSIN SUR CHART — bottom_left à côté du tableau GOM VERDICT ──
if of_cmp_show and barstate.islast
// Position: à côté du tableau GOM VERDICT (bottom-left)
_chart_hi = ta.highest(high, 200)
_chart_lo = ta.lowest(low, 200)
_chart_rng = _chart_hi - _chart_lo
_cmp_x = bar_index - 120 // 120 barres avant la fin (à côté du tableau)
_cmp_y = _chart_lo + _chart_rng * 0.05 // 5% du bas
_cmp_radius = _chart_rng * 0.04
// Cercle (8 lignes directionnelles)
for _a = 0 to 7
_angle1 = _a * 45.0 * math.pi / 180.0
_angle2 = (_a + 1) * 45.0 * math.pi / 180.0
_x1 = int(_cmp_x + math.cos(_angle1) * _cmp_radius)
_y1 = _cmp_y + math.sin(_angle1) * _cmp_radius
_x2 = int(_cmp_x + math.cos(_angle2) * _cmp_radius)
_y2 = _cmp_y + math.sin(_angle2) * _cmp_radius
line.new(_x1, _y1, _x2, _y2, color=color.new(OF_NEUT, 60), width=1, xloc=xloc.bar_index)
// Aiguille (angle courant)
_needle_angle = of_angle * math.pi / 180.0
_needle_len = _cmp_radius * 0.85
_nx = int(_cmp_x + math.cos(_needle_angle) * _needle_len)
_ny = _cmp_y + math.sin(_needle_angle) * _needle_len
line.new(int(_cmp_x), _cmp_y, _nx, _ny, color=of_cmp_col, width=2, xloc=xloc.bar_index)
// Angle label (centre compass)
label.new(int(_cmp_x), _cmp_y, str.tostring(math.round(of_angle)) + "°", yloc=yloc.price, color=color.new(OF_GOLD, 40), textcolor=OF_GOLD, size=size.small, style=label.style_label_center)
// ── GHOST label sur chart (compact) ──────────────────────────
if of_cmp_show and barstate.islast and show_verdict
of_ghost_txt = "Δ" + str.tostring(math.round(of_dv_cur, 0)) + " | CVD" + str.tostring(math.round(of_cvd, 0)) + " | Sent" + str.tostring(math.round(of_buy_pct, 0)) + "% | ◆" + str.tostring(math.round(of_angle)) + "°"
label.new(bar_index, high[0] * 0.995, of_ghost_txt, yloc=yloc.price, color=color.new(OF_GOLD, 20), textcolor=OF_GOLD, size=size.tiny)
// ═══════════════════════════════════════════════════════════════
// PLOTS data_window — MCP gom_verdict_poller (≤64 plots total)
// Chart : vwap + bb×3 + st_line = 5 | Data window : 33 | Total ≈ 38
// ═══════════════════════════════════════════════════════════════
_spike_export_pct = is_bc ? float(spike_pred_prob) : spike_prob * 100.0
plot(score_buy, "score_buy", display=display.data_window)
plot(score_sell, "score_sell", display=display.data_window)
plot(_spike_export_pct, "spike_pct", display=display.data_window)
plot(spike_imminence, "imminence_pct", display=display.data_window)
plot(spike_level_num, "spike_level", display=display.data_window)
plot(is_bc ? spike_progress * 100.0 : 0.0, "spike_progress_pct", display=display.data_window)
plot(is_bc ? float(bars_since_spike) : 0.0, "bars_since_spike", display=display.data_window)
plot(is_bc ? float(spike_freq_bars) : 0.0, "spike_freq_bars", display=display.data_window)
plot(spike_tradable ? 1.0 : 0.0, "spike_tradable", display=display.data_window)
plot(f_pre_spike * 100, "pre_spike_pct", display=display.data_window)
plot(rsi14, "rsi", display=display.data_window)
plot(st_dir, "st_dir", display=display.data_window)
plot(verdict_num, "verdict_num", display=display.data_window)
plot(verdict_gap, "verdict_gap", display=display.data_window)
plot(entry_quality * 100, "entry_quality", display=display.data_window)
plot(filter_ratio * 100, "coherence_pct", display=display.data_window)
plot(nz(kola_buy_level), "kola_buy", display=display.data_window)
plot(nz(kola_sell_level), "kola_sell", display=display.data_window)
plot(gd, "tf_global_dir", display=display.data_window)
plot(math.max(tb, ts), "tf_global_strength", display=display.data_window)
plot(tb, "tf_bull_count", display=display.data_window)
plot(ts, "tf_bear_count", display=display.data_window)
plot(path_bull_n, "pred_bull", display=display.data_window)
plot(path_bear_n, "pred_bear", display=display.data_window)
plot(path_neut_n, "pred_neut", display=display.data_window)
plot(path_net_n, "pred_net", display=display.data_window)
plot(setup_dir, "setup_dir", display=display.data_window)
plot(nz(setup_entry), "setup_entry", display=display.data_window)
plot(nz(setup_sl), "setup_sl", display=display.data_window)
plot(nz(setup_tp1), "setup_tp1", display=display.data_window)
plot(nz(setup_tp2), "setup_tp2", display=display.data_window)
plot(nz(setup_rr), "setup_rr", display=display.data_window)
setup_confirm_code = setup_confirm == "PIN_BAR_BULL" ? 1.0 : setup_confirm == "PIN_BAR_BEAR" ? -1.0 : 0.0
plot(setup_confirm_code, "setup_confirm_code", display=display.data_window)
plot(of_dv_cur, "ghost_delta", display=display.data_window)
plot(of_cvd, "ghost_cvd", display=display.data_window)
plot(of_buy_pct, "ghost_buypct", display=display.data_window)
plot(of_sel_pct, "ghost_sellpct", display=display.data_window)
plot(of_angle, "ghost_compass", display=display.data_window)
// OB confirmés — exportés pour TradeManager dashboard
plot(nz(ob_bull_top), "ob_bull_top", display=display.data_window)
plot(nz(ob_bull_bot), "ob_bull_bot", display=display.data_window)
plot(nz(ob_bear_top), "ob_bear_top", display=display.data_window)
plot(nz(ob_bear_bot), "ob_bear_bot", display=display.data_window)
// TENDANCES PAR TIMEFRAME — pour le poller GOM
plot(d1, "tf_m1_dir", display=display.data_window)
plot(d2, "tf_m5_dir", display=display.data_window)
plot(d3, "tf_m15_dir", display=display.data_window)
plot(d4, "tf_h1_dir", display=display.data_window)
// ═══════════════════════════════════════════════════════════════
// ALERTE WEBHOOK — POST /gom-verdict sur changement de verdict
// ═══════════════════════════════════════════════════════════════
verdict_changed = ta.change(verdict_num) != 0
rsi_extreme = (rsi_oversold and not rsi_oversold[1]) or (rsi_overbought and not rsi_overbought[1])
spike_entry_signal = spike_bc_en and is_bc and spike_tradable and not spike_tradable[1] and not bc_spike_now
spike_watch_signal = spike_bc_en and is_bc and spike_level_num == 2 and spike_level_num[1] != 2
_kola_st = kola_near_buy ? "NB" : kola_near_sell ? "NS" : "---"
// CSV 34 champs — webhook POST /gom-verdict/webhook (message = {{message}})
_path_payload = path_in_alert ? path_encoded : ""
gom_json = str.format(
"{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18},{19},{20},{21},{22},{23},{24},{25},{26},{27},{28},{29},{30},{31},{32},{33},{34},{35},{36},{37},{38},{39},{40},{41},{42},{43},{44},{45},{46},{47},{48},{49},{50},{51},{52},{53}",
syminfo.ticker,
verdict_txt,
verdict_num,
math.round(score_buy * 10) / 10,
math.round(score_sell * 10) / 10,
math.round(spike_prob * 100),
math.round(rsi14),
(rsi_oversold ? "true" : "false"),
(rsi_overbought ? "true" : "false"),
st_dir,
math.round(entry_quality * 100),
math.round(filter_ratio * 100),
nz(kola_buy_level, 0),
nz(kola_sell_level, 0),
math.round(verdict_gap * 10) / 10,
close,
_kola_st,
d1, math.round(r1),
d2, math.round(r2),
d3, math.round(r3),
d4, math.round(r4),
d5, math.round(r5),
d6, math.round(r6),
d7, math.round(r7),
gd,
tb,
ts,
path_bull_n,
path_bear_n,
path_neut_n,
_path_payload,
setup_dir,
math.round(nz(setup_entry) * 100) / 100,
math.round(nz(setup_sl) * 100) / 100,
math.round(nz(setup_tp1) * 100) / 100,
math.round(nz(setup_tp2) * 100) / 100,
math.round(nz(setup_rr) * 100) / 100,
spike_level_num,
spike_pred_prob,
math.round(spike_imminence),
math.round(spike_progress * 100),
spike_tradable ? 1 : 0,
bc_dir,
math.round(f_pre_spike * 100),
math.round(spike_z * 10) / 10,
bars_since_spike,
spike_freq_bars
)
// Sync chaque bougie confirmée → TradeManager / MT5 dashboard
if barstate.isconfirmed
alert(gom_json, alert.freq_once_per_bar_close)
// Alerte immédiate sur changement de verdict, RSI extreme, ou pre-spike tradable
if verdict_changed or rsi_extreme or spike_entry_signal or spike_watch_signal
alert(gom_json, alert.freq_once_per_bar)
// Alerte dédiée pre-spike (TradingView : activer le SON dans la config alerte)
_pre_spike_alert = "PRE_SPIKE_TRADE|" + syminfo.ticker + "|" + timeframe.period + "|" +
(is_boom ? "BUY" : "SELL") + "|" + str.tostring(spike_pred_prob) + "|" + gom_json
if spike_entry_signal
alert(_pre_spike_alert, alert.freq_once_per_bar)
// Alertes TradingView dédiées pre-spike (configurer son + webhook)
alertcondition(spike_entry_signal, title="Pre-Spike TRADABLE + BIP",
message="PRE_SPIKE_TRADE|{{ticker}}|{{interval}}|ENTRER|{{plot_5}}")
alertcondition(spike_watch_signal, title="Pre-Spike WATCH",
message="PRE_SPIKE_WATCH|{{ticker}}|{{interval}}|SURVEILLER")
alertcondition(bc_spike_now, title="Spike ACTIF",
message="SPIKE_ACTIF|{{ticker}}|{{interval}}|TROP_TARD")
Overview
Touch counts use an ATR percentage zone. Spike settings flag Boom and Crash cycle stages with a z-score threshold and a pre-spike table. Lookbacks, tolerances, and display toggles control how far back each module searches and which drawings appear. Structure marks stay hidden unless enabled. It does not place trades or define exits.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Line Break Period
int
3
kola_lb
Max bars analyse
int
120
kola_bars
Afficher niveaux KOLA
bool
true
kola_show
Touch detection
bool
true
kola_touch
Touch zone ATR%
float
30.0
kola_touch_zone_pct
Bars for touch count
int
150
kola_touch_bars
Lookback OB
int
10
ob_lookback
Afficher OBs
bool
true
ob_show
Afficher CHoCH / BOS (masqué par défaut)
bool
false
show_choch
Lookback pivots
int
8
struct_lb
Activer SIDO
bool
true
sido_enable
Pivot lookback SIDO
int
3
sido_lb
Show all 40 settings
Setting
Type
Default
Variable
Bars to analyze
int
300
sido_bars
Max bars between swings
int
80
sido_max_gap
Tolerance ATR%
float
35.0
sido_tol_pct
Afficher Fibonacci / OTE
bool
true
show_fib
Lookback swing
int
50
fib_lb
Lookback spike
int
25
spike_lb
Seuil alerte spike
float
0.62
spike_min
Seuil alerte precoce
float
0.52
spike_blink
Activer pre-spike Boom/Crash
bool
true
spike_bc_en
Frequence spike (0=auto)
int
0
spike_freq_ov
Reveil cycle (30%)
float
0.30
spike_wake
Zone watch (55%)
float
0.55
spike_watch
Zone entree (65%)
float
0.65
spike_entry
Reset cycle (85%)
float
0.85
spike_overdue
Prob min tradable %
int
55
spike_min_prob
Z-score spike min
float
2.0
spike_z_min
Barres stair
int
5
spike_stair_b
Tableau pre-spike
bool
true
spike_show_bc
Labels spike chart
bool
true
spike_show_lbl
SL spike (x ATR)
float
0.8
spike_sl_atr
TP spike (x ATR)
float
1.2
spike_tp_atr
Panneau SIGNAL unifié (recommandé Boom/Crash)
bool
true
ui_signal_panel
Mode compact Boom/Crash (moins de bruit)
bool
true
ui_compact_bc
Flèche clignotante entrée pre-spike
bool
true
ui_show_arrow
Chemin predictif sur Boom/Crash
bool
false
path_show_bc
VWAP session
bool
true
show_vwap
Bollinger Bands
bool
true
show_bb
Periode BB
int
20
bb_period
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.
This overlay indicator scans a pasted list of symbols for ICT liquidity sweep patterns using prior swing sweeps. The screening method looks back one, two, or three swings and can list long and short signals in a results table with adjustable rows, position, an
The Golden Bullet Strategy is an overlay signal indicator based on Smart Money Concepts and time-based liquidity models. It identifies liquidity sweeps of a designated reference session range during a specified hunt window. It then confirms Market Structure Sh
Indicatorv6SMC / ICT / price actionSessions & time
This indicator identifies fractal-based pivot highs and lows to map out market structure directly on the price chart. It detects bullish and bearish structure breaks, automatically labeling Break of Structure (BOS) and Change of Character (ChoCH) events when p
Apex Trend & Liquidity Master calculates a trend cloud using customizable moving averages expanded by a volatility multiplier directly on the main price chart. It identifies dynamic support and resistance liquidity zones from swing high and low pivots, extendi
The CISD with Fib/Range Extensions and Stats indicator tracks Change in State of Delivery (CISD) level breakouts on the price chart. It detects pullback structures filtered by ATR and volume conditions, then projects future target levels using customizable Fib
MTF State of Delivery tracks lower timeframe and higher timeframe orderflow delivery states by identifying bullish and bearish delivery levels across timeframes. It plots confirmed and pending orderflow lines directly on the price chart and provides an optiona