SeitenLebenszyklus einer Strategie · 8 / 29
Strategie-Lebenszyklus
Eine QuantCraft-Strategie ist eine gewöhnliche Python-Datei, die eine kleine Menge von Callback-Funktionen definiert. Die Backtest-Engine ruft diese Funktionen in fester Reihenfolge auf, während sie die historischen Daten durchläuft. Sie rufen sie nicht selbst auf — das tut die Engine. Nutzen Sie Test, um sie auszulösen (Code ausführen).
Der Lebenszyklus von Anfang bis Ende:
on_init → on_bar → on_tick (×4 per bar) → account tick(close) → on_finish
└─────────── repeats for every bar ───────────┘Im Backtest wird on_tick viermal pro Balken auf synthetischem OHLC aus jedem Balken ausgelöst (keine echte Tick-Historie). Nach diesen vier Ticks nimmt die Engine eine letzte Mark-to-Market-Bewertung des Kontos zum Schluss vor — kein fünftes on_tick. on_timer steht nur bei Forward-Läufen zur Verfügung (Chart-Forward, Forward-Bulk / QuantCloud), wenn timerIntervalMs gesetzt ist — der IDE-Dialog Run backtest hat kein Timer-Bedienelement. Bei Forward-Läufen läuft on_tick bei jedem Kursupdate Ihres verbundenen Brokers; an Intraday-Grenzen wird oft on_tick dann on_bar ausgelöst — nehmen Sie nicht die Backtest-Reihenfolge an.
Diese Seite behandelt jeden Callback, wie Multi-Symbol-Strategien daran andocken, und die Grundlagen, die Sie für Ihre erste Strategie brauchen.
Die Strategiedatei
Eine minimale Strategie sieht so aus:
import quantcraft.process.runtime as bt # account, chart_indicators, qc_* handles
from quantcraft import Timeframe
import quantcraft.runtime as rt
def on_init(symbols=None):
pass
def on_bar(bar_index, bar, fundamentals=None, calendar=None, symbol=None):
close_px = float(bar["close"])
daily = rt.get_bar(Timeframe.DAILY, shift=1)
def on_tick(bar_index, tick_in_bar, price, bar, fundamentals=None, calendar=None, symbol=None):
if tick_in_bar == 3:
close_px = price
def on_finish(bars, symbol=None, bars_by_symbol=None):
passEinige Regeln gelten für alle Callbacks:
- Sie müssen Funktionen auf oberster Ebene in Ihrer Datei sein (nicht verschachtelt oder in einer Klasse).
- Die Backtest-Engine verlangt aufrufbare
on_init,on_bar,on_tickundon_finish(sonst scheitert der Lauf mitUser script must define callable: …). Die IDE-Schaltflächen Test, Compile und Debug Test erscheinen, wenn der aktive.py-Tabon_init/on_bar/on_finishdefiniert; ein fehlendeson_ticklässt den Backtest beim Start dennoch scheitern.on_timerist optional und wird nur bei Forward-Läufen mit Timer-Intervall genutzt. - Fügen Sie nur die optionalen Argumente hinzu, die Sie nutzen.
calendar,symbols(beion_init) und die Argumente vonon_finishwerden nach Namen zugeordnet;fundamentalsundsymbolwerden zugeordnet, indem nacheinander die gängigen Formen probiert werden, daher behalten Sie die dokumentierte Reihenfolge bei. Die sicherste vollständige Signatur istdef on_bar(bar_index, bar, fundamentals=None, calendar=None, symbol=None). Ein deklariertescalendarerhält immer das Kalender-Handle oderNone— nie das Fundamentaldaten-Objekt. - In Callbacks platzieren Sie Orders über
accountund zeichnen Indikatorwerte überchart_indicatorsauf (Indikatorserien) — beides wird ausquantcraft.process.runtimegelesen.
Runtime-Globals
Die Engine stellt diese aus quantcraft.process.runtime bereit, bevor Callbacks laufen:
| Name | Zweck |
|---|---|
account | PaperAccount-Instanz, konfiguriert über den Tab Simulation (Saldo + Provision / Slippage). Bei Forward-Läufen ist es ein Live-Broker-Konto mit derselben API, das echte Orders platziert — siehe Kontomodell |
chart_indicators | ChartIndicatorSeries-Collector, der ins Ergebnis zusammengeführt wird — sammeln Sie Werte, während der Lauf die Balken durchgeht, und geben Sie die Serien in on_finish aus |
qc_fundamentals | Optionales QcFundamentals-Handle, wenn Fundamentaldaten geladen sind |
qc_fama_french | Optionales QcFamaFrench, wenn globale Fama-French-Faktoren geladen sind; bevorzugen Sie in Callbacks fama_french.qc — siehe Fama-French-Faktoren |
qc_calendar | BacktestCalendar | None — Kalenderdaten, wenn Include calendar data aktiviert ist; deckt jedes gehandelte Symbol ab. Nur Backtests — bei Forward-Läufen immer None. Callbacks können dasselbe Handle erhalten, indem sie ein Argument calendar benennen — siehe Kalenderdaten |
qc_ohlcv_by_tf | Optionale Puffer sekundärer Zeitrahmen, indiziert über den Zeitrahmen-String (fortgeschritten; bevorzugen Sie get_bar) |
get_bar(tf, shift=1) | Empfohlene API für den primären und die zusätzlichen Zeitrahmen — gibt einen BarSnapshot oder None zurück; Import über import quantcraft.runtime as rt |
Bevorzugen Sie das Argument bars von on_finish(bars, ...) für Vollserien-Rechnungen — verlassen Sie sich in IDE-Strategien nicht auf das qc_ohlcv auf Modulebene (es ist beim Import oft None, bei einem Lauf mit Screener-Ziel None und umfasst sonst den gesamten geladenen Bereich einschließlich Warmup). Lesen Sie Kurse in Callbacks aus dem bar-Objekt. isinstance(bar, dict) ist False — OhlcvBarView ist ein Mapping (dict-artiger Zugriff funktioniert weiterhin).
Lesen Sie diese als Modulattribute. Jedes Handle wird von einem Wert pro Sitzung getragen, daher beziehen sich
bt.account/bt.chart_indicatorsimmer auf diesen Lauf.from quantcraft.process.runtime import accountbindet einen Schnappschuss — in der Algo-Stammdatei bei einem Backtest in Ordnung, weil die Engine diese Namen nach dem Laden neu bindet (Forward-Läufe binden nuraccount,qc_fundamentalsundqc_fama_frenchneu, nutzen Sie dort alsobt.chart_indicatorsundrt.qc_ohlcv*), aber falsch in einem aufgeteilten Hilfsmodul, das insys.modulesgecacht bleibt und das Handle der ersten Sitzung behalten würde. Siehe Modul-Handles vs. importierte Namen.
Sekundäre Zeitrahmen
Der primäre Zeitrahmen des Laufs ist immer über rt.get_bar verfügbar. Wählen Sie zusätzliche Balkengrößen im Dropdown Additional timeframes des Dialogs Run backtest unter Symbol & data (Backtests) und lesen Sie sie dann aus jedem Callback:
from quantcraft import Timeframe
import quantcraft.runtime as rt
def on_bar(bar_index, bar, fundamentals=None, calendar=None, symbol=None):
daily = rt.get_bar(Timeframe.DAILY, shift=1)
if daily and bar.close[0] > daily.close:
account.open_trade(...)Die vollständige Semantik von shift, das Enum Timeframe und die Hinweise zu Forward-Läufen finden Sie unter OHLCV- und Balkendaten.
Ausführungszeitpunkt
bar["close"]inon_barist der Schlusskurs des aktuellen Ausführungsbalkens.account.open_trade/close_tradenehmen einen angeforderten Kurs entgegen und wenden dann die Slippage- und Provisionsmodelle des Laufs an. Die gespeichertenentry_price/exit_pricesind die Ausführungskurse; die angeforderten Kurse bleiben alsraw_entry_price/raw_exit_priceerhalten. Beim standardmäßigen Volumenanteil-Slippage weichen Ausführungen meist vom übergebenen Kurs ab.- Um aus
on_barden Balkenschluss anzupeilen, übergeben Siefloat(bar["close"])(weiterhin Slippage/Provision unterworfen). - Um während der Intrabar-Simulation auszuführen, rufen Sie aus
on_tickauf, wenntick_in_bar == 3—priceist dann der Schlusskurs dieses Symbols für die Zeile.
on_init — alles vorbereiten
Läuft direkt, nachdem die Engine das Konto vorbereitet, Marktdaten geladen und die Runtime aufgebaut hat — aber bevor Balken verarbeitet werden.
Nutzen Sie sie, um:
- Variablen zu initialisieren, die Ihre Strategie zwischen Balken mitführt (Zähler, Zustandsautomaten, Parameterwerte).
- Indikatoren oder Warmup-Puffer zu konfigurieren.
- Eingabeparameter zu lesen, damit sie den Rest des Laufs steuern.
def on_init(symbols=None):
global lookback, position_size
lookback = 20
position_size = 100In on_init haben Sie keinen Zugriff auf einen aktuellen Balken — zu diesem Zeitpunkt hat die Simulation noch nicht begonnen.
Parameter symbols (nur Screener-Läufe): Nutzt der Backtest eine Screener-Symbolquelle, wird on_init bei jedem Rebalancing erneut mit symbols=<Liste der Ticker> für das neue Universum ausgelöst. So kann Ihre Strategie auf einen neuen Tickersatz reagieren, ohne die Simulation neu zu starten.
- Zu Beginn des Laufs wird
on_initzweimal kurz hintereinander ausgelöst — einmal mitsymbols=None(der Standard-Setup-Aufruf) und einmal mitsymbols=<anfängliche Ticker>. - Erneute Auslösungen setzen den Zustand auf Modulebene nicht zurück; Variablen, die Sie im ersten Aufruf gesetzt haben, sind weiterhin da.
- Strategien, die
def on_init():(ohne Parameter) definieren, funktionieren genau wie bisher — die Engine ordnet Argumente nach Namen zu, sodass der fehlende Parameter einfach ignoriert wird.
on_bar — einmal pro Balken
Läuft einmal pro Ausführungsbalken, vor den vier Intrabar-Ticks dieses Balkens. Hier treffen die meisten Strategien ihre Entscheidungen.
def on_bar(bar_index, bar, fundamentals=None, calendar=None, symbol=None):
if bar.bars_back < 1:
return
if bar.close[0] > bar.close[1]:
account.open_trade(...)Parameter:
bar_index— nullbasierter Index des aktuellen Ausführungsbalkens.bar— die Ansicht des aktuellen Balkens (OhlcvBarView). Verhält sich wie ein schreibgeschütztes Mapping (bar["open"],bar["high"],bar["low"],bar["close"],bar["volume"],bar["t"]) und bietet außerdem Historie über Offsets:bar.close[0]ist der Schlusskurs des aktuellen Balkens,bar.close[1]der des vorherigen Balkens, und so weiter.len(bar.close)undbar.bars_backsagen, wie viel Historie verfügbar ist. Es ist kein einfachesdict(isinstance(bar, dict)istFalse).fundamentals— Fundamentaldaten-Snapshot für diesen Balken oderNone, wenn nicht konfiguriert.calendar—BacktestCalendar-Handle, wenn Include calendar data aktiviert ist, sonstNone(bei Forward-Läufen immerNone). Die vollständige API siehe Kalenderdaten. Da Argumente nach Namen zugeordnet werden, lässt das Weglassen dieses Parameters Ihren Callback unverändert.symbol— Ticker-String in Großbuchstaben. Nützlich in Multi-Symbol-Strategien; Sie können ihn weglassen, wenn Sie nur ein Symbol laden.
Gängige Muster:
- Ein- und Ausstiege zum Balkenschluss entscheiden: Übergeben Sie
bar.close[0](oderfloat(bar["close"])) anaccount.open_trade/account.close_trade. - Einfache Indikatoren berechnen, indem Sie Offsets wie
bar.close[0],bar.close[1], … lesen. - Frühe Balken überspringen, die nicht genug Historie haben (
if bar.bars_back < N: return).
Tipp:
on_barläuft immer vor allen Tick-Callbacks desselben Balkens.
on_tick — Intrabar- und Live-Kursupdates
on_tick hat im Backtest und im Forward-Lauf dieselbe Signatur, doch wann es ausgelöst wird und was price bedeutet, hängt vom Laufmodus ab.
Backtest (Test)
Läuft bis zu viermal pro Balken, nach on_bar, um die Bewegung innerhalb des Balkens zu simulieren. Die Engine geht das OHLC jedes Balkens in dieser Reihenfolge durch:
tick_in_bar | Bedeutung |
|---|---|
0 | Balken-Eröffnung |
1 | Balken-Hoch |
2 | Balken-Tief |
3 | Balken-Schluss |
Keine echten Tickdaten im Backtest. Historische Läufe laden keine Broker-Tick-Historie. Die vier Aufrufe pro Balken nutzen synthetische Kurse, die allein aus dem OHLC jedes Balkens abgeleitet sind. Sie können Intrabar-Ausführungen in der Simulation annähern, aber das
on_tick-Verhalten im Backtest wird nicht den Live-Forward-Ticks entsprechen. Für Entscheidungen zum Balkenschluss reichton_bar—on_tickmittick_in_bar == 3entspricht in der Simulation dem Handeln auf den Balkenschluss.
def on_tick(bar_index, tick_in_bar, price, bar, fundamentals=None, calendar=None, symbol=None):
if tick_in_bar == 3:
# Backtest simulation: closing tick fill
account.close_trade(position_id, price)Nutzen Sie das Backtest-on_tick, wenn das Timing innerhalb des Balkens in der Simulation zählt — etwa um am Schluss-Tick auszuführen, einen Intrabar-Stop zu simulieren oder die Berührung des Hochs oder Tiefs des Balkens auszuwerten.
Forward-Läufe (Chart-Forward, Forward-Bulk / QuantCloud)
Bei Forward-Läufen läuft on_tick jedes Mal, wenn die Plattform einen Kurs von Ihrem verbundenen Broker erhält, solange Ihr Algo angehängt ist. Es gibt keinen festen OHLC-Durchlauf mit vier Ticks — jeder Aufruf spiegelt eine Live-Notierung wider.
def on_tick(bar_index, tick_in_bar, price, bar, fundamentals=None, calendar=None, symbol=None):
# Forward run: react to each broker price update
if price <= stop_level:
account.close_trade(position_id, price)price— die jüngste Broker-Notierung für dieses Symbol.bar— die Ansicht des aktuellen Balkens für die Historie (gleiche Form wie im Backtest).tick_in_bar— kann aus Signaturkompatibilität noch vorhanden sein; verzweigen Sie bei Forward-Läufen nachprice, nicht nach synthetischen OHLC-Phasen.
Nutzen Sie das Forward-on_tick für Live-Intrabar-Logik — Stops, Trailing-Ausstiege oder jede Reaktion auf Marks zwischen Balkenschlüssen.
Parameter (beide Modi)
bar_index— wie inon_bar.tick_in_bar— im Backtest0,1,2oder3(synthetischer OHLC-Schritt). Verlassen Sie sich bei Forward-Läufen nicht darauf für Balkenphasen-Logik.price— synthetischer OHLC-Schritt im Backtest; Live-Broker-Notierung bei Forward-Läufen.bar— dieselbe Balkenansicht wie inon_bar.fundamentals,calendar,symbol— gleiche Bedeutung wie inon_bar.
Wenn Sie im Backtest nur Entscheidungen zum Balkenschluss brauchen, können Sie
on_tickganz ignorieren und Ihre Logik inon_barlegen.
on_timer — optionaler periodischer Callback (nur Forward-Läufe)
IDE-Backtest: Der Dialog Run backtest setzt kein Timer-Intervall. Auch wenn Sie
on_timerin Ihrer Strategiedatei definieren, wird es bei IDE-Backtests nicht aufgerufen. Legen Sie periodische oder zeitbasierte Logik bei Backtests inon_baroderon_tick.
Forward-Läufe (Chart-Forward-Test, Forward-Bulk / QuantCloud) können in ihrem Lauf-Dialog timerIntervalMs setzen. Ist es konfiguriert, läuft on_timer in einem festen Zeitintervall zwischen Ticks, nur wenn zwei Bedingungen erfüllt sind:
- Sie haben
timer_interval_ms/timerIntervalMsfür den Lauf konfiguriert (eine positive ganze Zahl in Millisekunden). - Ihre Datei definiert eine Funktion
on_timer.
def on_timer(bar_index, tick_in_bar, price, bar, fundamentals=None, calendar=None, symbol=None):
passDie Signatur hat dieselbe Form wie on_tick. Nutzen Sie on_timer für zeitbasierte Verhaltensweisen (zum Beispiel: ein langsames Signal jede Minute aktualisieren oder eine Prüfung unabhängig von der Balkenzahl einplanen). Ist timer_interval_ms nicht gesetzt, wird on_timer nie aufgerufen, auch wenn es definiert ist.
Kalenderdaten in Callbacks
Ist Include calendar data im Tab Symbol & data aktiviert, kann jeder Callback ein BacktestCalendar-Handle erhalten, indem Sie einen Parameter calendar hinzufügen:
def on_bar(bar_index, bar, calendar=None, symbol=None):
if calendar is None: # always guard — not every run enables calendar data
return
days = calendar.days_to_earnings(bar["t"], symbol)
if days is not None and days <= 2:
return # avoid trading into an earnings print
divs = calendar.dividends(bar["t"], symbol)
if divs and (divs[-1].amount or 0) > 0.5: # amount can be None
...Dasselbe Handle steht in on_tick und on_timer zur Verfügung — aber Kalenderdaten gibt es nur im Backtest, bei Forward-Läufen ist calendar also immer None. Alle Abfragen sind Point-in-Time — dividends(bar["t"], symbol) liefert nur Dividenden, die zu diesem Balken öffentlich waren, nie zukünftige.
Wichtige Methoden:
| Methode | Liefert |
|---|---|
dividends(bar_time, symbol=None) | Zu diesem Balken öffentliche Dividenden (älteste zuerst) |
splits(bar_time, symbol=None) | Bis zu diesem Balken angekündigte Splits (älteste zuerst) |
next_earnings(bar_time, symbol=None) | {date, basis, estimated, …} oder None |
days_to_earnings(bar_time, symbol=None) | int oder None |
has(symbol=None) | Ob dieses Symbol überhaupt Kalenderdaten hat |
Listen sind älteste zuerst — [-1] ist das jüngste Ereignis. Das ist das Gegenteil der Fundamentaldaten, bei denen Index 0 die neueste Periode ist.
Das Runtime-Global qc_calendar enthält dasselbe Handle, falls Sie es lieber aus quantcraft.process.runtime statt aus dem Callback-Argument lesen.
Siehe Kalenderdaten für die Feldschemata der Ereignisse, Details zur Simulation von Kapitalmaßnahmen und Fehlerbehebung.
on_finish — abschließen
Läuft einmal, nach dem letzten Balken. Nutzen Sie es, um finale Ausgaben zu berechnen, Chart-Indikatoren aufzubauen oder eine Zusammenfassung auszugeben.
def on_finish(bars, symbol=None, bars_by_symbol=None):
closes = [b["close"] for b in bars]
bt.chart_indicators.add(...)Parameter:
bars— chronologische Liste von OHLC-Dictionaries für das Taktsymbol, ältester zuerst — das ausgeführte Fenster, dieselben Zeilen, die der Test-Results-Chart zeichnet. Jeder Eintrag hatt,open,high,low,close,volumeund optionalfundamentals. Dies ist die kanonische Serie über die gesamte Historie füron_finish.symbol— Ticker des Taktsymbols in Großbuchstaben.- Bei einem Screener-Lauf gibt es kein Taktsymbol:
barsist[]undsymbolist"", nutzen Sie alsobars_by_symbol. bars_by_symbol— bei Multi-Symbol-Läufen ein Dict, das jeden Ticker auf seine chronologische Balkenliste abbildet (an die Ausführungsuhr angeglichen). Bei Einzelsymbol-Läufen können Sie es ignorieren.
Gängige Verwendungen:
- Indikatorserien an
chart_indicators.add(...)übergeben, damit sie in den Ergebnis-Charts erscheinen. Bevorzugen Sie Werte, die Sie pro Symbol angesammelt haben, während der Lauf die Balken durchging; berechnen Sie ausbarsnur Indikatoren neu, die Sie nicht pro Balken mitgeführt haben — siehe Indikatorserien. - Eine finale Zusammenfassung im Ausgabebereich ausgeben oder protokollieren.
- Portfolio-Kennzahlen für die Ergebnis-Nutzlast aktualisieren.
Jedes
returninon_finishist stumm. Der Lauf gelingt trotzdem und Test Results wird weiterhin gerendert — ein frühes Return bedeutet nur, dass Ihre Serien oder Kennzahlen nie angehängt wurden, ohne irgendeinen Fehler. Geben Sie beim Debuggen an jeder Absicherung aufsys.stderraus; die Meldung erscheint im Ausgabebereich.
Grundlagen für mehrere Symbole
QuantCraft-Strategien können auf einem Symbol oder auf mehreren gleichzeitig laufen. Der Lebenszyklus bleibt derselbe — nur die Callback-Signaturen und die Daten, die sie erhalten, ändern sich leicht.
Mehrere Symbole konfigurieren
Wählen Sie im Tab Symbol & data von Run backtest einen oder mehrere Ticker in Großbuchstaben. Alle aufgeführten Symbole werden gemeinsam geladen und simuliert.
Das Taktsymbol
Wenn Sie mehrere Symbole ausführen, bestimmt eines davon den Takt — seine Balkenzeitstempel entscheiden, wann Callbacks laufen. Der Dialog Run backtest hat kein Bedienelement für ein Primärsymbol. Auflösungsreihenfolge:
- Ein
SYMBOL = "AAPL"auf oberster Ebene in Ihrer Strategiedatei, wenn dieser Ticker in der geladenen Liste ist. - Andernfalls das erste geladene Symbol.
Bei einer Screener-Symbolquelle gibt es überhaupt kein Taktsymbol — die Symbolmenge ändert sich im Verlauf des Laufs. rt.get_bar liest immer die Balken des Taktsymbols (und gibt bei einem Screener-Lauf None zurück); nutzen Sie für die Historie eines anderen Tickers daher dessen bar.close[i]-Offsets.
Das Taktsymbol ist auch dasjenige, das on_finish(bars, symbol=...) für seine Argumente bars und symbol verwendet.
Callbacks pro Symbol
Für jeden Ausführungsbalken geht die Engine die Symbole in Listenreihenfolge durch:
on_barläuft einmal pro geladenem Ticker zur Zeit dieses Balkens (vor allen Ticks dieses Balkens).- Nur Backtest: für jedes
tick_in_bar(0= Eröffnung,1= Hoch,2= Tief,3= Schluss) läufton_tickeinmal pro Ticker auf synthetischem OHLC (keine echten Tickdaten). - Nur Backtest: nach den vier Tick-Runden bewertet ein abschließendes
tick(close_map)des Kontos das Eigenkapital zum Schluss — kein fünfteson_tick. - Forward-Läufe:
on_tickläuft pro Broker-Kursupdate pro Symbol, sobald Notierungen von Ihrem verbundenen Broker eintreffen (nicht auf vier Aufrufe pro Balken beschränkt). An Intraday-Grenzen wird ofton_tickdannon_barausgelöst — nehmen Sie nicht die Backtest-Reihenfolge an. on_timer(nur bei Forward-Läufen mit konfiguriertemtimerIntervalMs— nicht im IDE-Backtest) folgt zwischen den Ticks demselben Muster pro Ticker.
Das heißt, ein Multi-Symbol-on_bar wird zum Beispiel dreimal pro Balken aufgerufen, wenn drei Symbole geladen sind. Nutzen Sie den Parameter symbol, um zu wissen, für welchen Ticker der Aufruf gilt:
def on_bar(bar_index, bar, fundamentals=None, calendar=None, symbol=None):
if symbol == "AAPL":
...
elif symbol == "MSFT":
...Die bar-Ansicht gehört immer zu dem Symbol, für das der Callback aufgerufen wurde. Dasselbe gilt für price in on_tick.
As-of-Ausrichtung. Pro Ausführungsbalkenzeit erhält jeder Ticker seinen jüngsten Balken zu oder vor dieser Zeit. Ein Ticker ohne Balken zu genau dieser Zeit (ein Handelsstopp, eine fehlende Minute, ein anderer Handelskalender) erhält seinen vorherigen Balken erneut. Wenn Sie Balken oder Signale pro Ticker zählen, überspringen Sie Wiederholungen, indem Sie sich das zuletzt gesehene bar["t"] für dieses Symbol merken:
_last_t = {}
def on_bar(bar_index, bar, fundamentals=None, calendar=None, symbol=None):
if _last_t.get(symbol) == bar["t"]:
return # same bar as last step for this ticker
_last_t[symbol] = bar["t"]
...on_finish bei mehreren Symbolen
on_finish läuft weiterhin einmal am Ende. Um mit der Historie aller Symbole zu arbeiten, nutzen Sie bars_by_symbol:
def on_finish(bars, symbol=None, bars_by_symbol=None):
if bars_by_symbol:
for ticker, ticker_bars in bars_by_symbol.items():
...Alles zusammen
Eine typische Einzelsymbol-Strategie:
- Setzt Eingabeparameter oder anderen Zustand in
on_init. - Liest die Kurshistorie (OHLCV) über
bar.close[0],bar.close[1], … inon_bar, um Handelsentscheidungen zu treffen, und ruftaccount.open_trade/account.close_tradeauf. - Nutzt optional
on_tick— im Backtest für synthetische Intrabar-Ausführungen (z. B.tick_in_bar == 3für den Schluss-Tick); bei Forward-Läufen, um zwischen Balkenschlüssen auf Live-Broker-Kurse zu reagieren. - Nutzt optional
on_timerbei Forward-Läufen, wenntimerIntervalMskonfiguriert ist (im IDE-Run-backtest nicht verfügbar). - Baut Chart-Indikatoren auf und finalisiert Ergebnisse in
on_finishmithilfe vonbars.
Eine Multi-Symbol-Strategie nutzt dieselben Callbacks, verzweigt darin lediglich nach dem Argument symbol und liest Daten pro Ticker über den erhaltenen bar — und in on_finish über bars_by_symbol.
Mehr Details? Siehe Kontomodell, Fundamentaldaten, Fama-French-Faktoren, OHLCV- und Balkendaten, Indikatorserien oder den integrierten Tab Documentation in der rechten Seitenleiste (Arbeitsbereich-Layout).

