Guide · Testeur de stratégie

La console de back-test : tester des stratégies de trading en code

Chaque graphique a un bouton Stratégie. Il ouvre une console qui rejoue une stratégie sur les bougies passées et montre chaque trade sur le graphique, le profit, le drawdown, le taux de réussite et une courbe de capital comparée à l’achat-conservation. Commencez par une stratégie intégrée, puis écrivez la vôtre en quelques lignes de JavaScript.

Ouvrir la console sur EURUSD Aller aux exemples

Sûr par conception. Votre script s’exécute uniquement dans votre navigateur, dans un bac à sable sans accès à internet : il peut lire les bougies qu’on lui donne et rien d’autre (ni votre compte, ni ce site, ni d’autres sites). Rien de ce que vous écrivez ne tourne sur nos serveurs.

Démarrer en une minute

  1. Ouvrez n’importe quel graphique, par exemple EURUSD, et choisissez l’unité de temps à tester (H1, D1…).
  2. Appuyez sur Stratégie dans la barre d’outils du graphique. La console s’ouvre sous le graphique.
  3. Choisissez une stratégie intégrée, par exemple Croisement de moyennes mobiles. Elle s’exécute aussitôt : des flèches sur le graphique marquent chaque entrée et sortie.
  4. Modifiez un paramètre (par exemple la moyenne lente de 30 à 50) : le test se relance tout seul.
  5. Lisez Vue d’ensemble pour les totaux, Trades pour chaque trade, Courbe de capital pour le compte dans le temps.
  6. Appuyez sur Modifier une copie pour en faire votre propre script, modifiez-le et appuyez sur Lancer le test (ou Ctrl+Enter).

La console, élément par élément

ÉlémentCe que ça fait
Liste des stratégiesIntégrées : six stratégies prêtes (croisement de moyennes mobiles, retournement du RSI, retour à la moyenne sur bandes de Bollinger, croisement du signal MACD, cassure de Donchian, SuperTrend). Mes stratégies : celles que vous avez enregistrées (connectez-vous). Nouveau script… en commence un vide.
Voir le code / Modifier une copieVoir le code montre exactement ce que fait une stratégie intégrée. Modifier une copie la copie dans l’éditeur comme votre propre script, prête à être modifiée.
ParamètresChaque input(…) du script devient ici un champ (un nombre ou une liste). En modifier un relance le test. Par défaut les rétablit.
ParamètresCapital, taille d’ordre, coûts, nombre de bougies et autorisation des ventes à découvert (ci-dessous).
ÉditeurLe JavaScript de la stratégie. Tab indente, Ctrl/⌘+Enter lance. Une erreur affiche son message et le numéro de ligne.
RéférenceUne courte liste de tout ce qu’un script peut utiliser, directement dans la console. Cette page en est la version longue.
Enregistrer / Enregistrer sous…Conserve le script, son nom, ses paramètres et réglages dans votre compte (un point sur Enregistrer • signale des modifications non enregistrées). Vos stratégies sont listées sur chaque graphique.
Onglets de résultatsVue d’ensemble, Trades (avec Télécharger le CSV), Courbe de capital et Log. Les trades et les lignes tracées apparaissent aussi sur le graphique ; cliquez sur un trade pour y accéder.

Paramètres

RéglageSignificationBonne valeur de départ
Capital de départLe compte de départ du test, dans la devise du prix (pour EURUSD : des dollars).10,000
Taille de l’ordre% du capital : chaque trade utilise cette part du compte à ce moment-là (100 % = tout le compte, sans effet de levier). unités : une quantité fixe (pour le forex, unités de la devise de base : 100 000 = un lot standard). Un script peut fixer sa propre taille par ordre avec { qty }.100 % du capital, ou un lot fixe
Commission % par côtéDes frais en pourcentage de la valeur échangée, prélevés à l’entrée puis à nouveau à la sortie.0,002–0,01 pour le forex, 0,05–0,1 pour les actions et la crypto
Glissement / spread (prix)Ajouté en votre défaveur à chaque exécution, en unités de prix. Les bougies sont des prix bid : indiquez ici le spread typique pour que les achats soient réalistes.EURUSD 0,00010 (1 pip), or 0,30
BougiesSur le graphique : les bougies chargées à l’écran. Connecté, choisissez Jusqu’à 5 000 … 100 000 pour charger plus d’historique de la même unité de temps (ce site autorise jusqu’à 20,000).Autant que possible : plus de bougies, plus de trades, des chiffres plus fiables
Autoriser les ventesDésactivé : short() ne fait que clôturer un achat, sans jamais ouvrir de vente. Pratique pour tester seulement le côté acheteur sans modifier le script.Activé pour le forex, désactivé pour l’investissement à l’achat seul

