10. Prozessdaten (proc.*) und dynamisches Backtesting (Designspezifikation)
In diesem Kapitel werden die Definition und Zugriffsmethoden von Prozessdaten in qteasy sowie die Auswahl von statischen Zweigen vs. dynamischen Zweigen während des Backtestings und die Konsistenzkonventionen zwischen ihnen erläutert, als Referenz bei der Implementierung und Erweiterung. Für Erläuterungen zur Nutzungsebene lesen Sie es zusammen mit „How Strategies Declare and Use Data“ und der API-Dokumentation.
10.1. 1. 背景与目标
1.1 Bedeutung der Prozessdaten
Einige Strategien müssen sich auf Daten verlassen, die sich mit dem Backtest oder dem Ausführungspfad des Live-Handels ändern, zum Beispiel:
Aktuelle/historische Positionen, verfügbares Bargeld;
Historische Füllmenge, Füllpreis, Transaktionskosten;
Marktwert, Gesamtvermögen etc. abgeleitet aus Positionen und Preisen.
Solche Daten können nicht auf einmal vorab generiert werden, bevor ein Backtest beginnt; Es kann nur während der Laufzeit vom Backtester (Backtesting) oder Trader (Live-Handel) gepflegt und der Strategie bei jedem Schritt der Signalgenerierung gemäß dem „aktuell sichtbaren Umfang“ bereitgestellt werden. Wir bezeichnen sie zusammenfassend als Prozessdaten.
1.2 Designziele
Einheitlicher Einstiegspunkt: Prozessdaten werden wie statische historische Daten über „Strategy.get_data()“ abgerufen, wodurch die Lernkurve verkürzt wird.
Kein Look-Ahead: Beim Generieren des Signals für Schritt k kann die Strategie die Ausführungsergebnisse von Schritt k nicht sehen; Es kann nur der Verlauf abgeschlossener Schritte verwendet werden.
Backtest/Live-Konsistenz: Derselbe Satz von Strategien und das gleiche Aufrufmuster „get_data(‚proc.xxx‘)“ können sowohl beim Backtesting als auch beim Live-Handel verwendet werden; Wenn Prozessdaten benötigt werden, folgen sie dem dynamischen Ausführungspfad, andernfalls können sie in Bezug auf die Ergebnisse mit dem ursprünglichen statischen Pfad konsistent bleiben.
10.2. 2. 过程数据的统一定义(proc.*)
2.1 Benennungs- und Belichtungsmethode
Alle Prozessdaten werden der Strategie in der Form
proc.<Feldname>zur Verfügung gestellt, wie zum Beispiel „proc.own_cash“ und „proc.trade_records“.Prozessdaten müssen nicht über „data_types“ im „init“ der Strategie deklariert werden. Es wird zur Laufzeit von Backtester / Trader injiziert, und die Strategie muss bei Bedarf nur „get_data(‚proc.xxx‘, …)“ in „realize()“ aufrufen.
2.2 Integrierte Felder implementiert
Zu den in der aktuellen Version implementierten und für die Verwendung in Strategien verfügbaren Prozessdatenfeldern gehören:
Kategorie |
Feldname |
Bedeutung |
|---|---|---|
Kontoskalar |
|
Gesamtguthaben auf dem Konto zu Beginn des aktuellen Schritts |
|
Für die Auftragserteilung steht zu Beginn des aktuellen Schritts Bargeld zur Verfügung |
|
|
Gesamtmarktwert des Vermögens zu Beginn des aktuellen Schritts (Positionsbewertung + Bargeld) |
|
Positionsvektor |
|
Gesamtpositionsmenge jedes Instruments zu Beginn des aktuellen Schritts |
|
Verkaufbare Menge für jedes Instrument zu Beginn des aktuellen Schritts |
|
|
Positionsmarktwert für jedes Instrument zu Beginn des aktuellen Schritts (berechnet aus dem internen Preis und den Positionen) |
|
Ausführungsergebnisse |
|
Tatsächlich ausgeführte Menge für jedes Instrument bei jedem Schritt (positiv für Käufe, negativ für Verkäufe) |
|
Transaktionskosten für jedes Instrument bei jedem Schritt |
|
|
Ausführungspreis für jedes Instrument bei jedem Schritt |
Informationen zur Zeitsemantik und den Sichtbarkeitsbeschränkungen der oben genannten Felder beim Backtesting und Live-Handel finden Sie in Abschnitt 4.
2.3 Zukünftig erweiterbare Felder (optional)
Zu den Feldern, die per Design weiter erweitert werden können, gehören: „proc.realized_pnl“, „proc.unrealized_pnl“, „proc.last_trade_price“, „proc.last_trade_volume“ usw. Einzelheiten finden Sie in der Implementierung und Dokumentation.
10.3. 3. 访问接口:Strategy.get_data() 与 proc.*
3.1 Statische Daten (kein Prozesspräfix)
Einzelquelle:
self.get_data('close_E_d'); mehrere Quellen:self.get_data('close_E_d', 'high_E_d').Statische Daten unterstützen die Parameter „Lag“ und „Window“ nicht. Wenn angegeben, wird ein englischer „ValueError“ ausgelöst.
3.2 Prozessdaten (Proc.-Präfix)
Anrufbeispiele:
self.get_data('proc.own_cash'): die Cash-Serie bis zum aktuellen Schritt;self.get_data('proc.own_cash', lag=0): der Barwert beim letzten Schritt;self.get_data('proc.own_cash', lag='1d'): der Schritt, der dem Rückblick um einen Tag entspricht;self.get_data('proc.own_cash', window='5d'): ein Fensterausschnitt über die letzten 5 Tage.
Einschränkungen:
Ein einzelner Aufruf erlaubt nur ein
proc.*-Feld; Wenn mehrere Felder verwendet werden oder diese mit statischen Daten im selben Aufruf gemischt werden, wird ein englischer „ValueError“ ausgelöst.„lag“ und „window“ können nicht gleichzeitig angegeben werden; „lag“ kann eine Ganzzahl (Schritte) oder eine Zeichenfolge (z. B. „1d“, „8h“) sein, und „window“ ist eine Zeichenfolge (z. B. „5d“, „8h“).
Rückgabewert: immer
np.ndarray; Form und Datentyp unterliegen der API-Dokumentation.
10.4. 4. 回测分支选择与过程数据协作
4.1 Statischer Zweig und dynamischer Zweig
Statischer Zweig (
_backtest_static_operator): Rufen Sie „run_strategies“ einmal für alle Zeitschritte auf, um alle Signale zu generieren, und schließen Sie dann den Backtest mit vektorisierten Numba-Funktionen wie „backtest_batch_steps“ ab. Geeignet für Strategien, die nicht von Prozessdaten abhängen.Dynamischer Zweig (
_backtest_dynamic_operator): Schleife über Zeitschritte; Generieren Sie bei jedem Schritt Signale → Analysieren und simulieren Sie Füllungen → Aktualisieren Sie Positionen und Bargeld und fahren Sie dann mit dem nächsten Schritt fort. Prozessdaten werden vom Backtester verwaltet und vor jedem Schritt in den Operator eingespeist, sodass Strategien über „get_data(‚proc.xxx‘)“ darauf zugreifen können.
Vor der Ausführung entscheidet der Backtester über Operator.check_dynamic_data(), welchen Zweig er nehmen möchte.
4.2 Entscheidungslogik von check_dynamic_data() (aktuelle Implementierung)
Wenn eine der folgenden Aussagen zutrifft, geben Sie „True“ zurück und nehmen Sie den dynamischen Zweig:
op_type == ‚stepwise‘: Der Operator ist explizit für den schrittweisen Modus konfiguriert.
Verwendung von proc. im Strategie-Quellcode*: „_strategies_use_proc_data()“ prüft, ob der Quellcode von „realize()“ jeder Strategie „proc.“ oder „proc.“ enthält. Wenn ja, wird davon ausgegangen, dass es von den Prozessdaten abhängt.
Daher gilt: Solange „get_data(‚proc.xxx‘)“ in real() aufgerufen wird, erfolgt automatisch der dynamische Zweig ohne jegliche Deklaration. Der Zugriff auf Prozessdaten erfolgt nur über „proc.“; die Deklaration über DataType wird nicht mehr unterstützt (ältere „op_“-Typen wurden entfernt).
4.3 Keine Vorausschaugarantie
Wenn die Strategie in Schritt k ein Signal generiert:
Konto-/Positionsdaten (z. B. „own_cashes“, „own_amounts“) sind höchstens bis zum Index „[0…k]“ sichtbar (d. h. der Stand zu Beginn des aktuellen Schritts);
Handelsbezogene Daten (z. B. „trade_records“, „trade_cost“) sind maximal bis zu „[0…k-1]“ sichtbar, mit Ausnahme von Trades, die in diesem Schritt noch nicht stattgefunden haben.
Bei der Implementierung schneiden „_current_signal_index“ von Operator und „_get_process_data_single()“ von Strategy die oben genannten Bereiche auf. Der Backtester aktualisiert den Index, bevor bei jedem Schritt Signale generiert werden, sodass kein Look-Ahead erfolgt.
4.4 Die Injektionsbeziehung zwischen Backtester und Operator
Backtester (dynamischer Zweig): Fügen Sie am Einstiegspunkt „_backtest_dynamic_operator“ „own_cashes“, „available_cashes“, „own_amounts_array“, „available_amounts_array“, „trade_records_array“, „trade_cost_array“, „trade_price_array“, „trade_price_data“ ein. usw. in Operator als
_process_data_sourcesund setzen Sie_process_time_indexauf eine Zeitleiste, die anop_signal_indexausgerichtet ist.Operator: Berechnen und aktualisieren Sie in „run_strategy(step_index)“ vor jedem Aufruf von „stg.generate()“ den „_current_signal_index“** basierend auf „group_timing_table“ und „group_merge_type“, damit Strategy die „derzeit sichtbaren“ Prozessdaten aufteilt.
4.5 Prozessdaten im Live-Handel (Trader)
Wenn „operator.check_dynamic_data()“ True ist, führt Trader in „_run_strategy()“ Folgendes aus:
Fassen Sie das aktuelle Kontoguthaben, die Positionen, die verfügbaren Mengen, die aktuellen Preise usw. in einstufigen „_process_data_sources“ und „_process_time_index“ zusammen (ein einzelner Live-Handelslauf wird als ein Schritt behandelt);
In diesem Schritt kann die Strategie „get_data(‚proc.own_cash‘)“ usw. aufrufen, um die aktuelle Konto-/Positionsansicht zu erhalten; Die Handelshistorie ist in diesem Schritt leer, was mit der Semantik „In diesem Schritt sind noch keine Geschäfte stattgefunden“ übereinstimmt.
10.5. 5. 动态/静态路径一致性约定
Wenn eine Strategie Prozessdaten nicht verwendet: Sie sollte den statischen Zweig verwenden. Wenn aus anderen Gründen der dynamische Zweig verwendet wird, sollten die Backtest-Ergebnisse numerisch exakt identisch mit dem statischen Zweig sein (unter derselben Konfiguration und denselben Daten).
Wenn eine Strategie Prozessdaten verwendet: Sie muss den dynamischen Zweig durchlaufen; Andernfalls wird ein RuntimeError ausgelöst, wenn „_process_data_sources“ nicht injiziert wird.
Testkonvention: Verwenden Sie „StaticSignalStg“ (rein statisch) und „ProcAwareButStaticLogicStg“ (ruft proc auf, verwendet es aber nicht für Signale), um unter derselben Konfiguration einen Backtest durchzuführen und numerische Konsistenz für „own_cashes“, „own_amounts_array“, „trade_records_array“ usw. sicherzustellen; siehe Gruppe B in „tests/test_process_data_api.py“.
10.6. 6. 测试与文档索引
Spezielle Tests:
tests/test_process_data_api.pyGruppe A: das Verhalten von „check_dynamic_data()“ unter rein statischen Strategien / Strategien mit proc.*;
Gruppe B: Backtest-Array-Konsistenz zwischen statischen Strategien und „Proc aufrufen, aber logisch äquivalenten“ Strategien;
Gruppe C: „get_data“-Fehlerverhalten für statische Multi-Source-, Ablehnungs-Lag/Window-, Proc-Einzelfeld- und gemischte Aufrufe;
Gruppe D: keine Look-Ahead-Validierung für
proc.trade_records;Gruppe E: Korrektheit des realen dynamischen Strategiepfads basierend auf Prozessdaten.
Projektspeicher:
.cursor/rules/process-data-and-dynamic-backtest.mdc(Implementierungs- und Konventionszusammenfassung).Überblick über Strategiedaten: Wie Strategien Daten deklarieren und verwenden; Backtest-Einstiegspunkte und -Modi: Backtesting, Live-Handel und Optimierung.