Qué significa realmente el repintado
El repintado ocurre cuando la salida de un script en una barra cambia según cuándo se calculó. La misma barra, el mismo script, dos respuestas distintas: una mientras la barra se formaba y otra después de cerrar, cuando el script recalculó desde el historial.
Se deriva de algo estructural, no de un error. Una barra histórica contiene
cuatro números: apertura, máximo, mínimo y cierre. Una barra en tiempo real
tiene un cierre que aún se mueve, y un máximo y un mínimo que pueden seguir
extendiéndose [1]. Un script que lee high en una barra en formación lee un
valor que no es definitivo. Cuando después vuelve a recorrer esa barra como
historial, lee el definitivo.
No todo repintado es un defecto
Esta es la parte que se pierde, y equivocarse en cualquiera de los dos sentidos es caro.
Repintado que funciona como debe: un indicador que se actualiza mientras la barra actual se desarrolla. Eso lo quieres. Una señal que solo aparece tras el cierre es una señal que no puedes ejecutar al cierre.
Repintado que invalida un backtest: un script cuyos valores históricos no podrían haberse conocido en el momento en que se dibujan. Ese es el que hace que una curva de resultados parezca extraordinaria y no signifique nada, porque la ejecución histórica tuvo información que la ejecución en vivo nunca tendrá.
La prueba no es «¿cambia?», sino «¿podría haberse conocido este valor en esa barra, en ese momento, en tiempo real?». Si la respuesta es no, el backtest mide una estrategia que no puede existir.
TradingView es directo sobre la peor versión: los scripts que filtran datos futuros al historial son «extremadamente engañosos», y publicarlos no está permitido [2].
Las diez causas documentadas
TradingView documenta diez causas distintas [1]. No son igual de graves, así que aquí se agrupan por lo que hacen a un backtest y no por característica del lenguaje.
Fatales — el backtest mide algo imposible
| Causa | Qué ocurre |
|---|---|
Fuga de futuro vía request.security() | Usar lookahead sin desplazamiento revela precios posteriores a la barra actual, solo en barras históricas |
| Dibujar en el pasado | Dibujar retroactivamente en barras anteriores, de modo que el gráfico muestra una detección que no ocurrió entonces |
Ambas producen un registro histórico que no podría haber existido en vivo. Nada más en esta lista está en la misma categoría.
Estructurales — espera una diferencia y tenla en cuenta
| Causa | Qué ocurre |
|---|---|
| Valores de datos fluidos | Las barras históricas solo tienen OHLC; en una barra en tiempo real el máximo, el mínimo y el cierre siguen moviéndose |
Llamadas request.security() que repintan | Devuelven un valor sin confirmar de la temporalidad superior en tiempo real, que cambia al reiniciarse el script |
request.security() a temporalidad inferior | Las intrabarras no están ordenadas en tiempo real, así que el comportamiento en vivo no se reproduce desde el historial |
Estrategias con calc_on_every_tick | Se ejecutan en cada tick en tiempo real, pero solo al cierre de barra en el historial |
Estas cuatro son la asimetría normal entre una barra terminada y una que no lo está. No convierten una estrategia en falsa; convierten un backtest en una aproximación, y conviene conocer el tamaño de la diferencia antes de confiar en un resultado.
Dependientes del diseño — correctas hasta que asumes lo contrario
| Causa | Qué ocurre |
|---|---|
Declaraciones varip | Mantienen estado entre actualizaciones en tiempo real de un modo que el historial no reproduce |
| Built-ins de estado de barra | barstate.isnew y similares se comportan distinto en tiempo real |
timenow | Necesariamente difiere entre una ejecución histórica y una en vivo |
| Variaciones del conjunto de datos | Dónde empieza el historial del gráfico, y las revisiones posteriores de datos, cambian los cálculos con el tiempo |
La última sorprende a quien está seguro de que su script cambió cuando nada en él cambió. Un punto de inicio distinto del gráfico, o una revisión del feed, basta para mover un resultado [1].
La pregunta que hay que hacerse en cada caso
Para todas ellas la prueba útil es la misma: ¿habría tenido este valor una ejecución en vivo en ese momento? Las dos del grupo fatal responden que no. El resto responden que sí, con una diferencia de tiempo que puedes medir.
Lookahead y la fuga de futuro
request.security() acepta un parámetro lookahead con dos valores
posibles [2]:
| En barras históricas | En barras en tiempo real | |
|---|---|---|
barmerge.lookahead_off (por defecto) | el último valor confirmado de la temporalidad superior | el valor actual sin confirmar del símbolo |
barmerge.lookahead_on | valores de momentos posteriores a la barra en la que se ejecuta | el mismo valor sin confirmar que lookahead_off |
Lee la segunda fila dos veces. En el historial, lookahead_on entrega al script
datos del futuro. En tiempo real no puede, porque ese futuro todavía no existe,
así que devuelve exactamente lo que devolvería lookahead_off.
Esa asimetría es todo el problema. La mitad histórica de la ejecución responde con información que la mitad en vivo nunca tendrá, y ambas mitades son el mismo script. El backtest no es optimista: describe una estrategia distinta.
Las palabras de TradingView: los programadores deben extremar la precaución con lookahead, especialmente en temporalidades superiores [2].
Usarlo correctamente
Lookahead no está prohibido, y hay un patrón documentado que lo hace seguro. Dos cosas juntas [2]: desplazar la expresión solicitada con el operador de historial y activar lookahead.
float htfPrice = request.security(syminfo.tickerid, timeframe,
high[1], lookahead = barmerge.lookahead_on)
El [1] es lo que lo hace correcto. Pedir el máximo de la barra anterior de
la temporalidad superior, con lookahead activado, da un valor que realmente
estaba disponible en ese momento: la serie se comporta igual en barras
históricas y en tiempo real, y no repinta [2].
Sin el desplazamiento, esa misma llamada con lookahead_on es la fuga de
futuro.
Si no escribiste tú el script
Dos comprobaciones, en orden, antes de confiar en una estrategia ajena:
- Busca
lookahead. Si aparecebarmerge.lookahead_on, localiza la expresión solicitada y confirma que está desplazada:high[1], nohigh. - Comprueba si usa
request.security()en absoluto. Si lo usa y el backtest parece extraordinario, esto es lo primero que hay que descartar.
Publicar un script que filtra datos futuros al historial no está permitido en TradingView [2], lo que indica lo común que es el error.
Tactix AI en este flujo
AlfaTactix incluye Tactix AI: usa Tactix Studio para describir tu idea en lenguaje natural y rellenar timeframes, señales, filtros y riesgo en el mismo formulario de seis pasos — o abre Tactix Guide en cualquier paso si solo necesitas explicar un campo. Revisa cada valor y luego exporta MQL5 o Pine Script desde Code Generator (form-first — no prompt-to-code sin probar).

