Tetris ICT [Bolívar]: tu indicador ICT para TradingView
El mercado guarda memoria. Cada Fair Value Gap, cada Order Block no mitigado es una deuda estructural que el precio terminará por saldar. Tetris ICT [Bolívar] convierte esas deudas en piezas de Tetris en pantalla, para que aprendas a leer el orden oculto del mercado.
Creado por Xavier Mas, fundador de Bolívar Bolsa, para más de 42.000 estudiantes de ICT/SMC en LATAM. Gratuito, sin restricciones, escrito en Pine Script v6.
¿Qué hace?
- Fair Value Gap → pieza I. Detecta desequilibrios de 3 velas y los marca como zonas pendientes de relleno.
- Order Block → pieza O. Identifica la última vela opuesta antes de un movimiento expansivo: el origen institucional del impulso.
- Balanced Price Range (BPR) → pieza S. Mapea la superposición de dos FVG opuestos, zona de máxima eficiencia de precio.
- Breaker Block → pieza L. Detecta Order Blocks que han fallado y se han convertido en estructura inversa.
- Zonas de liquidez → pieza T. Marca acumulaciones de stops (iguales altos/bajos) donde el precio tiende a barrer antes de revertir.
- HUD lateral tipo arcade. Agrupa las piezas activas (no mitigadas) con un contador de deuda estructural en tiempo real.
Cómo se lee el score
El HUD calcula cuántas zonas no mitigadas quedan activas sobre el precio y muestra uno de tres estados:
| Score | Estado | Lectura educativa |
|---|---|---|
| ≥ 65 | BLOCKED | Alta densidad de deuda estructural. El precio enfrenta zonas pendientes en múltiples planos. |
| 30 – 64 | CAUTION | Densidad moderada. Algunas zonas sin mitigar pueden influir en el recorrido. |
| < 30 | GREEN | Tablero limpio. Pocas deudas activas; el precio se mueve en espacio abierto. |
La siguiente pieza (next-piece preview) anticipa qué tipo de zona aparece más cerca del precio. Cada pieza desaparece del tablero cuando el precio la mitiga, igual que una fila completa en Tetris.
Cómo instalarlo en TradingView
- Abre el Pine Editor. En TradingView, pestaña Pine Editor (parte inferior).
- Pega el código. Copia el código completo de abajo y reemplaza el contenido del editor.
- Agrégalo al gráfico. Botón «Add to chart» (arriba a la derecha del editor).
- Guárdalo. Pulsa Ctrl/Cmd + S y ponle un nombre. Quedará en tu biblioteca de TradingView.
Código Pine Script v6
Tetris-ICT [Bolívar] · Pine v6 · 1137 líneas · Gratis.
// ============================================================
// 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"}')
Preguntas frecuentes
¿Es gratis?
Sí, completamente gratis. Sin suscripción ni pago. Solo necesitas una cuenta TradingView (el plan gratuito basta).
¿En qué temporalidades funciona mejor?
En cualquier temporalidad. En la metodología ICT/SMC de Bolívar Bolsa se trabaja H4 para contexto y M15/M5 para estructura de detalle.
¿Funciona en XAUUSD y Forex?
Sí. Detecta deudas estructurales en cualquier instrumento de TradingView: XAUUSD, divisas, índices y criptomonedas. La lógica ICT es universal.
¿Necesito una cuenta de pago en TradingView?
No. El plan gratuito permite ejecutar indicadores en Pine Script v6 sin restricción de uso.
Herramienta exclusivamente educativa para el estudio de conceptos ICT/SMC; no constituye asesoramiento financiero ni garantiza resultados. El trading implica riesgo de pérdida de capital.