Saltar al contenido

252 estrategias y los bugs que se les escaparon

9 min de lectura

A finales de junio, el corpus de paridad de PineForge había pasado de 167 a 252 estrategias de referencia. Tres bugs que nunca llegó a poner a prueba aparecieron en scripts compartidos por la comunidad: una liquidación por margin call que no modelábamos, un chequeo de fondos que ignoraba el tipo de cambio y un trailing stop que, en silencio, nunca se armaba.

Las cifras de este artículo se midieron el 29 de junio de 2026 y se mantienen tal cual. Para ver los resultados actuales, consulta cómo medimos la paridad.

Cuando escribimos por última vez sobre el corpus de paridad, en mayo, tenía 167 estrategias de referencia, 165 de ellas con paridad estricta operación por operación frente a TradingView. Después siguió creciendo: a finales de junio tenía 252 estrategias: 251 con paridad «excellent» (99,6 %), una anomalía documentada y cero fallos.

La cifra del titular es la parte aburrida. Lo interesante es lo que el corpus no llegó a poner a prueba: tres comportamientos del broker de TradingView que no estábamos modelando, cada uno descubierto porque un script compartido por la comunidad recorrió una ruta de código que ninguna de las 252 tocaba. Este artículo los repasa uno a uno.

Dónde estaba el corpus el 2026-06-29

corpus/validation_report.md (2026-06-29)
  Total probes:    252
  Excellent:       251  (99.6%)
  Anomaly:           1
  Strong/Moderate/Weak/Minimal/Missing: 0
 
  TV trades:      389,590
  Engine trades:  389,688
  Matched:        389,468

El motor emitió 98 operaciones más que TradingView en todo el corpus, un delta del 0,025 % sobre 389.590 operaciones de referencia. Esa era la cifra agregada; por estrategia, a la única anomalía que describimos más abajo solo le correspondía una de esas operaciones. El resto venía de 23 estrategias que seguían calificadas como «excellent» según la rúbrica de aquel informe, que admitía una diferencia de menos del 1 % en el número de operaciones: la mayoría se desviaba en una o dos operaciones, y cuatro de ellas sumaban 90 de las 98.

Las seis sondas más recientes de entonces formaban una categoría drawing, añadida cuando lanzamos un runtime capaz de leer los objetos de dibujo de Pine (line.get_price, la geometría de label/box) como datos en tiempo de backtest, no solo como anotaciones del gráfico. Las estrategias que usan una línea dibujada como estado de cálculo (por ejemplo, una línea de tendencia anclada a mano que hace de soporte dinámico) necesitan que el motor siga sirviendo la geometría de objetos que el script ya ha borrado, igual que hace TradingView. Las seis obtuvieron la calificación «excellent».

La única anomalía, sin rodeos

No excluimos la única estrategia que no coincidía. anomaly-equity-mirror-strategy-equity-01 está exactamente en el límite de 1× equity para la admisión por margen. Nuestro análisis de entonces, al que las notas del corpus asignan una confianza del 95 % o más, era que el emulador de broker de TradingView no era monótono justo en ese límite: admitía algunas entradas por encima del equity y rechazaba algunas por debajo, de forma inconsistente, de una barra a otra. Nuestro motor es determinista: mismo equity, misma decisión de admisión, siempre. Eso es correcto según la semántica de margen documentada de Pine y, según ese análisis, no era lo que hacía el broker de TradingView en ese límite concreto. Igualarlo habría supuesto introducir nuestro propio no determinismo, así que no lo hicimos. La diferencia estaba documentada, no escondida, y era la única de las 252. (Desde entonces, la línea base que los mantenedores establecieron el 2026-09-29 ha calificado esta sonda como «excellent», y el README del corpus indica que ese diagnóstico está en revisión.)

Bug uno: la posición que TV habría cerrado a la fuerza

El broker de TradingView no espera a que la lógica de tu estrategia decida cuándo salir de una posición que ha rebasado su requisito de margen: la liquida. Si el equity de una posición apalancada o en corto cae tanto que el margen requerido supera el equity de la cuenta, TradingView ejecuta la liquidación en la misma barra en la que sucede, al peor precio de esa barra para la posición, y cierra cuatro veces la cantidad necesaria para restablecer el margen (como mucho, la posición entera), se cumplan o no las condiciones de strategy.exit().

El motor no modelaba esto. Mantenía la posición y dejaba que la propia lógica de la estrategia la cerrara en algún momento, lo que en una estrategia sin un stop ajustado podía significar decenas de barras con una posición que TradingView ya había liquidado. En 24 scripts compartidos por la comunidad que mantuvieron posiciones apalancadas o en corto hasta una llamada de margen, la lista de operaciones de TradingView incluía salidas «Margin call» que el motor nunca generó.

