Anleitung · Strategietester

Die Backtest-Konsole: Handelsstrategien mit Code testen

Jeder Chart hat eine Schaltfläche Strategie. Sie öffnet eine Konsole, die eine Strategie über vergangene Kerzen abspielt und jeden Trade auf dem Chart zeigt, den Gewinn, den Drawdown, die Trefferquote und eine Kapitalkurve im Vergleich zu Buy and Hold. Beginnen Sie mit einer integrierten Strategie und schreiben Sie dann Ihre eigene in wenigen Zeilen JavaScript.

Konsole für EURUSD öffnen Zu den Beispielen springen

Sicher durch Bauweise. Ihr Skript läuft nur in Ihrem Browser, in einer Sandbox ohne Internetzugang: Es kann die Kerzen lesen, die es erhält, und sonst nichts (nicht Ihr Konto, nicht diese Website, keine anderen Websites). Nichts, was Sie schreiben, läuft auf unseren Servern.

In einer Minute starten

  1. Öffnen Sie einen beliebigen Chart, zum Beispiel EURUSD, und wählen Sie den zu testenden Zeitrahmen (H1, D1…).
  2. Drücken Sie Strategie in der Werkzeugleiste des Charts. Die Konsole öffnet sich unter dem Chart.
  3. Wählen Sie eine integrierte Strategie, zum Beispiel Kreuzung gleitender Durchschnitte. Sie läuft sofort: Pfeile auf dem Chart markieren jeden Ein- und Ausstieg.
  4. Ändern Sie eine Eingabe (etwa den langsamen Durchschnitt von 30 auf 50): Der Test läuft von selbst erneut.
  5. Lesen Sie Übersicht für die Summen, Trades für jeden Trade, Kapitalkurve für das Konto über die Zeit.
  6. Drücken Sie Kopie bearbeiten, um sie zu Ihrem eigenen Skript zu machen, ändern Sie sie und drücken Sie Test ausführen (oder Ctrl+Enter).

Die Konsole, Teil für Teil

TeilWas es tut
StrategielisteIntegriert: sechs fertige Strategien (Kreuzung gleitender Durchschnitte, RSI-Umkehr, Bollinger-Bänder-Mean-Reversion, MACD-Signalkreuzung, Donchian-Ausbruch, SuperTrend). Meine Strategien: die von Ihnen gespeicherten (Anmeldung nötig). Neues Skript… beginnt ein leeres.
Code ansehen / Kopie bearbeitenCode ansehen zeigt genau, was eine integrierte Strategie tut. Kopie bearbeiten kopiert sie als Ihr eigenes Skript in den Editor, bereit zum Ändern.
EingabenJedes input(…) im Skript wird hier zu einem Feld (eine Zahl oder eine Liste). Eine Änderung startet den Test neu. Standardwerte setzt sie zurück.
EinstellungenKapital, Ordergröße, Kosten, wie viele Kerzen und ob Short-Trades erlaubt sind (unten).
EditorDas JavaScript der Strategie. Tab rückt ein, Ctrl/⌘+Enter führt aus. Ein Fehler zeigt seine Meldung und die Zeilennummer.
ReferenzEine kurze Liste von allem, was ein Skript nutzen kann, direkt in der Konsole. Diese Seite ist die lange Fassung.
Speichern / Speichern unter…Behält das Skript, seinen Namen, seine Eingaben und Einstellungen in Ihrem Konto (ein Punkt bei Speichern • bedeutet ungespeicherte Änderungen). Ihre Strategien sind auf jedem Chart aufgeführt.
Ergebnis-TabsÜbersicht, Trades (mit CSV herunterladen), Kapitalkurve und Log. Trades und gezeichnete Linien werden auch auf dem Chart dargestellt; klicken Sie auf einen Trade, um zu ihm zu springen.

Einstellungen