Lire les résultats

NombreCe que ça vous apprend
Bénéfice netCapital final moins capital de départ, après commission et slippage ; aussi en % du capital.
Acheter et conserverCe qu’aurait rapporté le simple fait d’acheter à la première bougie et de garder jusqu’à la dernière. Une stratégie qui ne fait pas mieux, ou seulement avec un drawdown bien plus profond, apporte peu.
Drawdown maximalLa plus forte baisse du capital depuis un sommet, en argent et en %. La douleur que vous auriez dû supporter. Beaucoup de traders regardent le profit net ÷ drawdown maximal.
Trades, % gagnantsLe nombre de trades clôturés et la part gagnante. Un faible taux de réussite convient si les gains sont bien plus gros que les pertes.
Facteur de profitProfit brut ÷ perte brute. Au-dessus de 1, on gagne de l’argent ; 1,3–2 est typique d’une stratégie solide ; des valeurs très élevées sur peu de trades relèvent généralement de la chance.
Trade moyen, gain moyen, perte moyenneProfit moyen par trade et taille typique des gains et des pertes. Comparez le trade moyen à vos coûts.
Plus gros gain / perte, trades perdants d’affiléeLes extrêmes. Un seul énorme gain qui porte tout le résultat est un signal d’alerte.
Ratio de SharpeRendement par unité de volatilité, annualisé à partir des variations du capital d’une bougie à l’autre. Au-dessus de 1, c’est bien ; au-dessus de 2, très bien (et rare).
Temps sur le marchéLa part des bougies avec une position ouverte. Moins de temps pour le même profit signifie moins de risque.
Achat / venteTrades, profit et taux de réussite de chaque côté séparément : souvent, un seul côté fait tout le travail.

L’onglet Trades liste chaque trade avec son sens, l’heure et le prix d’entrée et de sortie, la taille, le profit en argent et en %, le nombre de bougies qu’il a duré, la raison de sa clôture (signal, stop loss, take profit, retourné, encore ouvert à la fin) et votre commentaire. Télécharger le CSV l’exporte vers un tableur. La Courbe de capital montre le compte face à l’achat-conservation. Le Log montre ce que votre script a écrit avec log().

Comment les ordres sont exécutés

  • Pas de regard vers l’avant. Un ordre passé dans onBar(i) est exécuté à l’ouverture de la bougie suivante (i + 1). Une stratégie ne peut jamais trader à un prix qu’elle ne pouvait pas connaître.
  • Stops dans la bougie. Stop loss et take profit sont vérifiés par rapport au plus haut et au plus bas de chaque bougie. Si les deux pouvaient être atteints dans la même bougie, c’est le stop qui compte (l’hypothèse prudente). Un gap à travers un stop est exécuté à l’ouverture, comme sur les marchés réels.
  • Une position à la fois. long() pendant une vente clôture la vente et ouvre un achat (« retourné ») ; long() alors qu’on est déjà acheteur ne met à jour que les stops passés.
  • Des coûts à chaque exécution. Le slippage déplace chaque prix d’exécution en votre défaveur ; la commission est prélevée à l’entrée et à la sortie.
  • La fin. Un trade encore ouvert après la dernière bougie est clôturé à la dernière clôture et marqué « encore ouvert à la fin ».
onBar(i): long() filled at this open candle i + 1

Écrire votre propre stratégie

Une stratégie est un court programme JavaScript en deux parties :

  1. Le début s’exécute une fois : lisez vos paramètres avec input(), calculez des séries entières avec ta.* ou ind(), et tracez-les avec plot().
  2. function onBar(i) { … } s’exécute pour chaque bougie, de la plus ancienne à la plus récente. i est le numéro de la bougie (0 = la plus ancienne). C’est ici que vous décidez : long(), short(), exit() ou rien.
