// This work is licensed under a Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) // https://creativecommons.org/licenses/by-nc-sa/4.0/ // Based on / derived from: "Smart Money Concepts [LuxAlgo]" and "Liquidity Swings [LuxAlgo]" © LuxAlgo // // ===================================================================================================================== // SMC LIQUIDITY SNIPER — VERSIÓN ACCIONES // Motor de entradas: barrido de liquidez, BOS/CHoCH, medio cuerpo sobre la línea, vela dominante, Liquidez+FVG. // Ese motor es el mismo que la versión de futuros y no se ha tocado: es acción del precio y no sabe qué instrumento // está mirando. Lo que cambia por completo es la capa de dinero. // ===================================================================================================================== // // A1) TAMAÑO POR RIESGO FIJO — el cambio de fondo // En futuros el tamaño era fijo ($20/punto × contratos) y para que la pérdida cupiera en el tope había que // RECORTAR EL STOP. Eso metía el stop dentro del ruido y mataba operaciones que iban a ganar. // Aquí es al revés. El stop se queda donde el mercado dice, y lo que se adapta es cuántas acciones se compran: // acciones = riesgo_permitido / distancia_al_stop // Con $350 de riesgo: acción de $40 con stop a $0.50 -> 700 acciones. Acción de $400 con stop a $6 -> 58. // En las dos arriesgas $350 exactos. El tope de pérdida deja de ser un recorte y pasa a ser la regla de tamaño, // que es como debe funcionar. // Consecuencia técnica: NO existe un "dólares por punto" global. Cada operación tiene el suyo, congelado al // abrir en `tPP` (que en acciones es, literalmente, el número de acciones). Todo el dinero del script pasa por ahí. // // A2) DOS FRENOS AL TAMAÑO, imprescindibles con acciones baratas // Un stop de $0.04 haría que la fórmula pidiera 8.750 acciones. `maxPosUsd` y `maxSharesIn` cortan eso. // Cuando muerden, tu riesgo real es MENOR que el presupuestado; el dashboard lleva la cuenta para que lo sepas. // // A3) SALIDAS PARCIALES POR PORCENTAJE (40/35/25 por defecto) // Aquí sí se puede repartir la posición, porque las acciones se dividen y un contrato de NQ no. El resto de la // división entera va al último tramo activo, para que la posición salga entera y no queden picos colgados. // // A4) HUECOS DE APERTURA — la limitación que NO se puede arreglar // Elegiste permitir posiciones overnight, así que esto hay que decirlo claro: una acción abre con hueco. // Si abre por debajo de tu stop, la orden se ejecuta EN LA APERTURA, no en tu nivel, y pierdes más de los $400. // Ningún stop lo evita. Ni éste ni el de tu bróker. Lo único que evita el riesgo de hueco es no dormir con la // posición abierta (`eodFlat`, apagado por defecto porque tú pediste dejarlas correr). // Lo que sí hace el script: detecta el hueco en la primera vela y sale a mercado sin esperar, en vez de dejar // la posición corriendo a ver qué pasa. Y lleva un contador de cuántas veces te ha ocurrido. // TU TOPE DE $400 ES REAL INTRADÍA Y ORIENTATIVO DE UN DÍA PARA OTRO. No es un fallo del código; es cómo // funcionan las acciones. // // A5) TODO RELATIVO AL PRECIO, no en valores absolutos // El "pegado a liquidez" pasa de 5 puntos fijos a un múltiplo del ATR. 5 puntos son el 12% en un valor de $40 // y el 1% en uno de $400: un número absoluto no puede servir para las dos. // Por el mismo motivo el modo de objetivos por defecto es "Liquidity + R:R", que es relativo al riesgo. // El modo "Dollar targets" sigue estando, pero sólo tiene sentido si operas siempre el mismo valor. // // A6) SESIÓN ENCENDIDA POR DEFECTO (09:30-16:00) // Al revés que en futuros. En pre-market y after-hours el volumen es una fracción del real: los pivotes crean // niveles de liquidez falsos y el filtro de volumen deja pasar cualquier cosa. // // A7) FILTROS DE CALIDAD DEL VALOR (grupo nuevo) // Precio mínimo, volumen mínimo en dólares y rango de ATR en % del precio. Pine no puede leer el spread, así // que el precio mínimo es el único proxy disponible: en un valor de $1.50 un spread de $0.02 es el 1.3% de tu // entrada y te comes medio TP1 sólo en entrar y salir. // // LO QUE SE HEREDA DE LA VERSIÓN DE FUTUROS (sin cambios) // Tope de pérdida con cierre forzoso · trailing que sólo mueve el stop a favor · timeout · cancelación por // falta de progreso · registro interno · webhook · dashboard · tabla de rendimiento por setup. // // COSTES: comisión 0,5 centavos por acción y 2 ticks de slippage. Realista para large caps líquidas y OPTIMISTA // para small caps. Si operas valores de bajo precio, sube el slippage antes de creerte el backtest. // ===================================================================================================================== //@version=6 strategy('SMC Liquidity Sniper — Acciones', 'SMC Stocks', overlay = true, max_lines_count = 500, max_labels_count = 500, max_boxes_count = 500, initial_capital = 25000, currency = currency.USD, default_qty_type = strategy.fixed, default_qty_value = 100, pyramiding = 0, calc_on_every_tick = false, calc_on_order_fills = false, process_orders_on_close = true, commission_type = strategy.commission.cash_per_contract, commission_value = 0.005, slippage = 2) //===================================================================================================================== // INPUTS //===================================================================================================================== gG = '⚙️ General' atrLen = input.int(200, 'ATR length', minval = 1, group = gG) volLen = input.int(50, 'Volume average', minval = 1, group = gG) volBase = input.string('Median', 'Volume reference', options = ['Median', 'Average'], group = gG, tooltip = 'Median ignores the opening spike. Average gets inflated by it for the next `Volume average` bars and blocks every signal.') momLen = input.int(5, 'Momentum bars', minval = 1, group = gG) tzOpt = input.string('America/New_York', 'Timezone for displayed clocks', options = ['Chart exchange', 'America/New_York', 'America/Chicago', 'America/Los_Angeles', 'UTC', 'Europe/London', 'Europe/Madrid'], group = gG) gL = '💧 Liquidity' pivLen = input.int(5, 'Pivot lookback (liquidity)', minval = 1, maxval = 100, group = gL) areaMode = input.string('Wick Extremity', 'Liquidity area', options = ['Wick Extremity', 'Full Range'], group = gL) maxLevels = input.int(20, 'Max levels per side', minval = 2, maxval = 25, group = gL) showLiq = input.bool(false, 'Draw liquidity levels', group = gL) showLiqLbl = input.bool(false, 'Show volume · touches', group = gL) gS = '🏗️ Structure (SMC)' intLen = input.int(5, 'Internal structure', minval = 2, group = gS) swgLen = input.int(25, 'Swing structure', minval = 5, group = gS) showIntBos = input.bool(false, 'Show internal BOS / CHoCH', group = gS, tooltip = 'Off by default: on 1m this fires on every micro pivot and floods the 500-line budget, which makes TradingView drop older drawings including entry marks.') showSwgBos = input.bool(true, 'Show swing BOS / CHoCH', group = gS) bosWidth = input.int(1, 'BOS line width', minval = 1, maxval = 4, group = gS) extendBos = input.bool(true, 'Extend BOS lines to the right', group = gS) colorBars = input.bool(false, 'Color bars with trend', group = gS) gV = '🕯️ Candle Dominance' useCandle = input.bool(true, 'Enable candle system', group = gV) candleFilter = input.bool(true, 'Use it as a filter for other entries', group = gV) candleEntry = input.bool(false, 'Generate its own entries', group = gV, tooltip = 'Off by default: in testing this engine produced 416 trades at 66% win rate for -$8990. Keep it as a filter, not as an entry.') candleDom = input.float(1.15, 'Right candle must exceed left candle (x)', step = 0.05, minval = 1.0, group = gV) candleMinBody = input.float(0.05, 'Min left-candle body (x ATR)', step = 0.01, minval = 0.0, group = gV, tooltip = 'FIX B — without this a doji (body = 0) makes every following candle \'dominant\', because `body > 0 * 1.15` is true for any candle at all.') candleVolDom = input.float(1.00, 'Min volume vs left candle (x)', step = 0.05, minval = 0, group = gV) zoneMode = input.string('Both (recommended)', 'Zone definition', options = ['Premium / Discount', 'Structure', 'Both (recommended)', 'Liquidity / OB'], group = gV) zoneLen = input.int(50, 'Premium/Discount range', minval = 10, group = gV) zoneNear = input.float(1.0, 'Liquidity proximity (x ATR)', step = 0.1, minval = 0.1, group = gV) showCandle = input.bool(false, 'Color the dominant candle', group = gV) oppositeOnly = input.bool(true, 'Left candle must be the opposite color (reversal)', group = gV) showDots = input.bool(false, 'Mark dominant candles with dots', group = gV) gF = '🧩 Liquidity + FVG' useFvgSetup = input.bool(true, 'Liquidity + FVG entries', group = gF) showFvg = input.bool(true, 'Draw fair value gaps', group = gF) fvgMinAtr = input.float(0.15, 'Min FVG size (x ATR)', step = 0.05, minval = 0, group = gF) mssWindow = input.int(12, 'Max bars from sweep to MSS', minval = 1, group = gF) fvgWindow = input.int(12, 'Max bars from MSS to FVG', minval = 1, group = gF) mitWindow = input.int(20, 'Max bars waiting for mitigation', minval = 1, group = gF) seqStrict = input.bool(false, 'Each step of the sequence needs its own candle', group = gF, tooltip = 'FIX A — the mitigation step ALWAYS requires a later candle now (that was the bug). This extra option also forces the sweep, the MSS and the FVG onto separate candles, so one huge candle cannot complete the whole sequence by itself.') fvgUseSwing = input.bool(false, 'Require a swing MSS (stricter)', group = gF) markSeq = input.bool(true, 'Tag the sweep and the MSS on the chart', group = gF) maxFvg = input.int(10, 'Max FVG boxes on screen', minval = 1, maxval = 40, group = gF) gE = '🎯 Entries' mode = input.string('Aggressive', 'Mode', options = ['Aggressive', 'Balanced', 'Conservative'], group = gE) useSweep = input.bool(true, 'Liquidity sweep entries', group = gE) useBreak = input.bool(true, 'CHoCH/BOS entries with displacement', group = gE) useBosHalf = input.bool(true, 'Half-body close beyond a BOS / CHoCH line', group = gE) halfRatio = input.float(0.5, 'Body fraction beyond the line', step = 0.1, minval = 0.1, maxval = 1.0, group = gE) bosSrc = input.string('Both', 'Lines used for that entry', options = ['Both', 'Swing only', 'Internal only'], group = gE) minVolRatio = input.float(0.5, 'Min volume (x reference)', step = 0.1, minval = 0, group = gE) dispMult = input.float(0.55, 'Min displacement (x ATR)', step = 0.05, minval = 0, group = gE) confirmBars = input.int(8, 'Sweep validity (bars)', minval = 1, group = gE) cooldownBar = input.int(2, 'Min bars between signals', minval = 0, group = gE) useCancel = input.bool(true, 'Cancel the order if it makes no progress', group = gE) cancelBars = input.int(8, 'Bars to confirm progress', minval = 1, group = gE) cancelR = input.float(0.25, 'Min progress in favor (R)', step = 0.05, minval = 0.05, group = gE) cancelOffset = input.float(1.8, 'Cancel label distance (x ATR)', step = 0.1, minval = 0.2, group = gE) cancelWipe = input.bool(true, 'Wipe the whole trade off the chart when the order is cancelled', group = gE) cancelNotice = input.bool(true, 'Leave the CANCELLED notice on the chart', group = gE) rearmLvl = input.bool(true, 'Re-arm the BOS level after a cancelled order', group = gE) signalOffset = input.float(1.4, 'Signal label distance (x ATR)', step = 0.1, minval = 0.2, group = gE) earlySignal = input.bool(false, 'Fire the signal before the candle closes', group = gE, tooltip = 'S4 — OFF by default in the strategy version. It repaints by design: the signal is judged on a provisional close and the order cannot be un-sent if the candle turns. With it on, the backtest and the live behaviour stop being the same thing.') earlySecs = input.int(15, 'Seconds before close', minval = 1, maxval = 60, group = gE) useSession = input.bool(true, 'Filtro de sesión', group = gE, inline = 'ses') sessInput = input.session('0930-1600', '', group = gE, inline = 'ses', tooltip = 'A6 — ENCENDIDO por defecto, al revés que en futuros. En pre-market y after-hours el volumen es una fracción del real, así que los pivotes crean niveles de liquidez falsos y el filtro de volumen deja pasar cualquier cosa. NQ cotiza casi 24h y no tiene ese problema; una acción sí.') gQ = '🏦 Filtros del valor · calidad del subyacente' minPrice = input.float(3.0, 'Precio mínimo ($)', step = 0.5, minval = 0, group = gQ, tooltip = 'A7 — por debajo de esto el spread se come la operación. En un valor de $1.50 un spread de $0.02 es el 1.3% de tu entrada: te comes medio TP1 sólo en entrar y salir. Pine no puede medir el spread, así que el precio es el único filtro disponible.') maxPriceIn = input.float(0, 'Precio máximo ($), 0 = sin límite', step = 10, minval = 0, group = gQ) minDollarVol = input.float(2000000, 'Volumen mínimo en $ por vela (media)', step = 250000, minval = 0, group = gQ, tooltip = 'Volumen medio × precio. Filtra los valores donde tu propia orden movería el precio. Bájalo si operas small caps de verdad, súbelo si sólo quieres large caps.') minAtrPct = input.float(0.10, 'ATR mínimo (% del precio)', step = 0.05, minval = 0, group = gQ, tooltip = 'Descarta valores dormidos: si el ATR es el 0.02% del precio no hay recorrido que capturar y los objetivos nunca se tocan.') maxAtrPct = input.float(0, 'ATR máximo (% del precio), 0 = sin límite', step = 0.5, minval = 0, group = gQ, tooltip = 'Para no operar el día de resultados o un valor en pleno desplome. Un ATR del 8% del precio no es una oportunidad, es un valor roto.') gT = '💰 TP / SL' maxLossUsd = input.float(400, '★ PÉRDIDA MÁXIMA POR OPERACIÓN ($)', step = 25, minval = 25, group = gT, tooltip = 'R1 — techo absoluto. El stop nunca se coloca a una distancia que valga más que esto. Si el stop técnico queda más lejos se recorta (o se descarta la señal, según la opción de abajo). Comisión y slippage van aparte: deja margen si necesitas que el techo sea el importe final.') panicLossUsd = input.float(350, '★ CERRAR LA OPERACIÓN AL ir en contra ($)', step = 25, minval = 25, group = gT, tooltip = 'R2 — el disparador de cierre. Se convierte en el stop real que vive en el mercado, no en una comprobación al cierre de vela. Si lo pones por encima de la pérdida máxima, manda la pérdida máxima.') gapGuard = input.bool(true, 'Cerrar a mercado si un hueco de apertura salta el stop', group = gT, tooltip = 'A4 — LA LIMITACIÓN QUE NO SE PUEDE ARREGLAR DEL TODO. Una acción abre con hueco. Si abre por debajo de tu stop, la orden se ejecuta en la apertura, no en tu nivel, y la pérdida es mayor de lo que pediste. Esto no lo evita nadie. Lo que hace esta opción es detectarlo en la primera vela y salir a mercado sin esperar, en vez de dejar la posición corriendo.') eodFlat = input.bool(false, 'Cerrar todo antes del cierre de mercado', group = gT, tooltip = 'La única forma de que el tope de pérdida sea REAL es no dormir con posiciones abiertas. Elegiste dejar correr las operaciones, así que esto viene desactivado — pero está aquí si cambias de idea.') eodSess = input.session('1550-1600', 'Ventana de cierre forzoso', group = gT) minProfitUsd = input.float(150, '★ MINIMUM PROFIT PER TRADE ($)', step = 10, minval = 1, group = gT, tooltip = 'S3 — the master threshold. TP1 can never be worth less than this; a signal whose TP1 does not reach it is discarded; once TP1 is touched the stop locks at least this amount; and the trailing stop never secures less than this. It also drives the log filter and the "Trades >= $" counter.') tpMode = input.string('Liquidity + R:R', 'Cómo se colocan los objetivos', options = ['Dollar targets', 'Liquidity + R:R'], group = gT, tooltip = 'A5 — por defecto R:R, al revés que en futuros. Los objetivos en dólares fijos ($250/$600/$1500) NO escalan entre valores: en una acción de $40 con 700 acciones, $250 son $0.36 de recorrido; en una de $400 con 58 acciones son $4.31. Usa Dollar targets sólo si operas siempre el mismo valor.') tp1Usd = input.float(250, 'TP1 ($)', step = 25, minval = 1, group = gT, tooltip = 'Raised to the minimum profit above if it is set lower.') tp2Usd = input.float(600, 'TP2 ($)', step = 25, minval = 1, group = gT) tp3Usd = input.float(1500, 'TP3 ($)', step = 50, minval = 1, group = gT) enforceMinTp = input.bool(true, 'Reject signals whose TP1 does not reach the minimum profit', group = gT) snapAtr = input.float(0.25, 'Pegar el objetivo a la liquidez cercana si está a (x ATR), 0 = off', step = 0.05, minval = 0, group = gT, tooltip = 'A5 — en la versión de futuros esto eran 5 puntos fijos. En acciones no sirve: 5 puntos son un 12% en un valor de $40 y un 1% en uno de $400. Ahora es relativo al ATR, así que escala solo.') useRFloor = input.bool(false, 'Also require the minimum R:R below', group = gT, tooltip = 'With this off, `Dollar targets` ignores risk entirely: on a wide-ATR candle a 30-point stop can be paired with a 12-point TP1. Turn it on if you never want R:R below the values set here.') slBuf = input.float(0.30, 'SL buffer (x ATR)', step = 0.05, minval = 0, group = gT) slLook = input.int(2, 'SL lookback (bars used for the swing)', minval = 1, maxval = 20, group = gT, tooltip = 'FIX F — was hard-wired to 2. Raise it if the stop keeps landing inside the noise.') worstCase = input.bool(true, 'Resolve ambiguous bars as losses', group = gT, tooltip = 'FIX E — when one candle contains both TP3 and the stop there is no way to know which came first. On, the stop wins (honest). Off, TP3 wins (the old, optimistic behaviour).') rr1 = input.float(1.0, 'Min R:R TP1', step = 0.1, minval = 0.1, group = gT) rr2 = input.float(2.0, 'Min R:R TP2', step = 0.1, minval = 0.2, group = gT) rr3 = input.float(3.0, 'Min R:R TP3', step = 0.1, minval = 0.3, group = gT) tp3Ext = input.float(1.5, 'ATR extension for TP3', step = 0.1, minval = 0, group = gT) volBoost = input.bool(true, 'Widen TP on high volume', group = gT) rrMax = input.float(8.0, 'Max allowed R:R', step = 0.5, minval = 1, group = gT) useTimeout = input.bool(true, 'Close the trade if it never resolves', group = gT, tooltip = 'Without this, one trade that never reaches TP3 or SL keeps `freeSl` false forever and blocks every later signal.') maxBars = input.int(45, 'Close the trade after (bars)', minval = 5, group = gT) showTrade = input.bool(true, 'Draw active trade', group = gT) trailOn = input.bool(true, 'Trail the stop into profit (TP1 → break-even, TP2 → TP1)', group = gT) trailBE = input.float(150, 'Profit locked in as soon as TP1 is touched ($)', step = 10, minval = 0, group = gT, tooltip = 'Never applied below the minimum profit above.') trailFollow = input.bool(true, 'Stop follows the profit from the very first move', group = gT) trailGap = input.float(100, 'Cushion left behind the best profit ($)', step = 25, minval = 25, group = gT) trailStep = input.float(50, 'The stop only moves in steps of ($), 0 = continuous', step = 25, minval = 0, group = gT) logAtTp = input.bool(true, 'Log the trade as a winner as soon as TP1 is touched', group = gT) clearOnClose = input.bool(true, 'Clear the trade from the chart when it hits TP or SL', group = gT, tooltip = 'The whole trade, signal mark included, leaves the chart the moment it resolves. Only the live trade is ever on screen.') showPnl = input.bool(true, 'Live profit line while the trade runs', group = gT) pnlColor = input.color(#ffe000, 'Live profit line color', group = gT) pnlOffset = input.int(5, 'Live profit label distance (bars to the right)', minval = 0, maxval = 30, group = gT) pnlGap = input.float(0.0, 'Live profit label height offset (x ATR)', step = 0.1, group = gT) showCont = input.bool(true, 'Mark continuation candles', group = gT) contStep = input.float(0.5, 'Progress between continuation marks (R)', step = 0.1, minval = 0.1, group = gT) contKeep = input.int(1, 'Continuation marks kept on screen', minval = 1, maxval = 10, group = gT) gD = '📅 Día anterior · PDH / PDL' showPD = input.bool(true, 'Dibujar el máximo y el mínimo del día anterior', group = gD, tooltip = 'PDH (Previous Day High) y PDL (Previous Day Low). En SMC son de los charcos de liquidez más fiables que existen: mucho más gente los está mirando que a un pivote de 5 velas.\n\nSON SÓLO DIBUJO. No generan entradas, no mueven objetivos y no filtran nada. Tus resultados no cambian ni un dólar por activar esto.') pdSource = input.string('Día completo del gráfico', 'De qué día', options = ['Día completo del gráfico'], group = gD, tooltip = 'El máximo y el mínimo de la vela diaria tal como la da TradingView. En NQ eso incluye la sesión de noche; en acciones es sólo el horario regular.') pdhCss = input.color(#f23645, 'PDH', group = gD, inline = 'pdc') pdlCss = input.color(#089981, 'PDL', group = gD, inline = 'pdc') pdStyleIn = input.string('Discontinua', 'Estilo de línea', options = ['Sólida', 'Discontinua', 'Punteada'], group = gD) pdWidth = input.int(1, 'Grosor', minval = 1, maxval = 4, group = gD) pdLabels = input.bool(true, 'Etiqueta con el precio', group = gD) pdKeep = input.bool(false, 'Dejar en el gráfico los niveles de días pasados', group = gD, tooltip = 'Apagado: sólo ves el PDH/PDL de hoy, y la línea se mueve cada día. Encendido: cada día deja su par de líneas congelado, útil para revisar hacia atrás dónde estaban los niveles. Ojo con el presupuesto de 500 líneas de TradingView si miras mucho histórico.') pdBackPad = input.int(10, 'Barras que la línea se extiende a la derecha', minval = 0, maxval = 100, group = gD) gX = '⚠️ Liquidation' proxAtr = input.float(3.0, 'Detection radius (x ATR)', step = 0.5, minval = 0.5, group = gX) liqThresh = input.int(65, 'Warning threshold (0-100)', minval = 1, maxval = 100, group = gX) showWarn = input.bool(false, 'Mark warnings on chart (alerts stay active)', group = gX) gZ = '📐 Tamaño de posición · riesgo fijo en $' riskPerTrade = input.float(350, '★ RIESGO POR OPERACIÓN ($)', step = 25, minval = 10, group = gZ, tooltip = 'A1 — el corazón de esta versión. NO fijas un número de acciones: fijas cuánto estás dispuesto a perder. El script divide ese dinero entre la distancia al stop y de ahí salen las acciones.\n\nEjemplo: riesgo $350. En una acción de $40 con el stop a $0.50 -> 700 acciones. En una de $400 con el stop a $6 -> 58 acciones. En las dos arriesgas los mismos $350.\n\nAsí el stop se queda donde es técnicamente correcto y el que se adapta es el tamaño. Es al revés que en la versión de futuros, donde había que recortar el stop.') maxPosUsd = input.float(25000, 'Valor máximo de la posición ($)', step = 1000, minval = 100, group = gZ, tooltip = 'A2 — el freno imprescindible. Si el stop sale muy pegado (típico en una acción de $3), la fórmula del riesgo pide una barbaridad de acciones. Este tope corta ahí: nunca se compran más acciones de las que caben en este importe.') maxSharesIn = input.int(20000, 'Máximo de acciones', minval = 1, group = gZ, tooltip = 'Segundo freno, por si operas valores de muy bajo precio donde el tope en $ todavía deja un número de acciones que el libro no absorbe.') minSharesIn = input.int(1, 'Mínimo de acciones para abrir', minval = 1, group = gZ, tooltip = 'Si el riesgo por acción es tan grande que no sale ni este mínimo, la señal se descarta. Con acciones caras y stops anchos pasa.') roundLot = input.bool(false, 'Redondear a lotes de 100', group = gZ, tooltip = 'Algunos brókers dan mejor ejecución en lotes redondos. Redondea hacia abajo, así que nunca sube el riesgo.') tp1Pct = input.float(40, '% de la posición que sale en TP1', step = 5, minval = 0, maxval = 100, group = gZ, tooltip = 'A3 — aquí sí puedes partir la posición, porque las acciones se dividen y un contrato de NQ no. Los tres porcentajes se normalizan a 100 si no suman exacto.') tp2Pct = input.float(35, '% de la posición que sale en TP2', step = 5, minval = 0, maxval = 100, group = gZ) tp3Pct = input.float(25, '% de la posición que sale en TP3', step = 5, minval = 0, maxval = 100, group = gZ) gR = '📒 Trade Log' showLog = input.bool(true, 'Show trade log table', group = gR) showStats = input.bool(true, 'Show setup performance table', group = gR) statsPos = input.string('Top left', 'Performance table position', options = ['Top left', 'Middle left', 'Bottom left', 'Middle right'], group = gR) statsDrop = input.int(3, 'Push the performance table down (rows)', minval = 0, maxval = 12, group = gR) logWinMin = input.bool(true, 'Log winners only above the minimum profit', group = gR, tooltip = 'FIX D — this used to do nothing when `Log the trade as a winner at TP1` was on, because the row already existed. The filter is now re-applied when the trade closes, and a small winner drops off the log at that point.') logRows = input.int(10, 'Trades to display', minval = 1, maxval = 30, group = gR) logPos = input.string('Bottom right', 'Log position', options = ['Bottom right', 'Bottom center', 'Bottom left'], group = gR) logSize = input.string('Tiny', 'Log text size', options = ['Tiny', 'Small', 'Normal'], group = gR) gW = '🔗 Registro de entradas (webhook)' whOn = input.bool(false, 'Enviar cada operación a mi app', group = gW, tooltip = 'Manda un JSON al abrir y otro al cerrar. Sin esto el indicador sólo emite alertas de texto y el registro se queda vacío.') whKey = input.string('', 'Clave del webhook', group = gW, tooltip = 'La variable TV_KEY de la app. Viaja dentro del mensaje porque TradingView no deja poner cabeceras propias en un webhook.') whTag = input.string('', 'Etiqueta de esta gráfica (opcional)', group = gW) whMute = input.bool(true, 'Silenciar las alertas de texto cuando el webhook está activo', group = gW, tooltip = 'Las de texto no son JSON: la app las descarta, pero llenan el historial de alertas de TradingView para nada.') gP = '📊 Dashboard & Style' showShapes = input.bool(true, 'Leave a small arrow on past entries', group = gP, tooltip = 'A little triangle stays on every entry so you can see where they were. The full description box, the stem and the big arrow are still removed when the trade resolves or a new entry appears — only this small mark survives.') shapeSize = input.string('Tiny', 'Small arrow size', options = ['Tiny', 'Small', 'Normal', 'Large'], group = gP) showClock = input.bool(true, 'Countdown above the running candle', group = gP) clockCss = input.color(#ffe000, 'Countdown color', group = gP, inline = 'ck') clockAlert = input.color(#ff1744, 'Last seconds', group = gP, inline = 'ck') clockSize = input.string('Large', 'Countdown size', options = ['Small', 'Normal', 'Large', 'Huge'], group = gP) markBull = input.color(#00e676, 'Signal arrow bullish', group = gP, inline = 'ar') markBear = input.color(#ff1744, 'Signal arrow bearish', group = gP, inline = 'ar') arrowSize = input.string('Large', 'Signal arrow size', options = ['Small', 'Normal', 'Large', 'Huge'], group = gP) clockOffset = input.float(0.5, 'Countdown distance (x ATR)', step = 0.1, minval = 0.1, group = gP) rightPad = input.int(0, 'Bars drawn past the last candle', minval = 0, maxval = 30, group = gP, tooltip = 'Purely cosmetic: how far the drawings extend to the right of the last candle. It has no effect whatsoever on the orders.') sigKeep = input.int(1, 'Signal marks kept on screen', minval = 1, maxval = 60, group = gP, tooltip = '1 means a new entry always removes the previous one. Raise it only if you want to inspect a few recent signals side by side.') keepLatest = input.bool(true, 'Each new sweep / MSS / cancel mark removes the previous one', group = gP) useCleanup = input.bool(false, 'Clear chart marks after a while', group = gP) cleanupMin = input.int(120, 'Clear chart marks after (minutes)', minval = 1, group = gP) cleanBos = input.bool(true, 'Also clear old BOS / CHoCH lines', group = gP) showPanel = input.bool(true, 'Show dashboard', group = gP) panelPos = input.string('Top right', 'Dashboard position', options = ['Top right', 'Top left', 'Bottom right', 'Bottom left', 'Middle right'], group = gP) bullCss = input.color(#089981, 'Bullish', group = gP, inline = 'c') bearCss = input.color(#f23645, 'Bearish', group = gP, inline = 'c') warnCss = input.color(#ff9800, 'Warning', group = gP, inline = 'c') txtSize = input.string('Small', 'Text size', options = ['Tiny', 'Small', 'Normal'], group = gP) //===================================================================================================================== // TYPES AND GLOBAL VARIABLES //===================================================================================================================== // @type Liquidity level (swing high/low) with its area, touch count and accumulated volume type liq float price float top float btm int count float vol bool crossed bool swept int x1 line ln label lb var array hiLv = array.new() // buy-side liquidity (above price) var array loLv = array.new() // sell-side liquidity (below price) // @type Logged trade. res: 1 = closed in profit · -1 = closed at stop loss · 0 = still running · 2 = cancelled type tlog int t // entry time int dir float ent // entry price float ex // exit price float r float usd int tp // best TP reached, 0 if none int res string src // which engine fired the entry var array logArr = array.new() // @type A drawn BOS / CHoCH line kept alive as an entry level type bosLvl float price int dir bool used var array bosArr = array.new() // @type Fair value gap: the three-candle imbalance type fvg float top float btm int dir bool mit box bx var array fvgArr = array.new() // --- Liquidity + FVG state machine: 0 idle · 1 sweep done · 2 MSS done · 3 FVG armed var int sState = 0 var int sDir = 0 var int sBar = na var float sTop = na var float sBtm = na var float fvgEntTop = na var float fvgEntBtm = na // --- chart marks that expire: every drawing is registered with the time it was created var array