EinstellungBedeutungGuter Startwert
StartkapitalDas Konto, mit dem der Test beginnt, in der Währung des Kurses (für EURUSD: Dollar).10,000
Ordergröße% des Kapitals: Jeder Trade nutzt diesen Anteil des Kontos zum jeweiligen Zeitpunkt (100 % = das ganze Konto, ohne Hebel). Einheiten: eine feste Menge (bei Forex Einheiten der Basiswährung: 100.000 = ein Standardlot). Ein Skript kann mit { qty } seine eigene Größe pro Order festlegen.100 % des Kapitals oder ein festes Lot
Kommission % pro SeiteEine Gebühr als Prozentsatz des gehandelten Werts, beim Einstieg und erneut beim Ausstieg berechnet.0,002–0,01 für Forex, 0,05–0,1 für Aktien und Krypto
Slippage / Spread (Kurs)Wird bei jeder Ausführung gegen Sie aufgeschlagen, in Kurseinheiten. Die Kerzen sind Bid-Kurse; tragen Sie daher hier den typischen Spread ein, damit Käufe realistisch sind.EURUSD 0,00010 (1 Pip), Gold 0,30
KerzenAuf dem Chart: die auf dem Bildschirm geladenen Kerzen. Angemeldet wählen Sie Bis zu 5.000 … 100.000, um mehr Historie desselben Zeitrahmens zu laden (diese Website erlaubt bis zu 20,000).So viele wie möglich: mehr Kerzen, mehr Trades, verlässlichere Zahlen
Short-Trades erlaubenAus: short() schließt nur eine Long-Position und eröffnet nie einen Short. Praktisch, um nur die Long-Seite zu testen, ohne das Skript zu ändern.Für Forex an, für reine Long-Anlagen aus

Die Ergebnisse lesen

ZahlWas es Ihnen sagt
NettogewinnEndkapital minus Startkapital, nach Kommission und Slippage; auch in % des Kapitals.
Kaufen und haltenWas einfaches Kaufen bei der ersten Kerze und Halten bis zur letzten eingebracht hätte. Eine Strategie, die das nicht schlägt oder nur mit viel tieferem Drawdown, bringt wenig.
Maximaler DrawdownDer größte Rückgang des Kapitals von einem Hoch, in Geld und %. Der Schmerz, den Sie hätten aushalten müssen. Viele Trader betrachten Nettogewinn ÷ maximalen Drawdown.
Trades, % gewonnenWie viele Trades geschlossen wurden und der Anteil mit Gewinn. Eine niedrige Trefferquote ist in Ordnung, wenn die Gewinner viel größer sind als die Verlierer.
ProfitfaktorBruttogewinn ÷ Bruttoverlust. Über 1 verdient Geld; 1,3–2 ist typisch für solide Strategien; sehr hohe Werte bei wenigen Trades sind meist Glück.
Durchschnittlicher Trade, durchschnittlicher Gewinn, durchschnittlicher VerlustDurchschnittlicher Gewinn pro Trade und die typische Größe von Gewinnern und Verlierern. Vergleichen Sie den durchschnittlichen Trade mit Ihren Kosten.
Größter Gewinn / Verlust, Verlusttrades in FolgeDie Extreme. Ein einziger riesiger Gewinner, der das ganze Ergebnis trägt, ist ein Warnzeichen.
Sharpe RatioRendite pro Einheit Volatilität, annualisiert aus den Kapitalveränderungen von Kerze zu Kerze. Über 1 ist gut, über 2 sehr gut (und selten).
Zeit im MarktDer Anteil der Kerzen mit offener Position. Weniger Zeit für denselben Gewinn bedeutet weniger Risiko.
Long / ShortTrades, Gewinn und Trefferquote jeder Seite getrennt: Oft macht eine Seite die ganze Arbeit.

Der Tab Trades listet jeden Trade mit Richtung, Ein- und Ausstiegszeit und -kurs, Größe, Gewinn in Geld und %, wie viele Kerzen er dauerte, warum er geschlossen wurde (Signal, Stop-Loss, Take-Profit, gedreht, am Ende noch offen) und Ihrem Kommentar. CSV herunterladen exportiert ihn für eine Tabelle. Die Kapitalkurve zeigt das Konto im Vergleich zu Buy and Hold. Das Log zeigt, was Ihr Skript mit log() geschrieben hat.