// 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");
}

Bon à savoir dès le départ :

  • Les séries sont des tableaux, une valeur par bougie, de la plus ancienne à la plus récente : close[i] est la clôture de cette bougie, close[i - 1] celle d’avant. Les séries calculées valent null tant qu’elles n’ont pas assez de bougies ; vérifiez avant de les utiliser.
  • Dans onBar(i), vous ne pouvez lire les bougies que jusqu’à i. close[i + 1] arrête le test avec une erreur : ce serait regarder dans le futur.
  • Utilisez const et let comme d’habitude ; vos propres variables gardent leur valeur d’une bougie à l’autre (pratique pour un stop suiveur).
  • Les prix sont dans les unités du graphique (pour EURUSD 1,0850, pour l’or 2650,40) ; le temps est en secondes UTC.

Référence

Bougies et état actuel

NomCe que c’est
open, high, low, close, volumeTableaux des valeurs des bougies, de la plus ancienne à la plus récente (le volume est le volume de ticks).
timeL’heure d’ouverture de chaque bougie en secondes UTC. new Date(time[i] * 1000).getUTCHours() donne l’heure.
bars, symbol, timeframeLe nombre de bougies, le symbole (par ex. "EURUSD") et l’unité de temps (par ex. "H1").
positionDans onBar : 1 acheteur, -1 vendeur, 0 sans position.
positionSize, entryPrice, entryBarTaille en unités, prix d’exécution et numéro de bougie du trade ouvert (null sans position). i - entryBar = bougies détenues.
equity, cashLa valeur du compte à la clôture de cette bougie (avec le trade ouvert), et les liquidités sans lui.

Paramètres

AppelRésultat
input("Period", 14, { min: 2, max: 100, step: 1 })Un champ numérique (maintenu entre min et max). Renvoie la valeur saisie, ou 14.
input("Average", "EMA", { options: ["SMA", "EMA"] })Une liste. Renvoie le texte choisi.

Jusqu’à 30 paramètres. Leurs valeurs sont enregistrées avec la stratégie et l’accompagnent quand vous la publiez dans une idée.

Calculs (ta.*)

FonctionRenvoie
ta.sma(src, n), ta.ema(src, n), ta.wma(src, n), ta.rma(src, n)Moyenne mobile simple, exponentielle, pondérée et de Wilder de n’importe quelle série.
ta.rsi(src, 14)RSI (0–100).
ta.atr(14), ta.tr()Average True Range (moyenne simple, comme sur le graphique) et la True Range de chaque bougie.
ta.stdev(src, n)Écart type sur n bougies.
ta.highest(src, n), ta.lowest(src, n)Valeur la plus haute / la plus basse des n dernières bougies, celle-ci comprise. Pour une cassure, comparez avec [i - 1].
ta.change(src, n = 1), ta.roc(src, n = 1)Écart avec n bougies plus tôt, et le même en %.
ta.macd(src, 12, 26, 9){ macd, signal, hist }.
ta.bb(src, 20, 2)Bandes de Bollinger { upper, middle, lower }.
ta.stoch(14, 3, 3)Stochastique { k, d } (période, lissage %D, ralentissement).
ta.supertrend(10, 3){ line, dir } : dir vaut 1 en tendance haussière, -1 en tendance baissière.

src est n’importe quelle série : close, high, un autre résultat ou votre propre tableau. Chaque fonction renvoie un nouveau tableau ; rien ne modifie les bougies.

Signaux

FonctionVrai quand…
ta.crossover(a, b, i)a croise b à la hausse sur la bougie i (il était égal ou en dessous sur i - 1). b peut être un nombre : ta.crossover(rsi, 30, i).
ta.crossunder(a, b, i)a croise b à la baisse.
ta.cross(a, b, i)Les deux.
ta.rising(s, n, i), ta.falling(s, n, i)s a monté (baissé) sur chacune des n dernières bougies.

Ordres

