Tetris ICT [Bolívar]: your ICT indicator for TradingView
The market keeps memory. Every Fair Value Gap, every unmitigated Order Block is a structural debt the price will eventually settle. Tetris ICT [Bolívar] turns those debts into Tetris pieces on screen so you can learn to read the market's hidden order.
Built by Xavier Mas, founder of Bolívar Bolsa, for over 42,000 ICT/SMC students across Latin America. Free, unrestricted, written in Pine Script v6.
What does it do?
- Fair Value Gap → I piece. Detects 3-candle imbalances and marks them as unfilled zones.
- Order Block → O piece. Identifies the last opposite candle before an expansive move: the institutional origin of the impulse.
- Balanced Price Range (BPR) → S piece. Maps the overlap of two opposing FVGs — the zone of maximum price efficiency.
- Breaker Block → L piece. Detects Order Blocks that have failed and turned into reverse structure.
- Liquidity zones → T piece. Marks stop accumulations (equal highs/lows) where price tends to sweep before reversing.
- Arcade-style side HUD. Groups active (unmitigated) pieces with a real-time structural debt counter.
How to read the score
The HUD calculates how many unmitigated zones remain active above price and shows one of three states:
| Score | State | Educational reading |
|---|---|---|
| ≥ 65 | BLOCKED | High structural debt density. Price faces pending zones on multiple levels. |
| 30 – 64 | CAUTION | Moderate density. Some unmitigated zones may influence the path. |
| < 30 | GREEN | Clean board. Few active debts; price moves in open space. |
The next-piece preview anticipates which type of zone appears closest to price. Each piece disappears from the board when price mitigates it, just like a complete row in Tetris.
How to install on TradingView
- Open the Pine Editor. In TradingView, click the Pine Editor tab (bottom panel).
- Paste the code. Copy the full code below and replace the editor contents.
- Add to chart. Click "Add to chart" (top right of the editor).
- Save it. Press Ctrl/Cmd + S and give it a name. It will be stored in your TradingView library.
Pine Script v6 code
Tetris-ICT [Bolívar] · Pine v6 · 1137 lines · Free.
// ============================================================
// Tetris-ICT — bolivarbolsa.com — by Xavier Mas
// Indicador TradingView Pine Script v6
//
// Concepto: Las deudas estructurales del mercado ICT (FVG, OB,
// Breaker, BPR, Liquidez) se visualizan como piezas de Tetris
// en un tablero lateral con score HUD, next-piece preview y
// paleta de colores neón arcade. Cada zona no mitigada es una
// pieza activa que "bloquea" el precio hasta ser rellenada.
//
// Estructuras detectadas:
// FVG → pieza I (cyan / rojo)
// OB → pieza O (verde / naranja)
// BPR → pieza S (oro)
// Breaker → pieza L (azul / violeta)
// Liq → pieza T (magenta / violeta oscuro)
//
// Scoring:
// Debt Long = presión bajista encima del precio (FVG bear, OB bear, etc.)
// Debt Short = presión alcista debajo del precio
// BLOCKED (≥65) / CAUTION (≥30) / GREEN (<30)
//
// Créditos: Xavier Mas — Fundador bolivarbolsa.com
// 42k estudiantes Forex/ICT/SMC LATAM
// ============================================================
//@version=6
indicator("Tetris-ICT [Bolívar]", shorttitle="TETRIS-ICT", overlay=true,
max_boxes_count=500, max_lines_count=100, max_labels_count=150, max_bars_back=500)
// ============================================================
// PALETA DE COLORES NEÓN SYNTHWAVE
// ============================================================
var color C_FVG_BULL = #00F5FF
var color C_FVG_BEAR = #FF3131
var color C_OB_BULL = #39FF14
var color C_OB_BEAR = #FF7F00
var color C_BPR = #FFD700
var color C_BREAKER_BULL = #4D79FF
var color C_BREAKER_BEAR = #CC44FF
var color C_LIQ_HIGH = #FF00FF
var color C_LIQ_LOW = #9D00FF
var color C_BG = #0D0D0D
var color C_GRID = #1A1A2E
var color C_TEXT = #00FF41
var color C_BLOCKED = #FF0044
var color C_GREEN = #00FF41
var color C_CAUTION = #FFFF00
// ============================================================
// INPUTS — GRUPO "Detection"
// ============================================================
string GRP_DET = "Detection"
i_fvg_atr_mult = input.float(0.15, "FVG min gap (ATR × factor)", minval=0.0, step=0.05, group=GRP_DET,
tooltip="0 = sin filtro de tamaño mínimo")
i_ob_lookback = input.int(10, "OB lookback (barras)", minval=3, maxval=10, group=GRP_DET,
tooltip="Lookback limité à 10 para rendimiento")
i_swing_len = input.int(5, "Swing length (pivot left/right)", minval=2, maxval=20, group=GRP_DET)
i_bpr_enabled = input.bool(true, "Detectar BPR (Balanced Price Range)", group=GRP_DET)
i_mitigation_mode = input.string("Wick", "Modo mitigación", options=["Wick", "50%", "Close"], group=GRP_DET)
i_max_per_type = input.int(10, "Máx. estructuras activas por tipo", minval=3, maxval=20, group=GRP_DET)
// ============================================================
// INPUTS — GRUPO "Visuals"
// ============================================================
string GRP_VIS = "Visuals"
i_show_onchart = input.bool(true, "Mostrar bloques sobre el precio", group=GRP_VIS)
i_show_board = input.bool(true, "Mostrar Game Board lateral", group=GRP_VIS)
i_board_pos = input.string("Bottom Right", "Posición del Game Board", options=["Top Right", "Middle Right", "Bottom Right", "Top Left", "Middle Left", "Bottom Left"], group=GRP_VIS)
i_board_size = input.string("Compact", "Tamaño Game Board", options=["Compact", "Normal", "Large"], group=GRP_VIS)
i_show_hud = input.bool(true, "Mostrar HUD (top-right)", group=GRP_VIS)
i_hud_pos = input.string("Top Right", "Posición del HUD", options=["Top Right", "Top Center", "Top Left", "Bottom Right", "Bottom Center", "Bottom Left"], group=GRP_VIS)
i_show_next = input.bool(true, "Mostrar Next-Piece (top-left)", group=GRP_VIS)
i_next_pos = input.string("Top Left", "Posición del Next-Piece", options=["Top Left", "Top Center", "Top Right", "Bottom Left", "Bottom Center", "Bottom Right"], group=GRP_VIS)
i_show_grid = input.bool(true, "Mostrar grilla horizontal ATR", group=GRP_VIS)
i_opacity = input.int(70, "Opacidad bloques", minval=10, maxval=95, group=GRP_VIS)
i_beginner = input.bool(false, "Modo principiante (labels extendidos)", group=GRP_VIS)
// ============================================================
// INPUTS — GRUPO "Scoring"
// ============================================================
string GRP_SCO = "Scoring"
i_w_fvg = input.float(15.0, "Peso FVG", minval=0.0, group=GRP_SCO)
i_w_ob = input.float(20.0, "Peso OB", minval=0.0, group=GRP_SCO)
i_w_breaker = input.float(25.0, "Peso Breaker", minval=0.0, group=GRP_SCO)
i_w_bpr = input.float(20.0, "Peso BPR", minval=0.0, group=GRP_SCO)
i_w_liq = input.float(22.0, "Peso Liq", minval=0.0, group=GRP_SCO)
i_thresh_blocked = input.int(65, "Umbral BLOCKED", minval=1, maxval=100, group=GRP_SCO)
i_thresh_caution = input.int(30, "Umbral CAUTION", minval=1, maxval=100, group=GRP_SCO)
i_dist_cap_atr = input.float(6.0, "Distancia máx. (ATR ×)", minval=1.0, group=GRP_SCO)
i_age_halflife = input.int(500, "Semi-vida edad (barras)", minval=50, group=GRP_SCO)
// ============================================================
// INPUTS — GRUPO "Alerts"
// ============================================================
string GRP_ALT = "Alerts"
i_alert_blocked = input.bool(true, "Alerta BLOCKED", group=GRP_ALT)
i_alert_green = input.bool(true, "Alerta GREEN", group=GRP_ALT)
i_alert_mitigation = input.bool(true, "Alerta Mitigación", group=GRP_ALT)
// ============================================================
// ARRAYS DE DATOS — FVG
// ============================================================
var array<float> fvg_top = array.new<float>()
var array<float> fvg_bot = array.new<float>()
var array<int> fvg_dir = array.new<int>()
var array<int> fvg_bar = array.new<int>()
var array<bool> fvg_mitig = array.new<bool>()
var array<box> fvg_box = array.new<box>()
// ============================================================
// ARRAYS DE DATOS — OB (Order Block)
// ============================================================
var array<float> ob_top = array.new<float>()
var array<float> ob_bot = array.new<float>()
var array<int> ob_dir = array.new<int>()
var array<int> ob_bar = array.new<int>()
var array<bool> ob_mitig = array.new<bool>()
var array<box> ob_box = array.new<box>()
// ============================================================
// ARRAYS DE DATOS — BREAKER
// ============================================================
var array<float> brk_top = array.new<float>()
var array<float> brk_bot = array.new<float>()
var array<int> brk_dir = array.new<int>()
var array<int> brk_bar = array.new<int>()
var array<bool> brk_mitig = array.new<bool>()
var array<box> brk_box = array.new<box>()
var int brk_max = 8
// ============================================================
// ARRAYS DE DATOS — BPR (Balanced Price Range)
// ============================================================
var array<float> bpr_top = array.new<float>()
var array<float> bpr_bot = array.new<float>()
var array<int> bpr_dir = array.new<int>()
var array<int> bpr_bar = array.new<int>()
var array<bool> bpr_mitig = array.new<bool>()
var array<box> bpr_box = array.new<box>()
var int bpr_max = 6
// ============================================================
// ARRAYS DE DATOS — LIQUIDEZ (BSL / SSL)
// ============================================================
var array<float> liq_top = array.new<float>()
var array<float> liq_bot = array.new<float>()
var array<int> liq_dir = array.new<int>()
var array<int> liq_bar = array.new<int>()
var array<bool> liq_mitig = array.new<bool>()
var array<box> liq_box = array.new<box>()
// ============================================================
// VARIABLES DE ESTADO GLOBAL
// ============================================================
var int lines_cleared = 0
var int blink_until = 0
var bool mitig_event = false
var float mitig_price = na
var string mitig_type = ""
// Señales previas para alertas de transición
var string prev_signal_long = "GREEN"
var string prev_signal_short = "GREEN"
var int prev_lines = 0
// ============================================================
// GRILLA HORIZONTAL (creada una sola vez)
// ============================================================
var array<line> grid_lines = array.new<line>()
if i_show_grid and array.size(grid_lines) == 0
for _i = 0 to 19
array.push(grid_lines, line.new(bar_index, close, bar_index + 1, close,
color=color.new(C_GRID, 80), style=line.style_dotted, width=1))
// ============================================================
// TABLAS (creadas una sola vez)
// ============================================================
var table tbl_hud = na
var table tbl_next = na
var table tbl_board = na
// Helper: convertir string del input → position constant
pos_of(s) =>
switch s
"Top Right" => position.top_right
"Top Center" => position.top_center
"Top Left" => position.top_left
"Middle Right" => position.middle_right
"Middle Center" => position.middle_center
"Middle Left" => position.middle_left
"Bottom Right" => position.bottom_right
"Bottom Center" => position.bottom_center
"Bottom Left" => position.bottom_left
=> position.bottom_right
// Dimensiones de cada celda del Game Board (% del chart)
board_cell_w = i_board_size == "Compact" ? 1.4 : i_board_size == "Normal" ? 2.2 : 3.0
board_cell_h = i_board_size == "Compact" ? 0.9 : i_board_size == "Normal" ? 1.4 : 1.9
if i_show_hud and na(tbl_hud)
tbl_hud := table.new(pos_of(i_hud_pos), 2, 6,
bgcolor=C_BG, frame_color=C_TEXT, frame_width=2,
border_color=color.new(C_TEXT, 60), border_width=1)
if i_show_next and na(tbl_next)
tbl_next := table.new(pos_of(i_next_pos), 6, 8,
bgcolor=C_BG, frame_color=C_LIQ_HIGH, frame_width=2,
border_color=color.new(C_LIQ_HIGH, 60), border_width=1)
if i_show_board and na(tbl_board)
tbl_board := table.new(pos_of(i_board_pos), 12, 22,
bgcolor=C_BG, frame_color=C_TEXT, frame_width=3,
border_color=color.new(C_TEXT, 50), border_width=1)
// ============================================================
// ATR Y PARÁMETROS BASE
// ============================================================
atr14 = ta.atr(14)
fvg_min = atr14 * i_fvg_atr_mult
// ============================================================
// FUNCIÓN: color segun señal
// ============================================================
sig_color(sig) =>
sig == "BLOCKED" ? C_BLOCKED : sig == "CAUTION" ? C_CAUTION : C_GREEN
// ============================================================
// FUNCIÓN: barra de score ASCII
// ============================================================
score_bar(v) =>
filled = int(math.round(math.max(0.0, math.min(100.0, v)) / 10.0))
str.repeat("▓", filled) + str.repeat("░", 10 - filled)
// ============================================================
// FUNCIÓN: verificar mitigación de una zona
// ============================================================
is_mitigated(top_val, bot_val) =>
mid = (top_val + bot_val) / 2.0
result = false
if i_mitigation_mode == "Wick"
result := low <= top_val and high >= bot_val
else if i_mitigation_mode == "50%"
result := low <= mid or high >= mid
else
result := close >= bot_val and close <= top_val
result
// ============================================================
// FUNCIÓN: pruning de un array de estructuras
// ============================================================
prune_struct(tops, bots, dirs, bars, mitigs, boxes, max_count) =>
// Primero eliminar mitigadas
i = 0
while i < array.size(mitigs)
if array.get(mitigs, i)
b = array.get(boxes, i)
if not na(b)
box.delete(b)
array.remove(tops, i)
array.remove(bots, i)
array.remove(dirs, i)
array.remove(bars, i)
array.remove(mitigs, i)
array.remove(boxes, i)
else
i += 1
// Luego FIFO si sigue sobre el límite
while array.size(tops) >= max_count
b = array.get(boxes, 0)
if not na(b)
box.delete(b)
array.remove(tops, 0)
array.remove(bots, 0)
array.remove(dirs, 0)
array.remove(bars, 0)
array.remove(mitigs, 0)
array.remove(boxes, 0)
// ============================================================
// DETECCIÓN FVG — solo en barras confirmadas
// ============================================================
if barstate.isconfirmed
// FVG alcista: vela[0].low > vela[2].high → gap entre vela 2 y vela 0
bull_fvg_size = low[1] - high[3]
if low[1] > high[3] and bull_fvg_size >= fvg_min
prune_struct(fvg_top, fvg_bot, fvg_dir, fvg_bar, fvg_mitig, fvg_box, i_max_per_type)
new_box = box.new(bar_index[2], high[3], bar_index + 5, low[1],
border_color=C_FVG_BULL, border_width=2,
bgcolor=color.new(C_FVG_BULL, i_opacity))
array.push(fvg_top, low[1])
array.push(fvg_bot, high[3])
array.push(fvg_dir, 1)
array.push(fvg_bar, bar_index[2])
array.push(fvg_mitig, false)
array.push(fvg_box, new_box)
// FVG bajista: vela[0].high < vela[2].low → gap inverso
bear_fvg_size = low[3] - high[1]
if high[1] < low[3] and bear_fvg_size >= fvg_min
prune_struct(fvg_top, fvg_bot, fvg_dir, fvg_bar, fvg_mitig, fvg_box, i_max_per_type)
new_box = box.new(bar_index[2], high[1], bar_index + 5, low[3],
border_color=C_FVG_BEAR, border_width=2,
bgcolor=color.new(C_FVG_BEAR, i_opacity))
array.push(fvg_top, low[3])
array.push(fvg_bot, high[1])
array.push(fvg_dir, -1)
array.push(fvg_bar, bar_index[2])
array.push(fvg_mitig, false)
array.push(fvg_box, new_box)
// ============================================================
// DETECCIÓN OB (Order Block)
// ============================================================
if barstate.isconfirmed
// OB alcista: última vela bajista antes de un desplazamiento alcista fuerte
// Condición: close actual > high[3], y hay una vela bajista reciente
displacement_bull = close > high[3] and (close - low[3]) >= atr14 * 0.5
if displacement_bull
best_bear_idx = 0
best_bear_body = 0.0
for k = 1 to math.min(i_ob_lookback, 10)
body_k = open[k] - close[k]
if body_k > best_bear_body and body_k >= atr14 * 0.5
best_bear_body := body_k
best_bear_idx := k
if best_bear_idx > 0 and best_bear_body > 0.0
ob_t = high[best_bear_idx]
ob_b = low[best_bear_idx]
// Verificar que no existe ya un OB muy cercano
already_exists = false
if array.size(ob_top) > 0
for m = 0 to array.size(ob_top) - 1
if math.abs(array.get(ob_top, m) - ob_t) < atr14 * 0.3
already_exists := true
break
if not already_exists
prune_struct(ob_top, ob_bot, ob_dir, ob_bar, ob_mitig, ob_box, i_max_per_type)
new_box = box.new(bar_index[best_bear_idx], ob_b, bar_index + 5, ob_t,
border_color=C_OB_BULL, border_width=2,
bgcolor=color.new(C_OB_BULL, i_opacity))
array.push(ob_top, ob_t)
array.push(ob_bot, ob_b)
array.push(ob_dir, 1)
array.push(ob_bar, bar_index[best_bear_idx])
array.push(ob_mitig, false)
array.push(ob_box, new_box)
// OB bajista: última vela alcista antes de un desplazamiento bajista fuerte
displacement_bear = close < low[3] and (high[3] - close) >= atr14 * 0.5
if displacement_bear
best_bull_idx = 0
best_bull_body = 0.0
for k = 1 to math.min(i_ob_lookback, 10)
body_k = close[k] - open[k]
if body_k > best_bull_body and body_k >= atr14 * 0.5
best_bull_body := body_k
best_bull_idx := k
if best_bull_idx > 0 and best_bull_body > 0.0
ob_t = high[best_bull_idx]
ob_b = low[best_bull_idx]
already_exists = false
if array.size(ob_top) > 0
for m = 0 to array.size(ob_top) - 1
if math.abs(array.get(ob_top, m) - ob_t) < atr14 * 0.3
already_exists := true
break
if not already_exists
prune_struct(ob_top, ob_bot, ob_dir, ob_bar, ob_mitig, ob_box, i_max_per_type)
new_box = box.new(bar_index[best_bull_idx], ob_b, bar_index + 5, ob_t,
border_color=C_OB_BEAR, border_width=2,
bgcolor=color.new(C_OB_BEAR, i_opacity))
array.push(ob_top, ob_t)
array.push(ob_bot, ob_b)
array.push(ob_dir, -1)
array.push(ob_bar, bar_index[best_bull_idx])
array.push(ob_mitig, false)
array.push(ob_box, new_box)
// ============================================================
// DETECCIÓN BREAKER — OB invalidado (precio cierra más allá)
// ============================================================
if barstate.isconfirmed
i = 0
while i < array.size(ob_top)
ob_t = array.get(ob_top, i)
ob_b = array.get(ob_bot, i)
ob_d = array.get(ob_dir, i)
ob_br = array.get(ob_bar, i)
// OB alcista invalidado → precio cierra BAJO el OB → se convierte en bearish breaker
if ob_d == 1 and close < ob_b
prune_struct(brk_top, brk_bot, brk_dir, brk_bar, brk_mitig, brk_box, brk_max)
new_box = box.new(ob_br, ob_b, bar_index + 5, ob_t,
border_color=C_BREAKER_BEAR, border_width=2,
bgcolor=color.new(C_BREAKER_BEAR, i_opacity))
array.push(brk_top, ob_t)
array.push(brk_bot, ob_b)
array.push(brk_dir, -1)
array.push(brk_bar, ob_br)
array.push(brk_mitig, false)
array.push(brk_box, new_box)
// Eliminar del array OB
old_box = array.get(ob_box, i)
if not na(old_box)
box.delete(old_box)
array.remove(ob_top, i)
array.remove(ob_bot, i)
array.remove(ob_dir, i)
array.remove(ob_bar, i)
array.remove(ob_mitig, i)
array.remove(ob_box, i)
// OB bajista invalidado → precio cierra SOBRE el OB → se convierte en bullish breaker
else if ob_d == -1 and close > ob_t
prune_struct(brk_top, brk_bot, brk_dir, brk_bar, brk_mitig, brk_box, brk_max)
new_box = box.new(ob_br, ob_b, bar_index + 5, ob_t,
border_color=C_BREAKER_BULL, border_width=2,
bgcolor=color.new(C_BREAKER_BULL, i_opacity))
array.push(brk_top, ob_t)
array.push(brk_bot, ob_b)
array.push(brk_dir, 1)
array.push(brk_bar, ob_br)
array.push(brk_mitig, false)
array.push(brk_box, new_box)
old_box = array.get(ob_box, i)
if not na(old_box)
box.delete(old_box)
array.remove(ob_top, i)
array.remove(ob_bot, i)
array.remove(ob_dir, i)
array.remove(ob_bar, i)
array.remove(ob_mitig, i)
array.remove(ob_box, i)
else
i += 1
// ============================================================
// DETECCIÓN BPR (Balanced Price Range)
// ============================================================
if barstate.isconfirmed and i_bpr_enabled
n_fvg = array.size(fvg_top)
if n_fvg >= 2
last_bull_idx = -1
last_bear_idx = -1
for k = n_fvg - 1 to 0
d = array.get(fvg_dir, k)
if d == 1 and last_bull_idx < 0
last_bull_idx := k
else if d == -1 and last_bear_idx < 0
last_bear_idx := k
if last_bull_idx >= 0 and last_bear_idx >= 0
break
if last_bull_idx >= 0 and last_bear_idx >= 0
bar_bull = array.get(fvg_bar, last_bull_idx)
bar_bear = array.get(fvg_bar, last_bear_idx)
bar_diff = math.abs(bar_bull - bar_bear)
if bar_diff <= 20
// Verificar solapamiento
bull_top = array.get(fvg_top, last_bull_idx)
bull_bot = array.get(fvg_bot, last_bull_idx)
bear_top = array.get(fvg_top, last_bear_idx)
bear_bot = array.get(fvg_bot, last_bear_idx)
overlap_top = math.min(bull_top, bear_top)
overlap_bot = math.max(bull_bot, bear_bot)
if overlap_top > overlap_bot
// Zona de solapamiento válida — verificar no duplicado
already_bpr = false
if array.size(bpr_top) > 0
for m = 0 to array.size(bpr_top) - 1
if math.abs(array.get(bpr_top, m) - overlap_top) < atr14 * 0.2
already_bpr := true
break
if not already_bpr
prune_struct(bpr_top, bpr_bot, bpr_dir, bpr_bar, bpr_mitig, bpr_box, bpr_max)
new_box = box.new(math.min(bar_bull, bar_bear), overlap_bot,
bar_index + 5, overlap_top,
border_color=C_BPR, border_width=2,
bgcolor=color.new(C_BPR, i_opacity))
array.push(bpr_top, overlap_top)
array.push(bpr_bot, overlap_bot)
array.push(bpr_dir, 0)
array.push(bpr_bar, bar_index)
array.push(bpr_mitig, false)
array.push(bpr_box, new_box)
// ============================================================
// DETECCIÓN LIQUIDEZ (BSL / SSL via pivots)
// ============================================================
ph = ta.pivothigh(high, i_swing_len, i_swing_len)
pl = ta.pivotlow(low, i_swing_len, i_swing_len)
if barstate.isconfirmed
if not na(ph)
already_liq = false
if array.size(liq_top) > 0
for m = 0 to array.size(liq_top) - 1
if math.abs(array.get(liq_top, m) - ph) < atr14 * 0.3
already_liq := true
break
if not already_liq
prune_struct(liq_top, liq_bot, liq_dir, liq_bar, liq_mitig, liq_box, i_max_per_type)
new_box = box.new(bar_index[i_swing_len], ph - atr14 * 0.05,
bar_index + 5, ph + atr14 * 0.05,
border_color=C_LIQ_HIGH, border_width=1,
bgcolor=color.new(C_LIQ_HIGH, 85))
array.push(liq_top, ph + atr14 * 0.05)
array.push(liq_bot, ph - atr14 * 0.05)
array.push(liq_dir, 1)
array.push(liq_bar, bar_index[i_swing_len])
array.push(liq_mitig, false)
array.push(liq_box, new_box)
if not na(pl)
already_liq = false
if array.size(liq_top) > 0
for m = 0 to array.size(liq_top) - 1
if math.abs(array.get(liq_bot, m) - pl) < atr14 * 0.3
already_liq := true
break
if not already_liq
prune_struct(liq_top, liq_bot, liq_dir, liq_bar, liq_mitig, liq_box, i_max_per_type)
new_box = box.new(bar_index[i_swing_len], pl - atr14 * 0.05,
bar_index + 5, pl + atr14 * 0.05,
border_color=C_LIQ_LOW, border_width=1,
bgcolor=color.new(C_LIQ_LOW, 85))
array.push(liq_top, pl + atr14 * 0.05)
array.push(liq_bot, pl - atr14 * 0.05)
array.push(liq_dir, -1)
array.push(liq_bar, bar_index[i_swing_len])
array.push(liq_mitig, false)
array.push(liq_box, new_box)
// ============================================================
// CHECK MITIGACIÓN — actualizar estado de todas las estructuras
// ============================================================
mitig_event := false
if barstate.isconfirmed
// FVG
if array.size(fvg_top) > 0
for i = 0 to array.size(fvg_top) - 1
if not array.get(fvg_mitig, i)
if is_mitigated(array.get(fvg_top, i), array.get(fvg_bot, i))
array.set(fvg_mitig, i, true)
b = array.get(fvg_box, i)
if not na(b)
box.set_bgcolor(b, color.new(color.gray, 90))
box.set_border_color(b, color.new(color.gray, 50))
mitig_event := true
mitig_type := "FVG"
mitig_price := (array.get(fvg_top, i) + array.get(fvg_bot, i)) / 2.0
// OB
if array.size(ob_top) > 0
for i = 0 to array.size(ob_top) - 1
if not array.get(ob_mitig, i)
if is_mitigated(array.get(ob_top, i), array.get(ob_bot, i))
array.set(ob_mitig, i, true)
b = array.get(ob_box, i)
if not na(b)
box.set_bgcolor(b, color.new(color.gray, 90))
box.set_border_color(b, color.new(color.gray, 50))
mitig_event := true
mitig_type := "OB"
mitig_price := (array.get(ob_top, i) + array.get(ob_bot, i)) / 2.0
// Breaker
if array.size(brk_top) > 0
for i = 0 to array.size(brk_top) - 1
if not array.get(brk_mitig, i)
if is_mitigated(array.get(brk_top, i), array.get(brk_bot, i))
array.set(brk_mitig, i, true)
b = array.get(brk_box, i)
if not na(b)
box.set_bgcolor(b, color.new(color.gray, 90))
box.set_border_color(b, color.new(color.gray, 50))
mitig_event := true
mitig_type := "BRK"
mitig_price := (array.get(brk_top, i) + array.get(brk_bot, i)) / 2.0
// BPR
if array.size(bpr_top) > 0
for i = 0 to array.size(bpr_top) - 1
if not array.get(bpr_mitig, i)
if is_mitigated(array.get(bpr_top, i), array.get(bpr_bot, i))
array.set(bpr_mitig, i, true)
b = array.get(bpr_box, i)
if not na(b)
box.set_bgcolor(b, color.new(color.gray, 90))
box.set_border_color(b, color.new(color.gray, 50))
mitig_event := true
mitig_type := "BPR"
mitig_price := (array.get(bpr_top, i) + array.get(bpr_bot, i)) / 2.0
// Liquidez — sweep check
if array.size(liq_top) > 0
for i = 0 to array.size(liq_top) - 1
if not array.get(liq_mitig, i)
liq_d = array.get(liq_dir, i)
liq_t = array.get(liq_top, i)
liq_b = array.get(liq_bot, i)
swept = false
if liq_d == 1 and high > liq_t + atr14 * 0.1
swept := true
else if liq_d == -1 and low < liq_b - atr14 * 0.1
swept := true
if swept
array.set(liq_mitig, i, true)
b = array.get(liq_box, i)
if not na(b)
box.set_bgcolor(b, color.new(color.gray, 90))
box.set_border_color(b, color.new(color.gray, 50))
mitig_event := true
mitig_type := "LIQ"
mitig_price := (liq_t + liq_b) / 2.0
// ============================================================
// ACTUALIZAR box.set_right EN TODAS LAS ESTRUCTURAS ACTIVAS
// ============================================================
if (barstate.islast or barstate.isrealtime) and i_show_onchart
if array.size(fvg_box) > 0
for i = 0 to array.size(fvg_box) - 1
b = array.get(fvg_box, i)
if not na(b)
box.set_right(b, bar_index + 5)
if array.size(ob_box) > 0
for i = 0 to array.size(ob_box) - 1
b = array.get(ob_box, i)
if not na(b)
box.set_right(b, bar_index + 5)
if array.size(brk_box) > 0
for i = 0 to array.size(brk_box) - 1
b = array.get(brk_box, i)
if not na(b)
box.set_right(b, bar_index + 5)
if array.size(bpr_box) > 0
for i = 0 to array.size(bpr_box) - 1
b = array.get(bpr_box, i)
if not na(b)
box.set_right(b, bar_index + 5)
if array.size(liq_box) > 0
for i = 0 to array.size(liq_box) - 1
b = array.get(liq_box, i)
if not na(b)
box.set_right(b, bar_index + 5)
// ============================================================
// CÁLCULO DEBT SCORE — fórmula ICT ponderada con distancia y edad
// ============================================================
debt_long_raw = 0.0
debt_short_raw = 0.0
// Helper interno para acumular el score de una zona
calc_contrib(top_val, bot_val, dir_val, bar_val, weight, is_mitig) =>
contrib_l = 0.0
contrib_s = 0.0
if not is_mitig
mid = (top_val + bot_val) / 2.0
dist = math.abs(close - mid) / math.max(atr14, 0.0001)
if dist <= i_dist_cap_atr
dist_factor = math.max(0.5, math.min(2.0, 1.0 / (1.0 + dist * 0.1)))
age = bar_index - bar_val
age_factor = math.exp(-float(age) / float(i_age_halflife))
contrib = weight * dist_factor * age_factor
// dir_val: 1 = alcista (presiona deuda SHORT si está debajo, debería ser LONG driver)
// Lógica: estructura alcista DEBAJO del precio → deuda SHORT (el precio la debe regresar)
// Estructura bajista ENCIMA del precio → deuda LONG (el precio la debe cubrir)
if dir_val == 1 and mid < close
contrib_s := contrib
else if dir_val == -1 and mid > close
contrib_l := contrib
else if dir_val == 0
// BPR: cuenta en ambos lados según posición
if mid > close
contrib_l := contrib * 0.5
else
contrib_s := contrib * 0.5
[contrib_l, contrib_s]
// Helper top-level para contar zonas activas en cada fila Tetris
count_cells_into(tops, bots, mitigs, target, row_min_val) =>
if array.size(tops) > 0
for i = 0 to array.size(tops) - 1
if not array.get(mitigs, i)
t = array.get(tops, i)
b = array.get(bots, i)
mid = (t + b) / 2.0
row_idx = int(math.floor((mid - row_min_val) / (atr14 * 0.5)))
if row_idx >= 0 and row_idx < 20
array.set(target, row_idx, array.get(target, row_idx) + 1)
// Acumular FVG
if array.size(fvg_top) > 0
for i = 0 to array.size(fvg_top) - 1
[cl, cs] = calc_contrib(array.get(fvg_top, i), array.get(fvg_bot, i),
array.get(fvg_dir, i), array.get(fvg_bar, i),
i_w_fvg, array.get(fvg_mitig, i))
debt_long_raw += cl
debt_short_raw += cs
// Acumular OB
if array.size(ob_top) > 0
for i = 0 to array.size(ob_top) - 1
[cl, cs] = calc_contrib(array.get(ob_top, i), array.get(ob_bot, i),
array.get(ob_dir, i), array.get(ob_bar, i),
i_w_ob, array.get(ob_mitig, i))
debt_long_raw += cl
debt_short_raw += cs
// Acumular Breaker
if array.size(brk_top) > 0
for i = 0 to array.size(brk_top) - 1
[cl, cs] = calc_contrib(array.get(brk_top, i), array.get(brk_bot, i),
array.get(brk_dir, i), array.get(brk_bar, i),
i_w_breaker, array.get(brk_mitig, i))
debt_long_raw += cl
debt_short_raw += cs
// Acumular BPR
if array.size(bpr_top) > 0
for i = 0 to array.size(bpr_top) - 1
[cl, cs] = calc_contrib(array.get(bpr_top, i), array.get(bpr_bot, i),
array.get(bpr_dir, i), array.get(bpr_bar, i),
i_w_bpr, array.get(bpr_mitig, i))
debt_long_raw += cl
debt_short_raw += cs
// Acumular Liquidez
// BSL (dir=1) encima = deuda LONG; SSL (dir=-1) debajo = deuda SHORT
if array.size(liq_top) > 0
for i = 0 to array.size(liq_top) - 1
liq_d = array.get(liq_dir, i)
liq_mid = (array.get(liq_top, i) + array.get(liq_bot, i)) / 2.0
if not array.get(liq_mitig, i)
dist = math.abs(close - liq_mid) / math.max(atr14, 0.0001)
if dist <= i_dist_cap_atr
dist_factor = math.max(0.5, math.min(2.0, 1.0 / (1.0 + dist * 0.1)))
age_factor = math.exp(-float(bar_index - array.get(liq_bar, i)) / float(i_age_halflife))
contrib = i_w_liq * dist_factor * age_factor
// BSL encima bloquea longs (el precio lo irá a buscar pero hay riesgo de reversión)
if liq_d == 1 and liq_mid > close
debt_long_raw += contrib
// SSL debajo bloquea shorts
else if liq_d == -1 and liq_mid < close
debt_short_raw += contrib
// Normalizar (cap empírico 120)
debt_long = math.min(100.0, debt_long_raw / 120.0 * 100.0)
debt_short = math.min(100.0, debt_short_raw / 120.0 * 100.0)
// ============================================================
// SEÑAL FINAL
// ============================================================
var string signal_long = "GREEN"
var string signal_short = "GREEN"
signal_long := debt_long >= i_thresh_blocked ? "BLOCKED" : debt_long >= i_thresh_caution ? "CAUTION" : "GREEN"
signal_short := debt_short >= i_thresh_blocked ? "BLOCKED" : debt_short >= i_thresh_caution ? "CAUTION" : "GREEN"
// ============================================================
// GRILLA HORIZONTAL — actualizar posiciones cada barra
// ============================================================
if i_show_grid and array.size(grid_lines) > 0
for k = 0 to 19
y_level = close + (k - 10) * atr14 * 0.5
l = array.get(grid_lines, k)
line.set_x1(l, bar_index - 50)
line.set_x2(l, bar_index + 50)
line.set_y1(l, y_level)
line.set_y2(l, y_level)
// ============================================================
// BUSCAR ESTRUCTURA MITIGACIÓN MÁS CERCANA (para HUD + Next-piece)
// Pine v6: funciones no pueden mutar vars del scope padre vía :=,
// usamos arrays de 1 elemento (paso por referencia).
// ============================================================
var array<float> nearest_dist_arr = array.from(999999.0)
var array<float> nearest_price_arr = array.from(float(na))
var array<string> nearest_name_arr = array.from("---")
var array<color> nearest_color_arr = array.from(C_TEXT)
var array<string> nearest_type_arr = array.from("")
// Reset cada barra
array.set(nearest_dist_arr, 0, 999999.0)
array.set(nearest_price_arr, 0, na)
array.set(nearest_name_arr, 0, "---")
array.set(nearest_color_arr, 0, C_TEXT)
array.set(nearest_type_arr, 0, "")
check_nearest(tops, bots, mitigs, type_name, piece_color) =>
if array.size(tops) > 0
for i = 0 to array.size(tops) - 1
if not array.get(mitigs, i)
mid = (array.get(tops, i) + array.get(bots, i)) / 2.0
dist = math.abs(close - mid)
if dist < array.get(nearest_dist_arr, 0)
array.set(nearest_dist_arr, 0, dist)
array.set(nearest_price_arr, 0, mid)
array.set(nearest_name_arr, 0, type_name)
array.set(nearest_color_arr, 0, piece_color)
array.set(nearest_type_arr, 0, type_name)
check_nearest(fvg_top, fvg_bot, fvg_mitig, "FVG", C_FVG_BULL)
check_nearest(ob_top, ob_bot, ob_mitig, "OB", C_OB_BULL)
check_nearest(brk_top, brk_bot, brk_mitig, "BREAKER", C_BREAKER_BULL)
check_nearest(bpr_top, bpr_bot, bpr_mitig, "BPR", C_BPR)
check_nearest(liq_top, liq_bot, liq_mitig, "LIQ", C_LIQ_HIGH)
// Aliases locales para el resto del código
nearest_dist = array.get(nearest_dist_arr, 0)
nearest_price = array.get(nearest_price_arr, 0)
nearest_name = array.get(nearest_name_arr, 0)
nearest_color = array.get(nearest_color_arr, 0)
nearest_type = array.get(nearest_type_arr, 0)
// ============================================================
// DETECCIÓN "LÍNEA COMPLETA" TETRIS — solo en barras confirmadas
// ============================================================
if barstate.isconfirmed
row_min = close - 10.0 * atr14 * 0.5
cells_per_row = array.new<int>(20, 0)
count_cells_into(fvg_top, fvg_bot, fvg_mitig, cells_per_row, row_min)
count_cells_into(ob_top, ob_bot, ob_mitig, cells_per_row, row_min)
count_cells_into(brk_top, brk_bot, brk_mitig, cells_per_row, row_min)
count_cells_into(bpr_top, bpr_bot, bpr_mitig, cells_per_row, row_min)
count_cells_into(liq_top, liq_bot, liq_mitig, cells_per_row, row_min)
for row_idx = 0 to 19
if array.get(cells_per_row, row_idx) >= 7
lines_cleared += 1
blink_until := bar_index + 3
// ============================================================
// RENDER GAME BOARD LATERAL — solo en última barra (rendimiento)
// ============================================================
if barstate.islast and i_show_board and not na(tbl_board)
row_min_board = close - 10.0 * atr14 * 0.5
// Header
table.cell(tbl_board, 0, 0, "TETRIS-ICT",
text_color=C_TEXT, text_size=size.small,
bgcolor=color.new(C_BG, 20), text_halign=text.align_center,
width=board_cell_w, height=board_cell_h)
for c = 1 to 11
table.merge_cells(tbl_board, 0, 0, 11, 0)
break
// Filas 1-20: celdas de precio
for row_idx = 0 to 19
row_price = row_min_board + (19 - row_idx) * atr14 * 0.5
price_str = str.tostring(math.round(row_price, 2))
// Columna precio (col 0)
table.cell(tbl_board, 0, row_idx + 1, price_str,
text_color=color.new(C_TEXT, 40), text_size=size.tiny,
bgcolor=C_BG, text_halign=text.align_right,
width=board_cell_w * 1.5, height=board_cell_h)
// Columna leyenda (col 11)
table.cell(tbl_board, 11, row_idx + 1, "",
bgcolor=C_BG, text_size=size.tiny,
width=board_cell_w * 0.3, height=board_cell_h)
// Celdas de juego (cols 1-10)
cell_color = C_BG
cell_text = ""
cell_tcolor = C_BG
has_struct = false
// Verificar qué estructura ocupa esta fila
if array.size(fvg_top) > 0
for i = 0 to array.size(fvg_top) - 1
if not array.get(fvg_mitig, i)
t = array.get(fvg_top, i)
b = array.get(fvg_bot, i)
if row_price <= t and row_price >= b
cell_color := array.get(fvg_dir, i) == 1 ? color.new(C_FVG_BULL, 50) : color.new(C_FVG_BEAR, 50)
cell_text := "I"
cell_tcolor := color.white
has_struct := true
break
if not has_struct and array.size(ob_top) > 0
for i = 0 to array.size(ob_top) - 1
if not array.get(ob_mitig, i)
t = array.get(ob_top, i)
b = array.get(ob_bot, i)
if row_price <= t and row_price >= b
cell_color := array.get(ob_dir, i) == 1 ? color.new(C_OB_BULL, 50) : color.new(C_OB_BEAR, 50)
cell_text := "O"
cell_tcolor := color.white
has_struct := true
break
if not has_struct and array.size(brk_top) > 0
for i = 0 to array.size(brk_top) - 1
if not array.get(brk_mitig, i)
t = array.get(brk_top, i)
b = array.get(brk_bot, i)
if row_price <= t and row_price >= b
cell_color := array.get(brk_dir, i) == 1 ? color.new(C_BREAKER_BULL, 50) : color.new(C_BREAKER_BEAR, 50)
cell_text := "L"
cell_tcolor := color.white
has_struct := true
break
if not has_struct and array.size(bpr_top) > 0
for i = 0 to array.size(bpr_top) - 1
if not array.get(bpr_mitig, i)
t = array.get(bpr_top, i)
b = array.get(bpr_bot, i)
if row_price <= t and row_price >= b
cell_color := color.new(C_BPR, 50)
cell_text := "S"
cell_tcolor := color.white
has_struct := true
break
if not has_struct and array.size(liq_top) > 0
for i = 0 to array.size(liq_top) - 1
if not array.get(liq_mitig, i)
t = array.get(liq_top, i)
b = array.get(liq_bot, i)
if row_price <= t and row_price >= b
cell_color := array.get(liq_dir, i) == 1 ? color.new(C_LIQ_HIGH, 50) : color.new(C_LIQ_LOW, 50)
cell_text := "T"
cell_tcolor := color.white
has_struct := true
break
// Efecto blink en líneas completas
if blink_until >= bar_index and has_struct
cell_color := bar_index % 2 == 0 ? color.white : color.new(color.white, 60)
// Render celdas 1-10 con la misma info
for col_idx = 1 to 10
table.cell(tbl_board, col_idx, row_idx + 1, col_idx == 5 ? cell_text : "",
text_color=cell_tcolor, text_size=size.tiny,
bgcolor=has_struct ? cell_color : color.new(C_GRID, 80),
text_halign=text.align_center,
width=board_cell_w, height=board_cell_h)
// Footer
table.cell(tbl_board, 0, 21,
"LINES: " + str.tostring(lines_cleared) + " | bolivarbolsa.com",
text_color=C_TEXT, text_size=size.tiny,
bgcolor=color.new(C_BG, 20), text_halign=text.align_center,
width=board_cell_w, height=board_cell_h)
for c = 1 to 11
table.merge_cells(tbl_board, 0, 21, 11, 21)
break
// ============================================================
// RENDER HUD — top-right
// ============================================================
if barstate.islast and i_show_hud and not na(tbl_hud)
// Row 0: Título
table.cell(tbl_hud, 0, 0, "TETRIS-ICT",
text_color=C_TEXT, text_size=size.normal,
bgcolor=color.new(C_BG, 10), text_halign=text.align_center)
table.cell(tbl_hud, 1, 0, "bolivarbolsa.com",
text_color=color.new(C_TEXT, 40), text_size=size.tiny,
bgcolor=color.new(C_BG, 10), text_halign=text.align_center)
// Row 1: Debt Long
dl_str = str.tostring(math.round(debt_long)) + "% " + score_bar(debt_long)
table.cell(tbl_hud, 0, 1, "DEBT ↑ LONG",
text_color=color.new(C_TEXT, 20), text_size=size.small,
bgcolor=C_BG, text_halign=text.align_left)
table.cell(tbl_hud, 1, 1, dl_str,
text_color=sig_color(signal_long), text_size=size.small,
bgcolor=C_BG, text_halign=text.align_right)
// Row 2: Debt Short
ds_str = str.tostring(math.round(debt_short)) + "% " + score_bar(debt_short)
table.cell(tbl_hud, 0, 2, "DEBT ↓ SHORT",
text_color=color.new(C_TEXT, 20), text_size=size.small,
bgcolor=C_BG, text_halign=text.align_left)
table.cell(tbl_hud, 1, 2, ds_str,
text_color=sig_color(signal_short), text_size=size.small,
bgcolor=C_BG, text_halign=text.align_right)
// Row 3: Signal Long / Short
blink_blocked_l = signal_long == "BLOCKED" and bar_index % 2 == 0
blink_blocked_s = signal_short == "BLOCKED" and bar_index % 2 == 0
sig_l_bg = blink_blocked_l ? C_BLOCKED : sig_color(signal_long)
sig_s_bg = blink_blocked_s ? C_BLOCKED : sig_color(signal_short)
table.cell(tbl_hud, 0, 3, "SIG ↑ " + signal_long,
text_color=color.black, text_size=size.small,
bgcolor=color.new(sig_l_bg, 20), text_halign=text.align_center)
table.cell(tbl_hud, 1, 3, "SIG ↓ " + signal_short,
text_color=color.black, text_size=size.small,
bgcolor=color.new(sig_s_bg, 20), text_halign=text.align_center)
// Row 4: Next mitigación
next_str = na(nearest_price) ? "---" : nearest_name + " @ " + str.tostring(math.round(nearest_price, 2))
table.cell(tbl_hud, 0, 4, "NEXT MITIG",
text_color=color.new(C_TEXT, 20), text_size=size.small,
bgcolor=C_BG, text_halign=text.align_left)
table.cell(tbl_hud, 1, 4, next_str,
text_color=na(nearest_price) ? color.new(C_TEXT, 50) : nearest_color,
text_size=size.small, bgcolor=C_BG, text_halign=text.align_right)
// Row 5: Lines cleared
table.cell(tbl_hud, 0, 5, "LINES CLEARED",
text_color=color.new(C_TEXT, 20), text_size=size.small,
bgcolor=C_BG, text_halign=text.align_left)
table.cell(tbl_hud, 1, 5, str.tostring(lines_cleared),
text_color=C_TEXT, text_size=size.normal,
bgcolor=C_BG, text_halign=text.align_right)
// ============================================================
// RENDER NEXT-PIECE PREVIEW — top-left
// ============================================================
// Formas 4×4 para cada tipo de estructura
get_piece_shape(type_str) =>
// Devuelve array de 16 ints (4x4), 1=celda activa
shape = array.new<int>(16, 0)
if type_str == "FVG"
// I-piece: fila completa
array.set(shape, 0, 1)
array.set(shape, 1, 1)
array.set(shape, 2, 1)
array.set(shape, 3, 1)
else if type_str == "OB"
// O-piece: cuadrado 2x2
array.set(shape, 0, 1)
array.set(shape, 1, 1)
array.set(shape, 4, 1)
array.set(shape, 5, 1)
else if type_str == "BREAKER"
// L-piece: columna + esquina
array.set(shape, 0, 1)
array.set(shape, 4, 1)
array.set(shape, 8, 1)
array.set(shape, 9, 1)
else if type_str == "BPR"
// S-piece
array.set(shape, 1, 1)
array.set(shape, 2, 1)
array.set(shape, 4, 1)
array.set(shape, 5, 1)
else if type_str == "LIQ"
// T-piece
array.set(shape, 0, 1)
array.set(shape, 1, 1)
array.set(shape, 2, 1)
array.set(shape, 5, 1)
shape
if barstate.islast and i_show_next and not na(tbl_next)
// Row 0: Header
table.cell(tbl_next, 0, 0, "NEXT", text_color=C_LIQ_HIGH,
text_size=size.small, bgcolor=color.new(C_BG, 10),
text_halign=text.align_center)
for c = 1 to 5
table.merge_cells(tbl_next, 0, 0, 5, 0)
break
// Rows 1-4: grilla 4×4 de la pieza
piece_shape = get_piece_shape(nearest_name)
piece_col = nearest_color
for pr = 0 to 3
for pc = 0 to 3
cell_val = array.get(piece_shape, pr * 4 + pc)
table.cell(tbl_next, pc + 1, pr + 1, "",
bgcolor=cell_val == 1 ? color.new(piece_col, 30) : color.new(C_BG, 80),
text_size=size.tiny)
// Columna 0 (margen izq) y col 5 (margen der) — vacías
for pr = 1 to 4
table.cell(tbl_next, 0, pr, "", bgcolor=C_BG)
table.cell(tbl_next, 5, pr, "", bgcolor=C_BG)
// Row 5: Nombre estructura
table.cell(tbl_next, 0, 5, nearest_name,
text_color=nearest_color, text_size=size.small,
bgcolor=C_BG, text_halign=text.align_center)
for c = 1 to 5
table.merge_cells(tbl_next, 0, 5, 5, 5)
break
// Row 6: Precio
price_str_next = na(nearest_price) ? "---" : str.tostring(math.round(nearest_price, 2))
table.cell(tbl_next, 0, 6, price_str_next,
text_color=color.new(nearest_color, 20), text_size=size.small,
bgcolor=C_BG, text_halign=text.align_center)
for c = 1 to 5
table.merge_cells(tbl_next, 0, 6, 5, 6)
break
// Row 7: vacía
table.cell(tbl_next, 0, 7, "", bgcolor=C_BG)
for c = 1 to 5
table.merge_cells(tbl_next, 0, 7, 5, 7)
break
// ============================================================
// PLOTS SILENCIOSOS — para alertas JSON vía {{plot()}}
// ============================================================
plot(debt_long, "Debt Long", display=display.none)
plot(debt_short, "Debt Short", display=display.none)
plot(lines_cleared, "Lines Cleared", display=display.none)
// ============================================================
// ALERTCONDITIONS — 5 condiciones de alerta
// ============================================================
// 1. Blocked Long — transición a BLOCKED
alertcondition(
signal_long == "BLOCKED" and signal_long[1] != "BLOCKED",
title="Blocked Long",
message='{"event":"TETRIS_BLOCKED_LONG","pair":"{{ticker}}","tf":"{{interval}}","debt":{{plot("Debt Long")}},"source":"bolivarbolsa"}')
// 2. Green Long — transición a GREEN
alertcondition(
signal_long == "GREEN" and signal_long[1] != "GREEN",
title="Green Long",
message='{"event":"TETRIS_GREEN_LONG","pair":"{{ticker}}","tf":"{{interval}}","debt":{{plot("Debt Long")}},"source":"bolivarbolsa"}')
// 3. Green Short — transición a GREEN (short)
alertcondition(
signal_short == "GREEN" and signal_short[1] != "GREEN",
title="Green Short",
message='{"event":"TETRIS_GREEN_SHORT","pair":"{{ticker}}","tf":"{{interval}}","debt":{{plot("Debt Short")}},"source":"bolivarbolsa"}')
// 4. Mitigación detectada
alertcondition(
mitig_event,
title="Mitigación ICT",
message='{"event":"TETRIS_MITIGATED","pair":"{{ticker}}","tf":"{{interval}}","source":"bolivarbolsa"}')
// 5. Línea completa — Level Up
alertcondition(
lines_cleared > lines_cleared[1],
title="Line Cleared (Level Up)",
message='{"event":"TETRIS_LEVEL_UP","pair":"{{ticker}}","tf":"{{interval}}","lines":{{plot("Lines Cleared")}},"source":"bolivarbolsa"}')
Frequently asked questions
Is it free?
Yes, completely free. No subscription or payment needed. You only need a TradingView account (the free plan is enough).
Which timeframes work best?
Any timeframe. The Bolívar Bolsa ICT/SMC methodology uses H4 for context and M15/M5 for detailed structure.
Does it work on XAUUSD and Forex?
Yes. It detects structural imbalances on any TradingView instrument: XAUUSD, currency pairs, indices and crypto. ICT logic is universal.
Do I need a paid TradingView account?
No. The free plan lets you run Pine Script v6 indicators without restriction.
Exclusively educational tool for studying ICT/SMC concepts; does not constitute financial advice and does not guarantee results. Trading involves risk of capital loss.