Wie Orders ausgeführt werden

  • Kein Blick nach vorn. Eine in onBar(i) platzierte Order wird zur Eröffnung der nächsten Kerze (i + 1) ausgeführt. Eine Strategie kann nie zu einem Kurs handeln, den sie nicht hätte kennen können.
  • Stops innerhalb der Kerze. Stop-Loss und Take-Profit werden gegen Hoch und Tief jeder Kerze geprüft. Hätten beide in derselben Kerze erreicht werden können, zählt der Stop (die vorsichtige Annahme). Eine Lücke durch einen Stop wird zur Eröffnung ausgeführt, wie an echten Märkten.
  • Eine Position gleichzeitig. long() bei einer Short-Position schließt den Short und eröffnet einen Long („gedreht“); long() bei bereits bestehender Long-Position aktualisiert nur die übergebenen Stops.
  • Kosten bei jeder Ausführung. Slippage verschiebt jeden Ausführungskurs gegen Sie; Kommission wird bei Ein- und Ausstieg berechnet.
  • Das Ende. Ein nach der letzten Kerze noch offener Trade wird zum letzten Schlusskurs geschlossen und als „am Ende noch offen“ markiert.
onBar(i): long() filled at this open candle i + 1

Ihre eigene Strategie schreiben

Eine Strategie ist ein kurzes JavaScript-Programm in zwei Teilen:

  1. Der Anfang läuft einmal: Lesen Sie Ihre Eingaben mit input(), berechnen Sie ganze Reihen mit ta.* oder ind() und zeichnen Sie sie mit plot().
  2. function onBar(i) { … } läuft für jede Kerze, älteste zuerst. i ist die Nummer der Kerze (0 = die älteste). Entscheiden Sie hier: long(), short(), exit() oder nichts.
// 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");
}

Was man von Anfang an wissen sollte:

  • Reihen sind Arrays, ein Wert pro Kerze, älteste zuerst: close[i] ist der Schluss dieser Kerze, close[i - 1] der davor. Berechnete Reihen sind null, bis genug Kerzen vorhanden sind; prüfen Sie das vor der Verwendung.
  • Innerhalb von onBar(i) können Sie Kerzen nur bis i lesen. close[i + 1] stoppt den Test mit einem Fehler: Das wäre ein Blick in die Zukunft.
  • Verwenden Sie const und let wie gewohnt; Ihre eigenen Variablen behalten ihre Werte zwischen den Kerzen (praktisch für einen Trailing-Stop).
  • Kurse sind in den eigenen Einheiten des Charts (für EURUSD 1,0850, für Gold 2650,40); die Zeit ist in UTC-Sekunden.

Referenz

Kerzen und der aktuelle Zustand

NameWas es ist
open, high, low, close, volumeArrays der Kerzenwerte, älteste zuerst (Volumen ist das Tick-Volumen).
timeDie Eröffnungszeit jeder Kerze in UTC-Sekunden. new Date(time[i] * 1000).getUTCHours() liefert die Stunde.
bars, symbol, timeframeDie Anzahl der Kerzen, der Ticker (z. B. "EURUSD") und der Zeitrahmen (z. B. "H1").
positionInnerhalb von onBar: 1 Long, -1 Short, 0 keine Position.
positionSize, entryPrice, entryBarGröße in Einheiten, Ausführungskurs und Kerzennummer des offenen Trades (null ohne Position). i - entryBar = gehaltene Kerzen.
equity, cashDer Kontowert zum Schluss dieser Kerze (mit dem offenen Trade) und das Bargeld ohne ihn.

Eingaben

AufrufErgebnis
input("Period", 14, { min: 2, max: 100, step: 1 })Ein Zahlenfeld (zwischen min und max gehalten). Liefert den eingegebenen Wert oder 14.
input("Average", "EMA", { options: ["SMA", "EMA"] })Eine Liste. Liefert den gewählten Text.