AppelEffet (à l’ouverture de la bougie suivante)
long(options) (aussi buy)Ouvre une position acheteuse ; clôture d’abord une vente.
short(options) (aussi sell)Ouvre une position vendeuse ; clôture d’abord un achat. Si les ventes sont désactivées, il ne fait que clôturer l’achat.
exit("reason")Clôture la position ouverte. La raison apparaît dans la liste des trades.
stopLoss(price), takeProfit(price)Déplace immédiatement le stop / l’objectif du trade ouvert (null le supprime). Utilisez-les pour des stops suiveurs.
OptionSignification
{ sl: 1.0820, tp: 1.0950 }Stop loss et take profit en prix.
{ slPct: 2, tpPct: 4 }Stop et objectif en % depuis le prix d’exécution.
{ qty: 50000 }Taille en unités pour cet ordre (au lieu du réglage Taille d’ordre).
{ comment: "breakout" }Une note affichée avec le trade.

Sortie

AppelEffet
plot(series, "Name", "#2F80ED")Trace une ligne sur le graphique des prix (jusqu’à 12).
log("text", value, …)Écrit une ligne dans l’onglet Log avec l’heure de la bougie (jusqu’à 200 lignes).

Les 71 indicateurs du graphique dans le code : ind()

ind(id, inputs) calcule n’importe quel indicateur du graphique exactement comme le graphique. Il renvoie sa première ligne sous forme de série ; toutes les lignes sont dans .lines et leurs noms dans .names. Les paramètres omis prennent la valeur par défaut indiquée. method vaut SMA, EMA, SMMA ou LWMA ; price vaut close, open, high, low, median, typical ou 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];
idIndicateurParamètres (par défaut)Lignes
maMoving Averageperiod: 20, method: SMA, price: close, shift: 0MA
amaAdaptive Moving Averageperiod: 9, fast: 2, slow: 30, price: closeAMA
adxAverage Directional Movement Indexperiod: 14ADX, +DI, −DI
adxwADX Wilderperiod: 14ADX, +DI, −DI
bbBollinger Bandsperiod: 20, dev: 2, price: close, shift: 0Middle, Upper, Lower
demaDouble Exponential Moving Averageperiod: 14, price: closeDEMA
envEnvelopesperiod: 14, method: SMA, dev: 0.1, price: closeUpper, Lower
framaFractal Adaptive Moving Averageperiod: 14, price: closeFrAMA
ichimokuIchimoku Kinko Hyotenkan: 9, kijun: 26, senkou: 52Tenkan, Kijun, Span A, Span B, Chikou
sarParabolic SARstep: 0.02, max: 0.2SAR
stddevStandard Deviationperiod: 20, method: SMA, price: closeStdDev
temaTriple Exponential Moving Averageperiod: 14, price: closeTEMA
vidyaVariable Index Dynamic Averagecmo: 9, ema: 12, price: closeVIDYA
atrAverage True Rangeperiod: 14ATR
bearsBears Powerperiod: 13Bears
bullsBulls Powerperiod: 13Bulls
cciCommodity Channel Indexperiod: 14, price: typicalCCI
chaikinChaikin Oscillatorfast: 3, slow: 10, method: EMACHO
demarkerDeMarkerperiod: 14DeMarker
forceForce Indexperiod: 13, method: SMAForce
macdMACDfast: 12, slow: 26, signal: 9, price: closeMACD, Signal
momentumMomentumperiod: 14, price: closeMomentum
osmaMoving Average of Oscillatorfast: 12, slow: 26, signal: 9, price: closeOsMA
rsiRelative Strength Indexperiod: 14, price: closeRSI
rviRelative Vigor Indexperiod: 10RVI, Signal
stochStochastic Oscillatork: 5, d: 3, slowing: 3, method: SMA%K, %D
trixTriple Exponential Averageperiod: 14, price: closeTRIX
wprWilliams' Percent Rangeperiod: 14%R
adAccumulation/DistributionaucunA/D
mfiMoney Flow Indexperiod: 14MFI
obvOn Balance VolumeaucunOBV
volumesVolumesaucunVolume
acAccelerator OscillatoraucunAC
alligatorAlligatorjaw: 13, jawShift: 8, teeth: 8, teethShift: 5, lips: 5, lipsShift: 3Jaw, Teeth, Lips
aoAwesome OscillatoraucunAO
fractalsFractalsaucunUp, Down
gatorGator Oscillatorjaw: 13, teeth: 8, lips: 5Upper, Lower
bwmfiMarket Facilitation IndexaucunBW MFI
zigzagZigZagdepth: 12, dev: 0.3ZigZag
vwapVWAP (volume-weighted average price)aucunVWAP
supertrendSuperTrendperiod: 10, mult: 3Up trend, Down trend
keltnerKeltner Channelsperiod: 20, atr: 10, mult: 2Middle, Upper, Lower
donchianDonchian Channelsperiod: 20Upper, Lower, Middle
hmaHull Moving Averageperiod: 20, price: closeHMA
pivotsPivot Points (classic)aucunP, R1, S1, R2, S2, R3, S3
stochrsiStochastic RSIrsi: 14, stoch: 14, k: 3, d: 3%K, %D
aroonAroonperiod: 25Aroon up, Aroon down
cmfChaikin Money Flowperiod: 20CMF
uoUltimate Oscillatorp1: 7, p2: 14, p3: 28UO
chopChoppiness Indexperiod: 14CHOP
vwmaVolume-Weighted Moving Averageperiod: 20VWMA
zlemaZero-Lag Exponential Moving Averageperiod: 21, price: closeZLEMA
almaArnaud Legoux Moving Averageperiod: 9, offset: 0.85, sigma: 6, price: closeALMA
mcginleyMcGinley Dynamicperiod: 14McGinley
lsmaLeast Squares Moving Averageperiod: 25, price: closeLSMA
chandelierChandelier Exitperiod: 22, mult: 3Long exit, Short exit
rocRate of Changeperiod: 12ROC
cmoChande Momentum Oscillatorperiod: 9CMO
tsiTrue Strength Indexlong: 25, short: 13, signal: 13TSI, Signal
ppoPercentage Price Oscillatorfast: 12, slow: 26, signal: 9Histogram, PPO, Signal
dpoDetrended Price Oscillatorperiod: 20DPO
kstKnow Sure Thingsignal: 9KST, Signal
vortexVortex Indicatorperiod: 14VI+, VI−
eomEase of Movementperiod: 14EOM
massMass Indexema: 9, sum: 25Mass Index
coppockCoppock Curvewma: 10, long: 14, short: 11Coppock
hvHistorical Volatilityperiod: 20HV %
fisherFisher Transformperiod: 9Fisher, Trigger
squeezeSqueeze Momentumbb: 20, bbk: 2, kc: 20, kck: 1.5Momentum, Squeeze on, Squeeze off
crsiConnors RSIrsi: 3, streak: 2, rank: 100CRSI
pvtPrice Volume TrendaucunPVT

