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

proc.own_cash

Gesamtguthaben auf dem Konto zu Beginn des aktuellen Schritts

proc.available_cash

Für die Auftragserteilung steht zu Beginn des aktuellen Schritts Bargeld zur Verfügung

proc.total_value

Gesamtmarktwert des Vermögens zu Beginn des aktuellen Schritts (Positionsbewertung + Bargeld)

Positionsvektor

proc.own_amounts

Gesamtpositionsmenge jedes Instruments zu Beginn des aktuellen Schritts

proc.available_amounts

Verkaufbare Menge für jedes Instrument zu Beginn des aktuellen Schritts

proc.position_value

Positionsmarktwert für jedes Instrument zu Beginn des aktuellen Schritts (berechnet aus dem internen Preis und den Positionen)

Ausführungsergebnisse

proc.trade_records

Tatsächlich ausgeführte Menge für jedes Instrument bei jedem Schritt (positiv für Käufe, negativ für Verkäufe)

proc.trade_cost

Transaktionskosten für jedes Instrument bei jedem Schritt

proc.trade_price

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:

  1. op_type == ‚stepwise‘: Der Operator ist explizit für den schrittweisen Modus konfiguriert.

  2. 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_sources und setzen Sie _process_time_index auf eine Zeitleiste, die an op_signal_index ausgerichtet 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.py

    • Gruppe 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.