Bis zu 30 Eingaben. Ihre Werte werden mit der Strategie gespeichert und reisen mit ihr, wenn Sie sie in einer Idee veröffentlichen.

Berechnungen (ta.*)

FunktionLiefert
ta.sma(src, n), ta.ema(src, n), ta.wma(src, n), ta.rma(src, n)Einfacher, exponentieller, gewichteter und Wilder-Durchschnitt einer beliebigen Reihe.
ta.rsi(src, 14)RSI (0–100).
ta.atr(14), ta.tr()Average True Range (einfacher Durchschnitt, wie auf dem Chart) und die True Range jeder Kerze.
ta.stdev(src, n)Standardabweichung über n Kerzen.
ta.highest(src, n), ta.lowest(src, n)Höchster / niedrigster Wert der letzten n Kerzen, einschließlich dieser. Für einen Ausbruch vergleichen Sie mit [i - 1].
ta.change(src, n = 1), ta.roc(src, n = 1)Differenz zu vor n Kerzen und dieselbe in %.
ta.macd(src, 12, 26, 9){ macd, signal, hist }.
ta.bb(src, 20, 2)Bollinger-Bänder { upper, middle, lower }.
ta.stoch(14, 3, 3)Stochastik { k, d } (Periode, %D-Glättung, Verlangsamung).
ta.supertrend(10, 3){ line, dir }: dir ist 1 im Aufwärtstrend, -1 im Abwärtstrend.

src ist eine beliebige Reihe: close, high, ein anderes Ergebnis oder Ihr eigenes Array. Jede Funktion liefert ein neues Array; nichts verändert die Kerzen.

Signale

FunktionWahr, wenn…
ta.crossover(a, b, i)a kreuzt b auf Kerze i nach oben (es lag auf i - 1 bei oder unter ihm). b darf eine Zahl sein: ta.crossover(rsi, 30, i).
ta.crossunder(a, b, i)a kreuzt b nach unten.
ta.cross(a, b, i)Beides.
ta.rising(s, n, i), ta.falling(s, n, i)s ist auf jeder der letzten n Kerzen gestiegen (gefallen).

Orders

AufrufWirkung (zur Eröffnung der nächsten Kerze)
long(options) (auch buy)Eröffnet eine Long-Position; schließt vorher einen Short.
short(options) (auch sell)Eröffnet eine Short-Position; schließt vorher einen Long. Bei ausgeschalteten Short-Trades schließt es nur den Long.
exit("reason")Schließt die offene Position. Der Grund erscheint in der Trade-Liste.
stopLoss(price), takeProfit(price)Verschiebt Stop / Ziel des offenen Trades sofort (null entfernt sie). Nutzen Sie sie für Trailing-Stops.
OptionBedeutung
{ sl: 1.0820, tp: 1.0950 }Stop-Loss und Take-Profit als Kurse.
{ slPct: 2, tpPct: 4 }Stop und Ziel in % vom Ausführungskurs.
{ qty: 50000 }Größe in Einheiten für diese Order (statt der Einstellung Ordergröße).
{ comment: "breakout" }Eine Notiz, die mit dem Trade angezeigt wird.

Ausgabe

AufrufWirkung
plot(series, "Name", "#2F80ED")Zeichnet eine Linie auf den Kurschart (bis zu 12).
log("text", value, …)Schreibt eine Zeile mit der Zeit der Kerze in den Tab „Log“ (bis zu 200 Zeilen).

Alle 71 Chart-Indikatoren im Code: ind()