Exemples

Chaque exemple fonctionne tel quel : appuyez sur Copier, ouvrez la console, choisissez Nouveau script…, collez et appuyez sur Lancer le test. Les résultats dépendent du symbole, de l’unité de temps et de la période ; les exemples enseignent la technique, ce ne sont pas des recommandations.

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" });
  }
}

Les erreurs et comment les corriger

MessageCause et solution
the script needs a function onBar(i) { … }Le script n’a pas de onBar, ou il est mal orthographié (onbar). Écrivez function onBar(i) { … } au niveau principal du script, pas dans une autre fonction.
close[124] is in the future: inside onBar(123) only candles up to 123 are knownVous avez lu une bougie ultérieure. Utilisez close[i] et les précédentes (close[i - 1]). Pour une cassure, comparez la clôture du jour au plus haut d’hier : close[i] > hi[i - 1].
close cannot be changedLes bougies sont en lecture seule. Créez plutôt votre propre tableau : const mid = close.map((c, i) => (c + open[i]) / 2);
Cannot read properties of null / NaN dans les résultatsUne série vaut encore null au début (phase de chauffe). Commencez onBar par if (avg[i] == null) return;, et vérifiez i >= 1 avant d’utiliser [i - 1].
unknown indicator "…"Utilisez un identifiant de la table des indicateurs (en minuscules), par ex. ind("adx"), pas ind("ADX").
Aucun trade : la stratégie n’a jamais passé d’ordreLes conditions n’ont jamais été vraies en même temps. Ajoutez log("rsi", r[i]) pour voir les valeurs, assouplissez une condition ou chargez plus de bougies.
La stratégie a pris plus de 15 secondes (une boucle infinie ?)Une boucle qui ne finit jamais, ou une boucle lourde dans onBar. Calculez les séries une fois au début au lieu de boucler sur toutes les bougies à chaque bougie.
Unexpected token / SyntaxError (ligne N)Une faute de frappe sur cette ligne : une parenthèse, un guillemet ou une virgule manquant.