La corrección añade la liquidación forzada cuando el equity de la cuenta rompe el requisito de margen: calcula el precio de liquidación igual que el broker de TradingView y después cuantiza la cantidad liquidada al incremento de lote del símbolo. TradingView no liquida lotes fraccionarios más pequeños que el incremento de lote del símbolo, y ahora nosotros tampoco, siempre que la ejecución especifique ese incremento.

Esto importa sobre todo a quien hace backtests de estrategias apalancadas o en corto sobre perpetuos. Si tu curva de equity parecía demasiado suave durante un drawdown, es plausible que esta fuera la razón.

Bug dos: la comprobación de fondos que no sabía de divisas

Las estrategias Pine pueden declarar una divisa de cuenta distinta de la divisa de cotización del símbolo: por ejemplo, currency.INR operando un par en USDT. Antes de comprobar si puedes permitirte una orden, el broker de TradingView convierte el valor nocional de la orden a la divisa de tu cuenta con el tipo de cambio vigente y luego lo compara con el equity en esa misma divisa.

La comprobación de fondos del motor se saltaba la conversión. Comparaba directamente el nocional en USDT con un equity denominado en INR, como si el tipo de cambio fuera 1:1, de modo que una estrategia que declaraba currency.INR parecía decenas de veces más asequible de lo que debía (un USDT vale decenas de rupias). En los scripts afectados, el motor generaba aproximadamente el doble de operaciones que TradingView, porque admitía entradas que el broker de TradingView rechazaba por falta de fondos.

La corrección aplica el tipo de cambio de la divisa de la cuenta al margen requerido antes de que se ejecute la comprobación de fondos, en el mismo punto del pipeline de órdenes en el que lo aplica TradingView, con el tipo que indique la ejecución (el motor usa 1:1 por defecto). Si hacías backtests con una divisa de cuenta distinta del USD frente a un símbolo cotizado en USD o USDT, este era el bug que inflaba en silencio tu número de operaciones.

Bug tres: el trailing stop que nunca se armaba

Este es el que más nos habría gustado que un usuario detectara antes que nosotros, porque falla en silencio. strategy.exit() admite dos formas de especificar un trailing stop: trail_points, una distancia relativa a la entrada, y trail_price, un precio de activación absoluto.

// armed correctly today; trail_price-only calls used to never arm at all
strategy.exit("TS", from_entry = "Long", trail_price = close * 1.05, trail_offset = 10)

La condición de armado del motor solo comprobaba trail_points. Una salida con trailing especificada únicamente mediante trail_price compilaba sin errores, se ejecutaba sin errores y nunca se armaba: la posición se quedaba ahí sin ningún trailing stop activo, en silencio, durante todo el backtest. Sin excepción, sin línea de log, sin operación. La estrategia parecía protegida. No lo estaba.

Lo encontramos porque un script compartido por la comunidad que define sus trailing stops únicamente con trail_price solo coincidía con la mitad de las operaciones de TradingView; con la corrección, pasó a coincidir con el 99,7 %. Ninguna sonda del corpus abierto usaba trail_price, así que las cifras del corpus no podían reflejar ese hueco. Justo para ese tipo de hueco existe la categoría validation, según el artículo sobre el desglose del corpus: estrategias sintéticas y acotadas, ni rentables ni realistas, construidas para poner a prueba una sola función de Pine de forma aislada, de modo que una regresión así no pueda esconderse detrás de «las cifras generales se veían bien».

La corrección arma el trailing stop si está presente trail_points o trail_price. Si alguna vez escribiste una estrategia Pine con trail_price y la curva de equity se parecía sospechosamente a la de una estrategia sin trailing stop, es porque no lo había, en ninguna versión del motor anterior a la v0.10.14 (2026-07-02), la primera que incluyó la corrección.

Por qué este es el verdadero sentido de un corpus

Ninguno de estos tres bugs se encontró revisando código. Se encontraron porque una estrategia con la forma adecuada pasó por el motor y produjo una cifra que no coincidía con la de TradingView. Esa es toda la tesis detrás de mantener 167 estrategias, luego 252, y sumando como un corpus fijo y versionado en lugar de un puñado de pruebas de humo, y detrás de someter al mismo diff scripts compartidos por la comunidad, que es donde aparecieron estos tres: una cifra de titular impecable puede esconder un bug que cambia cuánto ganó o perdió una estrategia, y la única forma de detectar ese tipo de bugs es seguir pasando más formas de estrategia por el mismo diff contra la misma referencia.

Seguiremos añadiendo sondas a medida que encontremos huecos, y seguiremos publicando las cifras (las buenas y la única anomalía) tal como estén.

Por dónde seguir