ind(id, inputs) berechnet jeden Indikator des Charts genau wie der Chart. Es liefert seine erste Linie als Reihe; alle Linien stehen in .lines und ihre Namen in .names. Ausgelassene Eingaben nehmen den angezeigten Standardwert. method ist eines von SMA, EMA, SMMA, LWMA; price eines von 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];
idIndikatorEingaben (Standard)Linien
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/DistributionkeineA/D
mfiMoney Flow Indexperiod: 14MFI
obvOn Balance VolumekeineOBV
volumesVolumeskeineVolume
acAccelerator OscillatorkeineAC
alligatorAlligatorjaw: 13, jawShift: 8, teeth: 8, teethShift: 5, lips: 5, lipsShift: 3Jaw, Teeth, Lips
aoAwesome OscillatorkeineAO
fractalsFractalskeineUp, Down
gatorGator Oscillatorjaw: 13, teeth: 8, lips: 5Upper, Lower
bwmfiMarket Facilitation IndexkeineBW MFI
zigzagZigZagdepth: 12, dev: 0.3ZigZag
vwapVWAP (volume-weighted average price)keineVWAP
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)keineP, 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 TrendkeinePVT

Beispiele

Jedes Beispiel läuft so, wie es ist: Drücken Sie Kopieren, öffnen Sie die Konsole, wählen Sie Neues Skript…, fügen Sie ein und drücken Sie Test ausführen. Die Ergebnisse hängen von Ticker, Zeitrahmen und Zeitraum ab; die Beispiele lehren die Technik, sie sind keine Empfehlungen.

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

Fehler und wie man sie behebt

NachrichtUrsache und Lösung
the script needs a function onBar(i) { … }Das Skript hat kein onBar, oder es ist falsch geschrieben (onbar). Schreiben Sie function onBar(i) { … } auf der obersten Ebene des Skripts, nicht in einer anderen Funktion.
close[124] is in the future: inside onBar(123) only candles up to 123 are knownSie haben eine spätere Kerze gelesen. Verwenden Sie close[i] und frühere (close[i - 1]). Für einen Ausbruch vergleichen Sie den heutigen Schluss mit dem höchsten Hoch von gestern: close[i] > hi[i - 1].
close cannot be changedDie Kerzen sind schreibgeschützt. Erstellen Sie stattdessen Ihr eigenes Array: const mid = close.map((c, i) => (c + open[i]) / 2);
Cannot read properties of null / NaN in den ErgebnissenEine Reihe ist am Anfang noch null (Aufwärmphase). Beginnen Sie onBar mit if (avg[i] == null) return; und prüfen Sie i >= 1, bevor Sie [i - 1] verwenden.
unknown indicator "…"Verwenden Sie eine ID aus der Indikatortabelle (Kleinbuchstaben), z. B. ind("adx"), nicht ind("ADX").
Keine Trades: Die Strategie hat nie eine Order platziertDie Bedingungen wurden nie gemeinsam erfüllt. Fügen Sie log("rsi", r[i]) hinzu, um die Werte zu sehen, lockern Sie eine Bedingung oder laden Sie mehr Kerzen.
Die Strategie hat länger als 15 Sekunden gebraucht (eine Endlosschleife?)Eine Schleife, die nie endet, oder eine schwere Schleife in onBar. Berechnen Sie Reihen einmal am Anfang, statt bei jeder Kerze über alle Kerzen zu iterieren.
Unexpected token / SyntaxError (Zeile N)Ein Tippfehler in dieser Zeile: eine fehlende Klammer, ein fehlendes Anführungszeichen oder Komma.

Tests, denen Sie vertrauen können

  • Berücksichtigen Sie Ihre Kosten. Eine Strategie, die oft handelt, kann ohne Kosten großartig aussehen und mit einem realistischen Spread Geld verlieren. Füllen Sie immer Slippage (den Spread) und Kommission aus.
  • Nutzen Sie genug Trades. Unter etwa 30 Trades sind die Zahlen überwiegend Zufall. Laden Sie mehr Kerzen oder nutzen Sie einen niedrigeren Zeitrahmen.
  • Passen Sie nicht an die Vergangenheit an. Viele Eingaben auszuprobieren, bis das Ergebnis perfekt ist, findet das Rauschen dieses Zeitraums, keine funktionierende Regel. Optimieren Sie auf einem Teil der Historie und prüfen Sie dieselben Eingaben dann in einem anderen Zeitraum und bei einigen anderen Tickern.
  • Vergleichen Sie mit Buy and Hold und achten Sie auf den Drawdown, nicht nur auf den Gewinn.
  • Achten Sie auf Ausreißer. Wenn ein oder zwei Trades den ganzen Gewinn bringen, hängt die Strategie vom Glück ab.
  • Üben Sie, bevor Sie Geld riskieren. Lassen Sie die Strategie im Papierhandel oder mit Bar-Replay vorwärts laufen und setzen Sie Alarme für ihre Signale.

