Guía · Probador de estrategias
La consola de backtest: prueba estrategias de trading con código
Cada gráfico tiene un botón Estrategia. Abre una consola que reproduce una estrategia sobre velas pasadas y muestra cada operación en el gráfico, el beneficio, el drawdown, la tasa de acierto y una curva de capital frente a comprar y mantener. Empieza con una estrategia integrada y luego escribe la tuya en unas pocas líneas de JavaScript.
Seguro por diseño. Tu script solo se ejecuta en tu navegador, en un entorno aislado sin acceso a internet: puede leer las velas que recibe y nada más (ni tu cuenta, ni este sitio, ni otras webs). Nada de lo que escribes se ejecuta en nuestros servidores.
Empieza en un minuto
- Abre cualquier gráfico, por ejemplo EURUSD, y elige la temporalidad que quieres probar (H1, D1…).
- Pulsa Estrategia en la barra de herramientas del gráfico. La consola se abre debajo del gráfico.
- Elige una estrategia integrada, por ejemplo Cruce de medias móviles. Se ejecuta al instante: unas flechas en el gráfico marcan cada entrada y salida.
- Cambia un parámetro (por ejemplo la media lenta de 30 a 50): la prueba vuelve a ejecutarse sola.
- Mira Resumen para los totales, Operaciones para cada operación y Curva de capital para la cuenta a lo largo del tiempo.
- Pulsa Editar una copia para convertirla en tu propio script, cámbiala y pulsa Ejecutar prueba (o Ctrl+Enter).
La consola, parte por parte
| Parte | Qué hace |
|---|---|
| Lista de estrategias | Integradas: seis estrategias listas (cruce de medias móviles, giro del RSI, reversión a la media con Bandas de Bollinger, cruce de señal del MACD, ruptura de Donchian, SuperTrend). Mis estrategias: las que guardaste (inicia sesión). Nuevo script… empieza uno vacío. |
| Ver código / Editar una copia | Ver código muestra exactamente qué hace una estrategia integrada. Editar una copia la copia en el editor como tu propio script, lista para cambiar. |
| Parámetros | Cada input(…) del script se convierte aquí en un campo (un número o una lista). Cambiar uno vuelve a ejecutar la prueba. Predeterminados los restablece. |
| Ajustes | Capital, tamaño de orden, costes, cuántas velas y si se permiten operaciones en corto (abajo). |
| Editor | El JavaScript de la estrategia. Tab sangra, Ctrl/⌘+Enter ejecuta. Un error muestra su mensaje y el número de línea. |
| Referencia | Una lista breve de todo lo que un script puede usar, dentro de la propia consola. Esta página es la versión larga. |
| Guardar / Guardar como… | Guarda el script, su nombre, parámetros y ajustes en tu cuenta (un punto en Guardar • indica cambios sin guardar). Tus estrategias aparecen en cada gráfico. |
| Pestañas de resultados | Resumen, Operaciones (con Descargar CSV), Curva de capital y Log. Las operaciones y las líneas dibujadas también aparecen en el gráfico; haz clic en una operación para ir a ella. |
Ajustes
| Ajuste | Significado | Buen valor inicial |
|---|---|---|
| Capital inicial | La cuenta con la que empieza la prueba, en la divisa del precio (para EURUSD: dólares). | 10,000 |
| Tamaño de la orden | % del capital: cada operación usa esta parte de la cuenta en ese momento (100 % = toda la cuenta, sin apalancamiento). unidades: una cantidad fija (en forex, unidades de la divisa base: 100.000 = un lote estándar). Un script puede fijar su propio tamaño por orden con { qty }. | el 100 % del capital, o un lote fijo |
| Comisión % por lado | Una comisión en porcentaje del valor operado, cobrada a la entrada y otra vez a la salida. | 0,002–0,01 para forex, 0,05–0,1 para acciones y cripto |
| Deslizamiento / spread (precio) | Se suma en tu contra en cada ejecución, en unidades de precio. Las velas son precios bid, así que pon aquí el spread típico para que las compras sean realistas. | EURUSD 0,00010 (1 pip), oro 0,30 |
| Velas | En el gráfico: las velas cargadas en pantalla. Con sesión iniciada, elige Hasta 5.000 … 100.000 para cargar más historial de la misma temporalidad (este sitio permite hasta 20,000). | Tantas como puedas: más velas, más operaciones, cifras más fiables |
| Permitir operaciones cortas | Desactivado: short() solo cierra una posición larga y nunca abre una corta. Útil para probar solo el lado largo sin editar el script. | Activado para forex, desactivado para invertir solo en largo |
Cómo leer los resultados
| Número | Qué te dice |
|---|---|
| Beneficio neto | Capital final menos capital inicial, tras comisión y deslizamiento; también en % del capital. |
| Comprar y mantener | Lo que habría ganado simplemente comprar en la primera vela y mantener hasta la última. Una estrategia que no lo supera, o lo hace con un drawdown mucho mayor, aporta poco. |
| Drawdown máximo | La mayor caída del capital desde un máximo, en dinero y %. El mal trago que habrías tenido que aguantar. Muchos traders miran el beneficio neto ÷ drawdown máximo. |
| Operaciones, % ganadas | Cuántas operaciones se cerraron y la proporción que ganó dinero. Una tasa de acierto baja está bien si las ganadoras son mucho mayores que las perdedoras. |
| Factor de beneficio | Beneficio bruto ÷ pérdida bruta. Por encima de 1 gana dinero; 1,3–2 es típico de estrategias sólidas; valores muy altos con pocas operaciones suelen ser suerte. |
| Operación media, ganancia media, pérdida media | Beneficio medio por operación y tamaño típico de ganadoras y perdedoras. Compara la operación media con tus costes. |
| Mayor ganancia / pérdida, operaciones perdedoras seguidas | Los extremos. Una sola ganadora enorme que sostiene todo el resultado es una señal de alarma. |
| Ratio de Sharpe | Rentabilidad por unidad de volatilidad, anualizada a partir de los cambios del capital de vela a vela. Por encima de 1 es buena, por encima de 2 muy buena (y rara). |
| Tiempo en el mercado | La proporción de velas con una posición abierta. Menos tiempo para el mismo beneficio significa menos riesgo. |
| Largo / corto | Operaciones, beneficio y tasa de acierto de cada lado por separado: a menudo un lado hace todo el trabajo. |
La pestaña Operaciones lista cada operación con su dirección, hora y precio de entrada y salida, tamaño, beneficio en dinero y %, cuántas velas duró, por qué se cerró (señal, stop loss, take profit, invertida, aún abierta al final) y tu comentario. Descargar CSV la exporta a una hoja de cálculo. La Curva de capital muestra la cuenta frente a comprar y mantener. El Log muestra lo que tu script escribió con log().
Cómo se ejecutan las órdenes
- Sin mirar al futuro. Una orden colocada en
onBar(i)se ejecuta a la apertura de la vela siguiente (i + 1). Una estrategia nunca puede operar a un precio que no podía conocer. - Stops dentro de la vela. El stop loss y el take profit se comprueban con el máximo y el mínimo de cada vela. Si ambos podían alcanzarse en la misma vela, cuenta el stop (la suposición prudente). Un hueco que atraviesa un stop se ejecuta a la apertura, como en los mercados reales.
- Una posición a la vez.
long()estando en corto cierra el corto y abre un largo («invertido»);long()estando ya en largo solo actualiza los stops que pasas. - Costes en cada ejecución. El deslizamiento mueve cada precio de ejecución en tu contra; la comisión se cobra a la entrada y a la salida.
- El final. Una operación aún abierta tras la última vela se cierra al último cierre y se marca como «aún abierta al final».
Escribir tu propia estrategia
Una estrategia es un programa corto en JavaScript con dos partes:
- La parte superior se ejecuta una vez: lee tus parámetros con
input(), calcula series completas conta.*oind()y dibújalas conplot(). function onBar(i) { … }se ejecuta para cada vela, de la más antigua a la más reciente.ies el número de la vela (0 = la más antigua). Decide aquí:long(),short(),exit()o nada.
// 1. once: inputs and series
const len = input("Average length", 50, { min: 5, max: 400 });
const avg = ta.sma(close, len); // one value per candle
plot(avg, "SMA " + len, "#2F80ED"); // drawn on the price chart
// 2. every candle
function onBar(i) {
if (avg[i] == null) return; // the first len - 1 values are null (warming up)
if (position === 0 && close[i] > avg[i]) long();
if (position > 0 && close[i] < avg[i]) exit("below the average");
}
Conviene saber desde el principio:
- Las series son arrays, un valor por vela, de la más antigua a la más reciente:
close[i]es el cierre de esta vela,close[i - 1]el de la anterior. Las series calculadas sonnullhasta que tienen suficientes velas; compruébalo antes de usarlas. - Dentro de
onBar(i)solo puedes leer velas hastai.close[i + 1]detiene la prueba con un error: sería mirar al futuro. - Usa
constyletcomo siempre; tus propias variables conservan sus valores entre velas (útil para un stop dinámico). - Los precios están en las unidades del gráfico (para EURUSD 1,0850, para el oro 2650,40); el tiempo, en segundos UTC.
Referencia
Velas y estado actual
| Nombre | Qué es |
|---|---|
open, high, low, close, volume | Arrays con los valores de las velas, de la más antigua a la más reciente (el volumen es el volumen de ticks). |
time | La hora de apertura de cada vela en segundos UTC. new Date(time[i] * 1000).getUTCHours() da la hora. |
bars, symbol, timeframe | El número de velas, el símbolo (p. ej. "EURUSD") y la temporalidad (p. ej. "H1"). |
position | Dentro de onBar: 1 largo, -1 corto, 0 sin posición. |
positionSize, entryPrice, entryBar | Tamaño en unidades, precio de ejecución y número de vela de la operación abierta (null sin posición). i - entryBar = velas mantenidas. |
equity, cash | El valor de la cuenta al cierre de esta vela (con la operación abierta) y el efectivo sin ella. |
Parámetros
| Llamada | Resultado |
|---|---|
input("Period", 14, { min: 2, max: 100, step: 1 }) | Un campo numérico (entre min y max). Devuelve el valor introducido, o 14. |
input("Average", "EMA", { options: ["SMA", "EMA"] }) | Una lista. Devuelve el texto elegido. |
Hasta 30 parámetros. Sus valores se guardan con la estrategia y viajan con ella cuando la publicas en una idea.
Cálculos (ta.*)
| Función | Devuelve |
|---|---|
ta.sma(src, n), ta.ema(src, n), ta.wma(src, n), ta.rma(src, n) | Media móvil simple, exponencial, ponderada y de Wilder de cualquier serie. |
ta.rsi(src, 14) | RSI (0–100). |
ta.atr(14), ta.tr() | Average True Range (media simple, como en el gráfico) y el True Range de cada vela. |
ta.stdev(src, n) | Desviación estándar sobre n velas. |
ta.highest(src, n), ta.lowest(src, n) | Valor más alto / más bajo de las últimas n velas, incluida esta. Para una ruptura compara con [i - 1]. |
ta.change(src, n = 1), ta.roc(src, n = 1) | Diferencia con n velas atrás, y la misma en %. |
ta.macd(src, 12, 26, 9) | { macd, signal, hist }. |
ta.bb(src, 20, 2) | Bandas de Bollinger { upper, middle, lower }. |
ta.stoch(14, 3, 3) | Estocástico { k, d } (periodo, suavizado %D, ralentización). |
ta.supertrend(10, 3) | { line, dir }: dir es 1 en tendencia alcista y -1 en tendencia bajista. |
src es cualquier serie: close, high, otro resultado o tu propio array. Cada función devuelve un array nuevo; nada cambia las velas.
Señales
| Función | Verdadero cuando… |
|---|---|
ta.crossover(a, b, i) | a cruza por encima de b en la vela i (estaba igual o por debajo en i - 1). b puede ser un número: ta.crossover(rsi, 30, i). |
ta.crossunder(a, b, i) | a cruza por debajo de b. |
ta.cross(a, b, i) | Cualquiera de los dos. |
ta.rising(s, n, i), ta.falling(s, n, i) | s subió (bajó) en cada una de las últimas n velas. |
Órdenes
| Llamada | Efecto (a la apertura de la vela siguiente) |
|---|---|
long(options) (también buy) | Abre una posición larga; antes cierra una corta. |
short(options) (también sell) | Abre una posición corta; antes cierra una larga. Con las operaciones en corto desactivadas solo cierra la larga. |
exit("reason") | Cierra la posición abierta. El motivo aparece en la lista de operaciones. |
stopLoss(price), takeProfit(price) | Mueve al instante el stop / objetivo de la operación abierta (null lo elimina). Úsalos para stops dinámicos. |
| Opción | Significado |
|---|---|
{ sl: 1.0820, tp: 1.0950 } | Stop loss y take profit como precios. |
{ slPct: 2, tpPct: 4 } | Stop y objetivo en % desde el precio de ejecución. |
{ qty: 50000 } | Tamaño en unidades para esta orden (en lugar del ajuste Tamaño de orden). |
{ comment: "breakout" } | Una nota que se muestra con la operación. |
Salida
| Llamada | Efecto |
|---|---|
plot(series, "Name", "#2F80ED") | Dibuja una línea en el gráfico de precios (hasta 12). |
log("text", value, …) | Escribe una línea en la pestaña Log con la hora de la vela (hasta 200 líneas). |
Los 71 indicadores del gráfico en código: ind()
ind(id, inputs) calcula cualquier indicador del gráfico exactamente como lo hace el gráfico. Devuelve su primera línea como serie; todas las líneas están en .lines y sus nombres en .names. Los parámetros omitidos toman el valor por defecto indicado. method es uno de SMA, EMA, SMMA, LWMA; price uno de close, open, high, low, median, typical, weighted.
const st = ind("stoch", { k: 14, d: 3, slowing: 3 }); // st = %K, st.lines[1] = %D
const ich = ind("ichimoku"); // ich.names: Tenkan, Kijun, Span A, Span B, Chikou
const kijun = ich.lines[1];
| id | Indicador | Parámetros (por defecto) | Líneas |
|---|---|---|---|
ma | Moving Average | period: 20, method: SMA, price: close, shift: 0 | MA |
ama | Adaptive Moving Average | period: 9, fast: 2, slow: 30, price: close | AMA |
adx | Average Directional Movement Index | period: 14 | ADX, +DI, −DI |
adxw | ADX Wilder | period: 14 | ADX, +DI, −DI |
bb | Bollinger Bands | period: 20, dev: 2, price: close, shift: 0 | Middle, Upper, Lower |
dema | Double Exponential Moving Average | period: 14, price: close | DEMA |
env | Envelopes | period: 14, method: SMA, dev: 0.1, price: close | Upper, Lower |
frama | Fractal Adaptive Moving Average | period: 14, price: close | FrAMA |
ichimoku | Ichimoku Kinko Hyo | tenkan: 9, kijun: 26, senkou: 52 | Tenkan, Kijun, Span A, Span B, Chikou |
sar | Parabolic SAR | step: 0.02, max: 0.2 | SAR |
stddev | Standard Deviation | period: 20, method: SMA, price: close | StdDev |
tema | Triple Exponential Moving Average | period: 14, price: close | TEMA |
vidya | Variable Index Dynamic Average | cmo: 9, ema: 12, price: close | VIDYA |
atr | Average True Range | period: 14 | ATR |
bears | Bears Power | period: 13 | Bears |
bulls | Bulls Power | period: 13 | Bulls |
cci | Commodity Channel Index | period: 14, price: typical | CCI |
chaikin | Chaikin Oscillator | fast: 3, slow: 10, method: EMA | CHO |
demarker | DeMarker | period: 14 | DeMarker |
force | Force Index | period: 13, method: SMA | Force |
macd | MACD | fast: 12, slow: 26, signal: 9, price: close | MACD, Signal |
momentum | Momentum | period: 14, price: close | Momentum |
osma | Moving Average of Oscillator | fast: 12, slow: 26, signal: 9, price: close | OsMA |
rsi | Relative Strength Index | period: 14, price: close | RSI |
rvi | Relative Vigor Index | period: 10 | RVI, Signal |
stoch | Stochastic Oscillator | k: 5, d: 3, slowing: 3, method: SMA | %K, %D |
trix | Triple Exponential Average | period: 14, price: close | TRIX |
wpr | Williams' Percent Range | period: 14 | %R |
ad | Accumulation/Distribution | ninguno | A/D |
mfi | Money Flow Index | period: 14 | MFI |
obv | On Balance Volume | ninguno | OBV |
volumes | Volumes | ninguno | Volume |
ac | Accelerator Oscillator | ninguno | AC |
alligator | Alligator | jaw: 13, jawShift: 8, teeth: 8, teethShift: 5, lips: 5, lipsShift: 3 | Jaw, Teeth, Lips |
ao | Awesome Oscillator | ninguno | AO |
fractals | Fractals | ninguno | Up, Down |
gator | Gator Oscillator | jaw: 13, teeth: 8, lips: 5 | Upper, Lower |
bwmfi | Market Facilitation Index | ninguno | BW MFI |
zigzag | ZigZag | depth: 12, dev: 0.3 | ZigZag |
vwap | VWAP (volume-weighted average price) | ninguno | VWAP |
supertrend | SuperTrend | period: 10, mult: 3 | Up trend, Down trend |
keltner | Keltner Channels | period: 20, atr: 10, mult: 2 | Middle, Upper, Lower |
donchian | Donchian Channels | period: 20 | Upper, Lower, Middle |
hma | Hull Moving Average | period: 20, price: close | HMA |
pivots | Pivot Points (classic) | ninguno | P, R1, S1, R2, S2, R3, S3 |
stochrsi | Stochastic RSI | rsi: 14, stoch: 14, k: 3, d: 3 | %K, %D |
aroon | Aroon | period: 25 | Aroon up, Aroon down |
cmf | Chaikin Money Flow | period: 20 | CMF |
uo | Ultimate Oscillator | p1: 7, p2: 14, p3: 28 | UO |
chop | Choppiness Index | period: 14 | CHOP |
vwma | Volume-Weighted Moving Average | period: 20 | VWMA |
zlema | Zero-Lag Exponential Moving Average | period: 21, price: close | ZLEMA |
alma | Arnaud Legoux Moving Average | period: 9, offset: 0.85, sigma: 6, price: close | ALMA |
mcginley | McGinley Dynamic | period: 14 | McGinley |
lsma | Least Squares Moving Average | period: 25, price: close | LSMA |
chandelier | Chandelier Exit | period: 22, mult: 3 | Long exit, Short exit |
roc | Rate of Change | period: 12 | ROC |
cmo | Chande Momentum Oscillator | period: 9 | CMO |
tsi | True Strength Index | long: 25, short: 13, signal: 13 | TSI, Signal |
ppo | Percentage Price Oscillator | fast: 12, slow: 26, signal: 9 | Histogram, PPO, Signal |
dpo | Detrended Price Oscillator | period: 20 | DPO |
kst | Know Sure Thing | signal: 9 | KST, Signal |
vortex | Vortex Indicator | period: 14 | VI+, VI− |
eom | Ease of Movement | period: 14 | EOM |
mass | Mass Index | ema: 9, sum: 25 | Mass Index |
coppock | Coppock Curve | wma: 10, long: 14, short: 11 | Coppock |
hv | Historical Volatility | period: 20 | HV % |
fisher | Fisher Transform | period: 9 | Fisher, Trigger |
squeeze | Squeeze Momentum | bb: 20, bbk: 2, kc: 20, kck: 1.5 | Momentum, Squeeze on, Squeeze off |
crsi | Connors RSI | rsi: 3, streak: 2, rank: 100 | CRSI |
pvt | Price Volume Trend | ninguno | PVT |
Ejemplos
Cada ejemplo funciona tal cual: pulsa Copiar, abre la consola, elige Nuevo script…, pega y pulsa Ejecutar prueba. Los resultados dependen del símbolo, la temporalidad y el periodo; los ejemplos enseñan la técnica, no son recomendaciones.
1. Your first strategy: buy above a moving average
The smallest useful script. It is long while the close is above its 50-candle average and flat otherwise. The top of the script runs once; onBar(i) runs for every candle.
const len = input("Average length", 50, { min: 5, max: 400 });
const avg = ta.sma(close, len);
plot(avg, "SMA " + len, "#2F80ED");
function onBar(i) {
if (avg[i] == null) return; // still warming up
if (position === 0 && close[i] > avg[i]) long({ comment: "close above the average" });
if (position > 0 && close[i] < avg[i]) exit("close below the average");
}
2. Moving average crossover, long and short
Always in the market: long() while short reverses the position (and short() while long). Untick Allow short trades in the settings to make it long-only without changing the code.
const fast = ta.ema(close, input("Fast EMA", 12, { min: 2, max: 200 }));
const slow = ta.ema(close, input("Slow EMA", 26, { min: 3, max: 400 }));
plot(fast, "Fast", "#2F80ED");
plot(slow, "Slow", "#E39B2D");
function onBar(i) {
if (ta.crossover(fast, slow, i)) long();
if (ta.crossunder(fast, slow, i)) short();
}
3. Fixed stop loss and take profit in percent
slPct and tpPct are measured from the fill price. The stop and the target are checked inside every candle with its high and low; if both could have been hit in one candle, the stop counts.
const r = ta.rsi(close, 14);
const sl = input("Stop loss %", 1.5, { min: 0.1, max: 20, step: 0.1 });
const tp = input("Take profit %", 3, { min: 0.1, max: 50, step: 0.1 });
function onBar(i) {
if (position !== 0) return; // one trade at a time
if (ta.crossover(r, 30, i)) long({ slPct: sl, tpPct: tp, comment: "RSI back above 30" });
if (ta.crossunder(r, 70, i)) short({ slPct: sl, tpPct: tp, comment: "RSI back below 70" });
}
4. ATR stop and a 2:1 target in prices
sl and tp take prices. Here the stop sits 2 ATR from the close and the target twice as far (risk/reward 1:2). The fill is at the next open, so the real distance can differ slightly.
const a = ta.atr(14);
const hi = ta.highest(high, 20);
const mult = input("Stop (ATR multiple)", 2, { min: 0.5, max: 6, step: 0.5 });
function onBar(i) {
if (i < 1 || a[i] == null || hi[i - 1] == null || position !== 0) return;
if (close[i] > hi[i - 1]) { // a new 20-candle high
const stop = close[i] - mult * a[i];
const target = close[i] + 2 * mult * a[i];
long({ sl: stop, tp: target, comment: "breakout, risk " + (mult * a[i]).toFixed(5) });
}
}
5. A trailing stop that follows the price
stopLoss(price) moves the stop of the open trade. This one trails 3 ATR under the highest close since the entry and never moves down.
const a = ta.atr(14);
const fast = ta.ema(close, 20), slow = ta.ema(close, 50);
let stop = null;
function onBar(i) {
if (a[i] == null || slow[i] == null) return;
if (position === 0) {
stop = null;
if (ta.crossover(fast, slow, i)) long({ comment: "trend up" });
return;
}
const candidate = close[i] - 3 * a[i];
if (stop === null || candidate > stop) { // only ever raise it
stop = candidate;
stopLoss(stop);
}
}
6. Any chart indicator with ind(): ADX trend filter
ind(id, inputs) gives the first line of any of the chart's 71 indicators; the other lines are in .lines (their names in .names). Here: trade the MACD cross only when ADX says the market is trending.
const adx = ind("adx", { period: 14 }); // .lines[0] ADX, [1] +DI, [2] -DI
const plusDI = adx.lines[1], minusDI = adx.lines[2];
const m = ta.macd(close, 12, 26, 9);
const minAdx = input("Minimum ADX", 25, { min: 10, max: 50 });
function onBar(i) {
if (adx[i] == null || adx[i] < minAdx) return;
if (ta.crossover(m.macd, m.signal, i) && plusDI[i] > minusDI[i]) long();
if (ta.crossunder(m.macd, m.signal, i) && minusDI[i] > plusDI[i]) short();
}
7. Mean reversion with Bollinger Bands
Buys a close under the lower band, sells a close over the upper band, and exits at the middle band. A classic for ranging markets; it suffers in strong trends.
const b = ta.bb(close, input("Period", 20, { min: 5, max: 200 }), input("Width", 2, { min: 0.5, max: 4, step: 0.1 }));
plot(b.upper, "Upper", "#8E44AD");
plot(b.lower, "Lower", "#8E44AD");
function onBar(i) {
if (b.middle[i] == null) return;
if (position === 0 && close[i] < b.lower[i]) long({ comment: "below the lower band" });
else if (position === 0 && close[i] > b.upper[i]) short({ comment: "above the upper band" });
else if (position > 0 && close[i] >= b.middle[i]) exit("middle band");
else if (position < 0 && close[i] <= b.middle[i]) exit("middle band");
}
8. Trade only at certain hours (time filter)
time[i] is the candle's open in UTC seconds. This example only opens trades between 07:00 and 16:00 UTC (the London session) and closes anything still open at 20:00. Use it on an intraday timeframe.
const from = input("Start hour (UTC)", 7, { min: 0, max: 23 });
const to = input("End hour (UTC)", 16, { min: 1, max: 24 });
const fast = ta.sma(close, 10), slow = ta.sma(close, 30);
function onBar(i) {
const hour = new Date(time[i] * 1000).getUTCHours();
if (position !== 0 && hour >= 20) { exit("end of the day"); return; }
if (hour < from || hour >= to) return;
if (ta.crossover(fast, slow, i)) long();
if (ta.crossunder(fast, slow, i)) short();
}
9. Your own calculation: a volatility breakout
Series are plain arrays, so you can compute your own. This builds yesterday's range for every candle and buys a break above the open plus half of it. log() writes to the Log tab.
const k = input("Range multiple", 0.5, { min: 0.1, max: 2, step: 0.1 });
const range = high.map((h, i) => (i ? high[i - 1] - low[i - 1] : null)); // the previous candle's range
const trigger = open.map((o, i) => (range[i] == null ? null : o + k * range[i]));
plot(trigger, "Trigger", "#16A085");
function onBar(i) {
if (trigger[i] == null) return;
if (position === 0 && close[i] > trigger[i]) {
long({ slPct: 1, comment: "close above open + " + k + " x range" });
log("signal at", close[i], "trigger", trigger[i]);
} else if (position > 0 && i - entryBar >= 3) {
exit("held 3 candles");
}
}
10. Position size from risk (1% of equity per trade)
{ qty } sets the size in units for one order, overriding the Order size setting. Here the size is chosen so that hitting the stop loses about 1% of the current equity.
const a = ta.atr(14);
const riskPct = input("Risk per trade %", 1, { min: 0.1, max: 5, step: 0.1 });
const fast = ta.ema(close, 20), slow = ta.ema(close, 50);
function onBar(i) {
if (a[i] == null || slow[i] == null || position !== 0) return;
if (ta.crossover(fast, slow, i)) {
const stopDistance = 2 * a[i];
const qty = (equity * riskPct / 100) / stopDistance;
long({ qty: qty, sl: close[i] - stopDistance, tp: close[i] + 3 * stopDistance,
comment: "risk " + riskPct + "%" });
}
}
11. Two conditions in a row: pullback in an uptrend
Combines a trend filter (price above the 200 average), a pullback (RSI under 40 within the last 5 candles) and a trigger (close back above the 20 average).
const trend = ta.sma(close, 200), fast = ta.ema(close, 20), r = ta.rsi(close, 14);
function onBar(i) {
if (trend[i] == null || i < 5) return;
let dipped = false;
for (let k = 0; k < 5; k++) if (r[i - k] != null && r[i - k] < 40) dipped = true;
if (position === 0 && close[i] > trend[i] && dipped && ta.crossover(close, fast, i)) {
long({ slPct: 2, tpPct: 4, comment: "pullback bought" });
}
}
Errores y cómo corregirlos
| Mensaje | Causa y solución |
|---|---|
| the script needs a function onBar(i) { … } | El script no tiene onBar, o está mal escrito (onbar). Escribe function onBar(i) { … } en el nivel superior del script, no dentro de otra función. |
| close[124] is in the future: inside onBar(123) only candles up to 123 are known | Has leído una vela posterior. Usa close[i] y anteriores (close[i - 1]). Para una ruptura, compara el cierre de hoy con el máximo más alto de ayer: close[i] > hi[i - 1]. |
| close cannot be changed | Las velas son de solo lectura. Crea tu propio array: const mid = close.map((c, i) => (c + open[i]) / 2); |
| Cannot read properties of null / NaN en los resultados | Una serie aún es null al principio (calentamiento). Empieza onBar con if (avg[i] == null) return; y comprueba i >= 1 antes de usar [i - 1]. |
| unknown indicator "…" | Usa un id de la tabla de indicadores (en minúsculas), p. ej. ind("adx"), no ind("ADX"). |
| Sin operaciones: la estrategia nunca colocó una orden | Las condiciones nunca se cumplieron a la vez. Añade log("rsi", r[i]) para ver los valores, relaja una condición o carga más velas. |
| La estrategia tardó más de 15 segundos (¿un bucle infinito?) | Un bucle que nunca termina, o un bucle pesado dentro de onBar. Calcula las series una vez arriba en lugar de recorrer todas las velas en cada vela. |
| Unexpected token / SyntaxError (línea N) | Un error de escritura en esa línea: falta un paréntesis, una comilla o una coma. |
Pruebas en las que puedes confiar
- Añade tus costes. Una estrategia que opera a menudo puede parecer genial sin costes y perder dinero con un spread realista. Rellena siempre el deslizamiento (el spread) y la comisión.
- Usa suficientes operaciones. Con menos de unas 30 operaciones, las cifras son sobre todo azar. Carga más velas o usa una temporalidad menor.
- No te ajustes al pasado. Probar muchos parámetros hasta que el resultado sea perfecto encuentra el ruido de ese periodo, no una regla que funcione. Ajusta con una parte del historial y luego comprueba los mismos parámetros en otro periodo y en otros símbolos.
- Compara con comprar y mantener y mira el drawdown, no solo el beneficio.
- Vigila los extremos. Si una o dos operaciones generan todo el beneficio, la estrategia depende de la suerte.
- Practica antes de arriesgar dinero. Ejecuta la estrategia hacia delante en trading simulado o con repetición de barras, y crea alertas para sus señales.
Los backtests usan velas bid pasadas e ignoran swaps, financiación y spreads que cambian con el mercado. Los resultados pasados no garantizan resultados futuros. Esto no es asesoramiento de inversión.
Guardar, compartir, publicar
- Guardar (con sesión iniciada) conserva hasta 50 estrategias con sus parámetros y ajustes; aparecen en la lista de cada gráfico.
- Publica una idea desde el gráfico y adjunta una estrategia: los lectores pueden abrir tu gráfico y ejecutar la estrategia ellos mismos, con tus parámetros. Ver Ideas.
- Comparte tu análisis con un equipo en un club.
- Descargar CSV en la pestaña Operaciones exporta las operaciones a una hoja de cálculo.
- Para investigación más pesada, descarga las mismas velas con la API de precios (
GET /bars) en Python, R o Excel.
Límites
| Velas por prueba | Visitantes: las velas del gráfico. Con sesión iniciada: hasta 20,000 (los planes Premium pueden aumentarlo; ver Premium). |
| Script | 50.000 caracteres, 30 parámetros, 12 líneas dibujadas, 200 líneas de registro, 5.000 operaciones listadas, 15 segundos de ejecución. |
| Estrategias guardadas | 50 por cuenta. |
| Posiciones | Una posición a la vez por prueba (largo, corto o sin posición); un símbolo y una temporalidad por prueba. |
Preguntas
¿Necesito saber programar?
No. Las estrategias integradas funcionan sin código; solo cambias sus parámetros. Si quieres más, Editar una copia te da un script que funciona para cambiarlo línea a línea, y los ejemplos de arriba cubren las piezas habituales.
¿Es como Pine Script?
La idea es la misma (series, parámetros, trazados, órdenes) y el lenguaje es JavaScript normal. La mayoría de las estrategias Pine sencillas se traducen línea a línea: ta.sma(close, 20) sigue siendo ta.sma(close, 20), strategy.entry("L", strategy.long) pasa a ser long(), y el cuerpo del script va dentro de onBar(i) con [i] tras cada serie.
¿Puede la estrategia operar por mí?
No. La consola solo prueba con velas pasadas. Para seguir una estrategia en vivo, usa el trading simulado y las alertas, o llama a la API de precios desde tu propio programa.
¿Por qué mi resultado es distinto del de otra plataforma?
Velas distintas (precios bid frente a medios, horarios de sesión del bróker), ejecuciones en la siguiente apertura en lugar del cierre, los costes y la regla de stop antes que objetivo cambian los resultados. Compara lo comparable.
¿Mi código es privado?
Sí. Se ejecuta en tu navegador; solo se guarda en tu cuenta cuando pulsas Guardar, y otras personas solo lo ven si lo publicas en una idea.