Cambios de v6 que detienen el script
El compilador encuentra todos estos, así que cuestan tiempo y no dinero. Cada fila viene de la guía oficial de migración [1].
| Código v5 | Por qué v6 lo rechaza | Solución |
|---|---|---|
if algúnNúmero | los números ya no se convierten a bool implícitamente | if bool(algúnNúmero) o if algúnNúmero != 0 |
na(algúnBool), nz(algúnBool) | un bool ya no puede ser na | elimínalo — un bool es true o false |
plot(close, style = na) | los parámetros de tipo único rechazan na | usa un estilo real |
un switch de tipo único sin rama por defecto | la rama ausente sería na | añade una rama => |
strategy.entry("id", strategy.long, when = cond) | when se eliminó | envuélvelo en if cond |
6[1], true[10], color.red[3] | sin historial en literales ni constantes | quita el [] |
miObjeto.campo[10] | sin historial directo en un campo de UDT | (miObjeto[10]).campo — con paréntesis |
plot(close, color = a, color = b) | un parámetro no puede repetirse | deja uno |
plot(close, offset = valorSeries) | offset debe ser simple, no series | usa una constante o un input |
plot(close, linewidth = 0) | el mínimo es 1 | usa 1 o más |
plot(close, transp = 80) | transp se eliminó | color = color.new(miColor, 80) |
ta.ema(close, longMutable) | una variable mutada ahora es series, y ta.ema quiere simple | usa una longitud fija o de input |
Dos merecen una nota.
when era la forma estándar de colocar una orden condicional en v5, así que
aparece en una gran parte de las estrategias v5 publicadas. Todas necesitan la
reescritura con if. Es mecánico, pero no es opcional.
La regla de las variables mutables es la que la gente discute. Una variable
reasignada durante la ejecución cuenta como series, y funciones como
ta.ema() solo aceptan simple en su longitud [1]. En v5 esto pasaba
desapercibido. Nunca fue correcto — una longitud de indicador que cambia barra a
barra no tiene un historial consistente — así que v6 rechaza algo que estaba
mal en silencio, no elimina una función.
Las constantes de color cambiaron de valor
No es un error, pero va aquí porque sorprende a quien compara capturas [1]:
| Constante | v5 | v6 |
|---|---|---|
color.red | #FF5252 | #F23645 |
color.teal | #00897B | #089981 |
color.yellow | #FFEB3B | #FDD835 |
label.new() también cambió su color de texto por defecto de color.black a
color.white.
Cambios de v6 que no dan error
Estos son los cambios de migración que permiten que el script compile y se ejecute, y alteran lo que devuelve. Son la razón por la que una estrategia convertida puede dar un backtest distinto pareciendo idéntica, y todos están documentados [1] — solo que no agrupados así.
| Qué cambió | v5 | v6 | Consecuencia |
|---|---|---|---|
Evaluación de and / or | ambos lados siempre | cortocircuito | una llamada a un indicador tras una condición falsa se omite, corrompiendo su historial |
margin_long / margin_short por defecto | 0 — sin control de margen | 100 — control estricto | llamadas de margen donde no había |
strategy.exit() con niveles relativos y absolutos | ganaba el absoluto | el que se active antes | profit = 0, limit = price ahora sale en la entrada |
| Órdenes más allá de 9.000 | error de ejecución, se detenía | se recortan las más antiguas en silencio | las recortadas devuelven na en strategy.closedtrades.* |
timeframe.period en gráfico diario | "D" | "1D" | timeframe.period == "D" ahora siempre es falso |
5 / 2 con dos const int | 2 | 2.5 | umbrales y periodos se desplazan |
| Un bool referenciado antes de existir | na | false | la lógica de la primera barra toma la otra rama |
for i = 0 to expr | expr una sola vez | reevaluado en cada iteración | un bucle con límite mutable puede no terminar |
Un int o float usado como condición | se convertía a bool implícitamente | ya no se convierte | una comprobación de veracidad que compilaba en v5 ahora no compila |
array.get(-1) | error de ejecución | devuelve el último elemento | un desajuste de índice que antes gritaba ahora devuelve un valor erróneo verosímil |
color.red | #FF5252 | #F23645 | los plots cambian de tono; color.teal y color.yellow también, y el texto por defecto de las etiquetas pasó de negro a blanco |
El de la evaluación perezosa es el peor
Todos los demás cambian un número. Este corrompe el estado interno de un indicador, y el daño es invisible en el código.
En v5 ambos lados de and se evaluaban siempre, así que un indicador dentro de
una condición se llamaba en cada barra y mantenía un historial continuo. En v6
la evaluación hace cortocircuito [1], así que la llamada se omite en las barras
donde la primera condición es falsa — y un indicador que no se ejecutó en cada
barra ya no tiene el historial que necesita.
El propio ejemplo de TradingView [1]:
// v6: ta.rsi() se omite cuando close <= open,
// así que su historial interno deja de ser continuo
if close > open and ta.rsi(close, 14) > 50
signal := true
La solución es sacar la llamada al ámbito global, donde se ejecuta siempre:
rsiVal = ta.rsi(close, 14)
if close > open and rsiVal > 50
signal := true
Merece la pena revisarlo en cada script convertido que llame a una función
ta.* dentro de un if, un switch o el lado derecho de un and. Nada te
avisa, y el backtest sigue produciendo una curva.
El margen por defecto afecta a las estrategias apalancadas
margin_long y margin_short valían 0 por defecto en v5, es decir, sin
control de margen y permitiendo sobreapalancamiento. En v6 valen 100, lo que
aplica el margen y dispara llamadas de margen [1].
Una estrategia v5 que estaba sobreapalancada en silencio ahora será cerrada por el motor. Para reproducir el comportamiento antiguo a propósito, ponlos explícitamente:
strategy("Mi estrategia", margin_long = 0, margin_short = 0)
Es una decisión sobre realismo, no un arreglo: el valor por defecto de v6 es el más honesto de los dos, y una estrategia que solo funcionaba sin control de margen nunca iba a sobrevivir en real.
Usar el conversor, y qué se le escapa
El propio conversor del Pine Editor gestiona automáticamente la mayoría de los cambios mecánicos [1]. Úsalo primero: migrar a mano la tabla anterior es trabajo perdido.
Sus límites documentados [1]:
- Un script que no compila limpiamente como v5 no puede convertirse automáticamente. Arréglalo como v5 primero y después convierte.
- En casos raros sigue habiendo errores de compilación v6 que requieren arreglo manual.
- El comportamiento de las peticiones dinámicas puede diferir, y recuperar
la semántica de v5 puede exigir poner
dynamic_requests = falseexplícitamente.
Este último merece ampliarse, porque es un cambio de valor por defecto y no de
sintaxis. En v5 las llamadas request.*() no eran dinámicas por defecto y hacía
falta dynamic_requests = true para llamarlas dentro de bucles o
condicionales. En v6 son dinámicas por defecto y el compilador lo desactiva
cuando no hace falta [1]. Los scripts que antes necesitaban la marca ahora
funcionan sin ella — pero las peticiones anidadas pueden comportarse distinto, y
llamar a request.*() en un ámbito local con dynamic_requests = false ahora
es un error de compilación.
Lo que el conversor no puede hacer
Nada en el conversor aborda los cambios silenciosos anteriores. Reescribe
sintaxis; no sabe que tu ta.rsi() dentro de un and ya no se ejecuta en cada
barra, ni que tu estrategia ahora aplica margen. Después de convertir, la
lista silenciosa sigue siendo tu trabajo.
Cuándo exportar evita la pregunta
Si la estrategia se construyó en AlfaTactix en lugar de escribirse a mano, no hay nada que migrar: la estrategia se guarda como datos estructurados y el generador emite v6 actual. Volver a exportar produce un archivo ya correcto en v6, incluidos los valores por defecto anteriores.
Eso solo sirve para las estrategias que construyas aquí. Un script v5 que escribiste o heredaste hay que migrarlo, y para eso es el resto de esta página.
Qué emite el generador
Este es un mapa orientado a la exportación, no un manual del lenguaje: para la superficie completa, la referencia oficial de v6 es la autoridad [2].
El envoltorio. Toda exportación es una declaración strategy(), nunca
indicator(). Eso es lo que permite que el Strategy Tester informe de
operaciones [3]; un script declarado como indicador dibuja y nada más.
Llamadas a indicadores. La exportación Basic usa el espacio de nombres
ta.* — ta.rsi, ta.sma, ta.ema, ta.macd, ta.stoch, ta.atr,
ta.bb, ta.cci, ta.wpr, entre otros. Las longitudes se emiten como valores
fijos o de input, nunca como variables mutadas, que es además lo que v6 exige
para un parámetro de longitud simple.
Entradas y salidas. Las órdenes se colocan con strategy.entry() dentro de
un if, y se cierran con strategy.exit() o strategy.close(). La forma con
if no es una preferencia de estilo: when ya no existe en v6.
Tamaño de posición y costes. strategy() lleva el capital, la comisión y el
slippage que fijaste en el builder, así que el Strategy Tester incluye costes en
lugar de operar sin fricción. Un backtest sin costes es la forma más común de
que una estrategia parezca rentable sin serlo.
Qué no emite la exportación Basic
La lógica multi-temporalidad, los objetivos basados en VWAP y los filtros de noticias quedan fuera de Basic. La exportación se niega en lugar de emitir algo que se comporte distinto de lo que diseñaste — ver los errores del generador en Errores comunes de Pine Script.
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).