Des tests fiables

  • Ajoutez vos coûts. Une stratégie qui trade souvent peut sembler excellente sans coûts et perdre de l’argent avec un spread réaliste. Renseignez toujours le slippage (le spread) et la commission.
  • Utilisez assez de trades. En dessous d’environ 30 trades, les chiffres relèvent surtout du hasard. Chargez plus de bougies ou utilisez une unité de temps plus courte.
  • N’ajustez pas au passé. Essayer de nombreux paramètres jusqu’à un résultat parfait trouve le bruit de cette période, pas une règle qui fonctionne. Réglez sur une partie de l’historique, puis vérifiez les mêmes paramètres sur une autre période et sur quelques autres symboles.
  • Comparez avec l’achat-conservation et regardez le drawdown, pas seulement le profit.
  • Surveillez les extrêmes. Si un ou deux trades font tout le profit, la stratégie dépend de la chance.
  • Entraînez-vous avant de risquer de l’argent. Faites tourner la stratégie en avant en trading fictif ou avec le replay des barres, et créez des alertes pour ses signaux.

Les back-tests utilisent les bougies bid passées et ignorent les swaps, le financement et les spreads qui varient avec le marché. Les résultats passés ne garantissent pas les résultats futurs. Ceci n’est pas un conseil en investissement.

Enregistrer, partager, publier

  • Enregistrer (connecté) conserve jusqu’à 50 stratégies avec leurs paramètres et réglages ; elles apparaissent dans la liste sur chaque graphique.
  • Publiez une idée depuis le graphique et joignez une stratégie : les lecteurs peuvent ouvrir votre graphique et lancer eux-mêmes la stratégie avec vos paramètres. Voir Idées.
  • Partagez votre analyse avec une équipe dans un club.
  • Télécharger le CSV dans l’onglet Trades exporte les trades vers un tableur.
  • Pour une recherche plus poussée, récupérez les mêmes bougies avec l’API de prix (GET /bars) dans Python, R ou Excel.

Limites

Bougies par testVisiteurs : les bougies du graphique. Connecté : jusqu’à 20,000 (les offres Premium peuvent l’augmenter ; voir Premium).
Script50 000 caractères, 30 paramètres, 12 lignes tracées, 200 lignes de journal, 5 000 trades listés, 15 secondes d’exécution.
Stratégies enregistrées50 par compte.
PositionsUne position à la fois par test (achat, vente ou aucune) ; un symbole et une unité de temps par test.

Questions

Faut-il savoir programmer ?

Non. Les stratégies intégrées fonctionnent sans code ; vous ne modifiez que leurs paramètres. Pour aller plus loin, Modifier une copie vous donne un script fonctionnel à changer ligne par ligne, et les exemples ci-dessus couvrent les briques habituelles.

Est-ce comme Pine Script ?

L’idée est la même (séries, paramètres, tracés, ordres), le langage est du JavaScript standard. La plupart des stratégies Pine simples se traduisent ligne par ligne : ta.sma(close, 20) reste ta.sma(close, 20), strategy.entry("L", strategy.long) devient long(), et le corps du script va dans onBar(i) avec [i] après chaque série.

La stratégie peut-elle trader pour moi ?

Non. La console ne teste que sur des bougies passées. Pour suivre une stratégie en direct, utilisez le trading fictif et les alertes, ou appelez l’API de prix depuis votre propre programme.

Pourquoi mon résultat diffère-t-il de celui d’une autre plateforme ?

Des bougies différentes (prix bid ou moyens, horaires de séance du courtier), des exécutions à l’ouverture suivante plutôt qu’à la clôture, les coûts et la règle du stop avant l’objectif modifient tous les résultats. Comparez ce qui est comparable.

Mon code est-il privé ?

Oui. Il s’exécute dans votre navigateur ; il n’est stocké dans votre compte que si vous appuyez sur Enregistrer, et les autres ne le voient que si vous le publiez dans une idée.