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.
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
- Öffnen Sie einen beliebigen Chart, zum Beispiel EURUSD, und wählen Sie den zu testenden Zeitrahmen (H1, D1…).
- Drücken Sie Strategie in der Werkzeugleiste des Charts. Die Konsole öffnet sich unter dem Chart.
- Wählen Sie eine integrierte Strategie, zum Beispiel Kreuzung gleitender Durchschnitte. Sie läuft sofort: Pfeile auf dem Chart markieren jeden Ein- und Ausstieg.
- Ändern Sie eine Eingabe (etwa den langsamen Durchschnitt von 30 auf 50): Der Test läuft von selbst erneut.
- Lesen Sie Übersicht für die Summen, Trades für jeden Trade, Kapitalkurve für das Konto über die Zeit.
- 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
| Teil | Was es tut |
|---|---|
| Strategieliste | Integriert: 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 bearbeiten | Code ansehen zeigt genau, was eine integrierte Strategie tut. Kopie bearbeiten kopiert sie als Ihr eigenes Skript in den Editor, bereit zum Ändern. |
| Eingaben | Jedes input(…) im Skript wird hier zu einem Feld (eine Zahl oder eine Liste). Eine Änderung startet den Test neu. Standardwerte setzt sie zurück. |
| Einstellungen | Kapital, Ordergröße, Kosten, wie viele Kerzen und ob Short-Trades erlaubt sind (unten). |
| Editor | Das JavaScript der Strategie. Tab rückt ein, Ctrl/⌘+Enter führt aus. Ein Fehler zeigt seine Meldung und die Zeilennummer. |
| Referenz | Eine 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
| Einstellung | Bedeutung | Guter Startwert |
|---|---|---|
| Startkapital | Das 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 Seite | Eine 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 |
| Kerzen | Auf 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 erlauben | Aus: 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
| Zahl | Was es Ihnen sagt |
|---|---|
| Nettogewinn | Endkapital minus Startkapital, nach Kommission und Slippage; auch in % des Kapitals. |
| Kaufen und halten | Was 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 Drawdown | Der 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, % gewonnen | Wie 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. |
| Profitfaktor | Bruttogewinn ÷ 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 Verlust | Durchschnittlicher 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 Folge | Die Extreme. Ein einziger riesiger Gewinner, der das ganze Ergebnis trägt, ist ein Warnzeichen. |
| Sharpe Ratio | Rendite 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 Markt | Der Anteil der Kerzen mit offener Position. Weniger Zeit für denselben Gewinn bedeutet weniger Risiko. |
| Long / Short | Trades, 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.
Ihre eigene Strategie schreiben
Eine Strategie ist ein kurzes JavaScript-Programm in zwei Teilen:
- Der Anfang läuft einmal: Lesen Sie Ihre Eingaben mit
input(), berechnen Sie ganze Reihen mitta.*oderind()und zeichnen Sie sie mitplot(). function onBar(i) { … }läuft für jede Kerze, älteste zuerst.iist 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 sindnull, bis genug Kerzen vorhanden sind; prüfen Sie das vor der Verwendung. - Innerhalb von
onBar(i)können Sie Kerzen nur bisilesen.close[i + 1]stoppt den Test mit einem Fehler: Das wäre ein Blick in die Zukunft. - Verwenden Sie
constundletwie 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
| Name | Was es ist |
|---|---|
open, high, low, close, volume | Arrays der Kerzenwerte, älteste zuerst (Volumen ist das Tick-Volumen). |
time | Die Eröffnungszeit jeder Kerze in UTC-Sekunden. new Date(time[i] * 1000).getUTCHours() liefert die Stunde. |
bars, symbol, timeframe | Die Anzahl der Kerzen, der Ticker (z. B. "EURUSD") und der Zeitrahmen (z. B. "H1"). |
position | Innerhalb von onBar: 1 Long, -1 Short, 0 keine Position. |
positionSize, entryPrice, entryBar | Größe in Einheiten, Ausführungskurs und Kerzennummer des offenen Trades (null ohne Position). i - entryBar = gehaltene Kerzen. |
equity, cash | Der Kontowert zum Schluss dieser Kerze (mit dem offenen Trade) und das Bargeld ohne ihn. |
Eingaben
| Aufruf | Ergebnis |
|---|---|
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.*)
| Funktion | Liefert |
|---|---|
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
| Funktion | Wahr, 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
| Aufruf | Wirkung (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. |
| Option | Bedeutung |
|---|---|
{ 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
| Aufruf | Wirkung |
|---|---|
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];
| id | Indikator | Eingaben (Standard) | Linien |
|---|---|---|---|
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 | keine | A/D |
mfi | Money Flow Index | period: 14 | MFI |
obv | On Balance Volume | keine | OBV |
volumes | Volumes | keine | Volume |
ac | Accelerator Oscillator | keine | AC |
alligator | Alligator | jaw: 13, jawShift: 8, teeth: 8, teethShift: 5, lips: 5, lipsShift: 3 | Jaw, Teeth, Lips |
ao | Awesome Oscillator | keine | AO |
fractals | Fractals | keine | Up, Down |
gator | Gator Oscillator | jaw: 13, teeth: 8, lips: 5 | Upper, Lower |
bwmfi | Market Facilitation Index | keine | BW MFI |
zigzag | ZigZag | depth: 12, dev: 0.3 | ZigZag |
vwap | VWAP (volume-weighted average price) | keine | 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) | keine | 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 | keine | PVT |
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
| Nachricht | Ursache 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 known | Sie 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 changed | Die 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 Ergebnissen | Eine 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 platziert | Die 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 Test | Besucher: die Kerzen auf dem Chart. Angemeldet: bis zu 20,000 (die Premium-Tarife können dies erhöhen; siehe Premium). |
| Skript | 50.000 Zeichen, 30 Eingaben, 12 gezeichnete Linien, 200 Log-Zeilen, 5.000 aufgelistete Trades, 15 Sekunden Laufzeit. |
| Gespeicherte Strategien | 50 pro Konto. |
| Positionen | Eine 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.