Backtests verwenden vergangene Bid-Kerzen und ignorieren Swaps, Finanzierung und Spreads, die sich mit dem Markt ändern. Vergangene Ergebnisse garantieren keine zukünftigen. Dies ist keine Anlageberatung.

Speichern, teilen, veröffentlichen

  • Speichern (angemeldet) behält bis zu 50 Strategien mit ihren Eingaben und Einstellungen; sie erscheinen in der Liste auf jedem Chart.
  • Veröffentlichen Sie eine Idee aus dem Chart und hängen Sie eine Strategie an: Leser können Ihren Chart öffnen und die Strategie mit Ihren Eingaben selbst ausführen. Siehe Ideen.
  • Teilen Sie Ihre Analyse mit einem Team in einem Club.
  • CSV herunterladen im Tab „Trades“ exportiert die Trades in eine Tabelle.
  • Für umfangreichere Forschung laden Sie dieselben Kerzen über die Kurs-API (GET /bars) in Python, R oder Excel.

Limits

Kerzen pro TestBesucher: die Kerzen auf dem Chart. Angemeldet: bis zu 20,000 (die Premium-Tarife können dies erhöhen; siehe Premium).
Skript50.000 Zeichen, 30 Eingaben, 12 gezeichnete Linien, 200 Log-Zeilen, 5.000 aufgelistete Trades, 15 Sekunden Laufzeit.
Gespeicherte Strategien50 pro Konto.
PositionenEine Position gleichzeitig pro Test (Long, Short oder keine); ein Ticker und ein Zeitrahmen pro Test.

Fragen

Muss ich programmieren können?

Nein. Die integrierten Strategien funktionieren ohne Code; Sie ändern nur ihre Eingaben. Wenn Sie mehr wollen, gibt Ihnen Kopie bearbeiten ein funktionierendes Skript, das Sie Zeile für Zeile ändern können, und die Beispiele oben decken die üblichen Bausteine ab.

Ist das wie Pine Script?

Die Idee ist dieselbe (Reihen, Eingaben, Zeichnungen, Orders), die Sprache ist einfaches JavaScript. Die meisten einfachen Pine-Strategien lassen sich Zeile für Zeile übertragen: ta.sma(close, 20) bleibt ta.sma(close, 20), strategy.entry("L", strategy.long) wird zu long(), und der Hauptteil des Skripts kommt in onBar(i) mit [i] nach jeder Reihe.

Kann die Strategie für mich handeln?

Nein. Die Konsole testet nur auf vergangenen Kerzen. Um einer Strategie live zu folgen, nutzen Sie Papierhandel und Alarme oder rufen Sie die Kurs-API aus Ihrem eigenen Programm auf.

Warum unterscheidet sich mein Ergebnis von dem einer anderen Plattform?

Andere Kerzen (Bid- statt Mittelkurse, die Sitzungszeiten des Brokers), Ausführungen zur nächsten Eröffnung statt zum Schluss, Kosten und die Regel „Stop vor Ziel“ verändern die Ergebnisse. Vergleichen Sie Gleiches mit Gleichem.

Ist mein Code privat?

Ja. Es läuft in Ihrem Browser; es wird nur in Ihrem Konto gespeichert, wenn Sie Speichern drücken, und andere sehen es nur, wenn Sie es in einer Idee veröffentlichen.