Statistische Arbitrage durch kointegrierte Aktien (Teil 7): Punktesystem 2
Einführung
Im letzten Artikel haben wir ein vorgeschlagenes Bewertungssystem für Aktienkörbe vorgestellt. Wir beließen es bei den Ergebnissen eines Backtests, der relativ gute Zahlen zeigte, aber auch einige Schwachstellen in unserem Korb aufdeckte.
- Eine im Durchschnitt sehr lange Haltedauer der Positionen
- Zumindest eine Stelle dauerte mehr als sechzehn Wochen! Dies war die maximale Haltezeit der Position.
Das Hauptziel dieses Backtests bestand nicht darin, die Rentabilität der Strategie oder des Korbs zu bewerten, sondern zu testen, wie unser Bewertungssystem abschneiden würde, wenn zwei der vorgeschlagenen Ausschlusskriterien fehlen würden: die Stabilität der Portfoliogewichte und die Zeit bis zur Mean Reversion oder Halbwertszeit. Das heißt, als wir den letzten Artikel verließen, verwendete unser Bewertungssystem nur zwei der vier vorgeschlagenen Ausschlusskriterien. Wir haben einfach mehrere Körbe mit den liquidesten Wertpapieren aus der Halbleiterbranche zusammengestellt und denjenigen mit dem höchsten Kointegrationsgrad ausgewählt. Wir haben nicht untersucht, ob die durch den Johansen-Test ermittelten Portfoliogewichte stabil genug sind, und wir haben nicht untersucht, wie lange es dauert, bis sich der Mittelwert umkehrt.
- Liquidität
- Stärke der Kointegrationsvektoren
- Stabilität des Kointegrationsvektors (Portfoliogewichte)
- Zeit bis zur Umkehrung des Mittelwerts (Halbzeit)
In diesem Artikel werden wir diese Lücke schließen, indem wir die beiden verbleibenden Punkte behandeln: Stabilität der Portfoliogewichte und Zeit bis zur Mean Reversion. Wir werden von diesem Punkt aus fortfahren und dieselben ausgewählten Körbe verwenden, um zu prüfen, wie das System von der Einbeziehung dieser beiden Rankingfaktoren profitieren kann.
Für die Leser, die diese Serie vielleicht nicht verfolgen, entwickeln wir einen statistischen Arbitrage-Rahmen für den durchschnittlichen Einzelhändler mit einem Notebook für normale Konsumenten, begrenzten Mitteln und einer normalen Internet-Bandbreite. Das Projekt begann als informelles Gespräch mit Freunden und führte zu einer Herausforderung, die mein Partner und ich als das annahmen, was es ist: eine Gelegenheit zum Lernen und zur Verbesserung unserer Fähigkeiten als Händler. Anlass für das Gespräch war das Ableben des Mathematikers und Hedgefondsmanagers Jim Simmons, der mit seinem legendären Medallion Fund 30 Jahre lang ununterbrochen Gewinne erzielte, mit einer „durchschnittlichen jährlichen Bruttorendite von 66,1 % bzw. einer durchschnittlichen jährlichen Nettorendite von 39,1 % zwischen 1988 und 2018“, wobei er statistische Arbitrage und – in seinen eigenen Worten – „eine Art maschinelles Lernen“ einsetzte.
Bisher haben wir gesehen, wie man die gängigsten Korrelations-, Kointegrations- und Stationaritätstests für den Paarhandel und für Portfoliogruppen (Körbe) anwendet und interpretiert. Wir haben mehrere Python-Skripte für die Analyse implementiert, zwei Beispiel-Expert Advisors, einen für den Paarhandel und den anderen für Körbe, und einige Backtests mit ihnen durchgeführt. Wir haben auch das erforderliche Datenbankschema zur Unterstützung unserer Experimente eingerichtet und weiterentwickelt.
Wenn der statistische Arbitrage-Ansatz für Sie von Interesse ist, lesen Sie bitte die vorherigen Artikel dieser Serie und nehmen Sie sich etwas Zeit, um damit zu spielen. Sie werden sehen, dass das Gespräch sehr händlerfreundlich ist, da wir „auf den Schultern der Giganten“ stehen. Professionelle Mathematiker und Statistiker haben die harte Arbeit bereits für uns erledigt, und wir profitieren davon, indem wir uns auf die Handelsseite konzentrieren – statt auf die harte Mathematik – und in großem Umfang auf fertige Open-Source-Bibliotheken zurückgreifen. Nun, da wir uns dem Ende des grundlegenden Teils dieser Reihe nähern, ist es an der Zeit, die Grundlagen zu wiederholen.
Lassen Sie uns also unser Punktesystem fertigstellen. Wir beginnen damit, unsere coint_rank-Tabelle zu ändern, um die fehlenden Daten, die beiden fehlenden Ranking-Faktoren, aufzunehmen.

Abb. 1. Diagramm mit den aktualisierten Feldern und Datentypen der Tabelle coint_rank
Zeit bis zur mittleren Umkehrung
Der Backtest, den wir durchgeführt haben, um unser (halb implementiertes) Scoring-System zu testen, brachte einige seltsame Zahlen in Bezug auf die durchschnittliche Haltedauer der Positionen: fast viereinhalb Wochen im Durchschnitt! Und mindestens eine Stelle blieb mehr als sechzehn Wochen lang offen. Dies ist nicht das erwartete Risiko für unsere Strategie.

Abb. 2. Screenshot des Backtest-Berichts des vorherigen Artikels, der die Positionshaltezeiten zeigt
Unter der Annahme, dass mit dem EA und der Backtest-Umgebung alles in Ordnung ist, und da unsere Strategie auf der mittleren Umkehrung basiert, ist der offensichtlichste Faktor zur Erklärung dieser seltsamen Zahlen die Zeit bis zur mittleren Umkehrung. Unser Spread könnte viel länger dauern als erwartet, um zum Mittelwert zurückzukehren, was der Auslöser dafür ist, alle abgesicherten Positionen zu schließen.
Erinnern Sie sich, wie wir den Korb für den Handel ausgewählt haben? Wir nahmen den „am stärksten“ kointegrierten Korb, d. h. den Korb mit der höchsten Kointegrationsstärke, einen Vier-Symbol-Korb, der aus den zehn liquidesten Symbolen aus der Halbleiterbranche gebildet wurde. Mit anderen Worten: Wir haben nach Liquidität und Kointegrationsstärke gepunktet... und sonst nichts. Sobald jedoch ein kointegriertes Portfolio identifiziert ist, besteht der nächste logische Schritt darin, zu bewerten, wie schnell die Streuung der Abweichungen vom Mittelwert zu ihm zurückkehrt. Dieser Metrik wurde der schicke Name „Halbwertszeit der mittleren Umkehrung“ gegeben. Die Halbwertszeit der Mittelwertumkehr quantifiziert die erwartete Zeit, die der Spread (das Residuum aus der kointegrierenden Beziehung) benötigt, um nach der Abweichung halbwegs zu seinem Mittelwert zurückzukehren.
Bei stat arb, wo die Gewinne aus Wetten auf vorübergehende Fehlbewertungen stammen, ist die Quantifizierung der Halbwertszeit von entscheidender Bedeutung für die Schätzung der Haltedauer von Positionen und folglich für die Beurteilung der Durchführbarkeit der Gesamtstrategie. Später, wenn es um Geld-/Risikomanagement geht, wird die Halbwertszeit der mittleren Umkehrung nützlich sein, um das Volumen unserer Positionen und die Größe unserer Handelsgeschäfte zu bestimmen, da kürzere Halbwertszeiten aufgrund der geringeren Unsicherheit größere Einsätze ermöglichen.
Eine kurze Halbwertszeit (in unserem Fall Minuten bis Stunden) deutet auf eine schnelle Umkehrung hin und ermöglicht eine schnelle Auflösung der Position. Wir verringern unser Risiko, und unser Kapital wird frei, um in neue Chancen investiert zu werden. Umgekehrt bindet eine lange Halbwertszeit (Tage bis Wochen) unser Kapital, erhöht die Haltekosten (z. B. Swaps für Devisen und Leihgebühren für Leerverkäufe von Aktien) und steigert unser Risiko bei Nachrichtenereignissen oder Liquiditätsengpässen. Ohne die Quantifizierung der Halbwertszeit der Mittelwertumkehr besteht die Gefahr, dass wir nicht nur länger als erwartet im Markt bleiben, sondern auch vorzeitig aussteigen.
Wenn Sie nur eine einzige Information aus dieser Einführung behalten möchten, dann schreiben Sie Folgendes in Stein: DIE RÜCKKEHR ZUM MITTELWERTS IST NICHT GARANTIERT.
Ihre Position kann für immer offen bleiben. Längere Abweichungen können Stop-Losses oder Margin-Calls auslösen. Halbwertszeiten, die Ihre Risikotoleranz überschreiten (z. B. >30 Tage bei Hochfrequenz-Setups), sollten herausgefiltert werden.
Berechnung der Halbwertszeit
Die Mathematik hinter der Berechnung der Halbwertszeit bis zur Rückkehr zum Mittelwert ist für Nichteingeweihte in der weißen Magie der Mathematik haarig. Die Halbwertszeit ergibt sich aus der Modellierung der Ausbreitung als Ornstein-Uhlenbeck-Prozess, einer stochastischen Differentialgleichung, die das Verhalten einer Rückkehr zum Mittelwert beschreibt. In diskreter Zeit, was in unserem Fall der Fall ist, nähert sich dies einem AR(1)-Modell, einem autoregressiven Modell erster Ordnung.
„Der Ornstein-Uhlenbeck-Prozess wird im Vasicek-Modell des Zinssatzes verwendet. Der Ornstein-Uhlenbeck-Prozess ist einer von mehreren Ansätzen zur stochastischen Modellierung von Zinssätzen, Wechselkursen und Rohstoffpreisen (mit Modifikationen). (...) Eine Anwendung des Prozesses ist eine Handelsstrategie, die als Paarhandel bekannt ist.“ (übersetzt von der Wikipedia-Seite über Ornstein-Uhlenbeck Prozess in der Finanzmathematik)
Ich zitiere den obigen Text als Referenz für den mathematisch interessierten Leser. Glücklicherweise müssen wir, wie oben erwähnt, die Mathematik hinter dieser Berechnung nicht beherrschen, da es eine Formel gibt, und wir können uns bei der AR(1)-Modellanpassung auf unsere bewährte Python-Bibliothek verlassen. Alles, was wir wissen müssen, sind die Berechnungsschritte. (Ihr KI-Assistent kann hier eine unschätzbare Hilfe sein, da es sich um eine sehr bekannte Berechnung handelt und statsmodels eine sehr bekannte Statistikbibliothek ist).
In der beigefügten Python-Datei wird dies in der Methode compute_half_life() durchgeführt.
if len(spread) < 3: return np.nan lag = spread.shift(1).dropna() delta = (spread - lag).dropna() if len(delta) < 2: return np.nan res = sm.OLS(delta, sm.add_constant(lag, has_constant='add')).fit() # beta = res.params[1] beta = res.params.iloc[-1] if beta >= 0: return np.inf else: return -np.log(2) / beta
Die Streuung muss auf Stationarität geprüft werden, bevor sie von hier weitergehen können, was wir bereits zuvor mit einem ADF-Test in unserer Pipeline getan haben. Die Daten, die wir hier weitergeben, sind bereits stationär. Außerdem müssen wir dies mit Daten durchführen, die außerhalb der Stichprobe liegen, um eine Überanpassung zu vermeiden und die Vorhersagekraft zu bestätigen, was wir als Nächstes tun werden. Als anfängliche Schwelle suchen wir für unsere Strategie nach Halbwertszeiten zwischen vier und zwanzig Stunden (1-5 Balken) (denken Sie daran, dass wir im H4-Zeitrahmen Swing Trading betreiben). Sie sollten diesen Bereich für Ihre eigenen Bedürfnisse anpassen. Sie könnten zum Beispiel nach einer Spanne von 5-20 Tagen für einen täglichen Zeitrahmen suchen. Aber auch hier gilt: ändern, testen, wieder ändern, testen und schließlich optimieren. Wenn die Halbwertszeit unendlich ist (nicht umkehrbar), wird das Paar verworfen.
Interpretation
Die Halbwertszeit wird durch die Anzahl der Perioden ausgedrückt, die dem Zeitrahmen entsprechen. In unserer Beispielstrategie handelt es sich um einen vierstündigen Zeitraum, da wir für den H4-Zeitrahmen testen. Eine Halbwertszeit von 10 bedeutet also, dass es etwa 10 Balken oder 40 Stunden dauert, bis die Streuung ihre Abweichung vom Mittelwert um 50 % verringert hat.
In Abbildung 3 sehen Sie zum Beispiel die Tabelle coint_rank ohne Filter (mit Ausnahme von half_life IS NOT NULL). Die ersten beiden Zeilen zeigen die Datensätze für denselben Korb, denselben Zeitrahmen und denselben Rückblick, die zu zwei verschiedenen Zeitpunkten (Zeitstempeln) aufgenommen wurden. Die gemessene Halbwertszeit beträgt ~6,98, was bedeutet, dass dieser Korb, wenn er mit der Johansen-Methode im H4-Zeitrahmen mit einem Rückblick von 180 Tagen auf Kointegration getestet wird, fast 7 Balken zu je 4 Stunden oder 48 Stunden braucht, bis seine abweichende Streuung zum Mittelwert zurückkehrt. Es ist klar, dass die Spanne in den anderen 48 Stunden vom Mittelwert bis zum Höchstwert reicht.

Abb. 3. Die Metaeditor-Datenbankschnittstelle zeigt die Tabelle coint_rank mit dem Feld NOT NULL half_life
In Abbildung 4 sehen Sie, dass wir in derselben Tabelle ein Beispiel für eine sehr lange Halbwertszeit haben. In diesem Fall ist die maximale Halbwertszeit unter den von uns aufgezeichneten Tests. Der betreffende Korb benötigt bei einem Rückblick von 120 Tagen fast 934 Balken zu je 4 Stunden oder fast 155 Tage, um zum Mittelwert zurückzukehren. Natürlich werden Sie diesen Korb nicht handeln wollen, da er ein längeres Halten der Positionen erfordert, was unser Marktrisiko erhöht.

Abb. 4. Metaeditor-Datenbankschnittstelle zeigt die Tabelle coint_rank mit dem Feld MAX half_life und nicht unendlich
Beachten Sie die Verwendung der NOT IN-Klausel zum Ausschluss von „Infinity“-Werten in SQLite. Ohne diese Klausel würde die zurückgegebene MAX-Halbwertszeit „Unendlich“ lauten. In diesem Fall können wir davon ausgehen, dass der Spread niemals zum Mittelwert zurückkehren kann, d.h. unsere Positionen können unbegrenzt offen bleiben, es sei denn, wir haben andere Kriterien für das Schließen/Stop-Loss, wie z.B. eine Schwelle für die Haltedauer der Position (Schließen nach Zeit).

Abb. 5. Metaeditor-Datenbankschnittstelle mit Anzeige der Tabelle coint_rank mit MAX half_life ohne Unendlichkeitsfilter
Unendliche Halbwertszeit bedeutet, dass es keine mittlere Umkehrung gibt (die Streuung ist nicht-stationär oder zufallsbedingt). Dies wird in unserem Code als „np.inf“ gespeichert und deutet darauf hin, dass der Korb für den Mean-Reversion-Handel im H4-Zeitrahmen mit einem Lookback von 180 Tagen nicht geeignet ist.
def compute_half_life(self, spread: pd.Series) -> float: """ Compute the half-life of mean reversion for a given spread series. Parameters ---------- spread : pd.Series The stationary spread (residual) series from the cointegration vector. Returns ------- float The half-life in periods (e.g., bars). Returns np.inf if no mean reversion (beta >= 0), np.nan if insufficient data. """ if len(spread) < 3: return np.nan lag = spread.shift(1).dropna() delta = (spread - lag).dropna() if len(delta) < 2: return np.nan res = sm.OLS(delta, sm.add_constant(lag, has_constant='add')).fit() # beta = res.params[1] beta = res.params.iloc[-1] if beta >= 0: return np.inf else: return -np.log(2) / beta
Im Gegensatz dazu haben wir in denselben Beispieldaten eine sehr kurze Halbwertszeit, die minimale Halbwertszeit der Tabelle beträgt fast eineinhalb H4-Balken oder etwa 6 Stunden bis zur mittleren Umkehr.

Abb. 6. Die Metaeditor-Datenbankschnittstelle zeigt die Tabelle coint_rank mit dem Feld MIN half_life
Dies ist ideal für statistische Arbitrage, da es darauf hindeutet, dass Handelsgelegenheiten (Einstieg in einen Handel, wenn der Spread abweicht, und Ausstieg, wenn er sich zurückbildet) innerhalb eines kurzen Zeitrahmens häufiger auftreten können.

Abb. 7. Erfassen_metaeditor_db_coint_rank_half_life_NULL
In Abbildung 7 schließlich sehen Sie, dass wir Tausende von Halbwertszeiten mit NaN (Not a Number) haben. Sie werden von SQLite als NULL gespeichert und treten bei unzureichenden Daten oder numerischen Problemen wie konstanter Streuung auf und zeigen an, dass die Berechnung fehlgeschlagen ist.
Zusammenfassend lässt sich sagen, dass eine kürzere Halbwertszeit für stat arb-Strategien in der Regel besser ist, da sie schnellere Abschlüsse mit geringerer Kapitalbindung und einem geringeren Risiko des Zusammenbruchs der Kointegration bedeutet, aber wir müssen immer berücksichtigen, dass eine kurze oder lange Halbwertszeit relativ ist, da sie mit unseren strategischen Kriterien für die Bewertung übereinstimmen muss. In unserem Beispiel wird es kurz oder lang in Bezug auf den H4-Zeitrahmen sein, der seit Beginn der Bewertung unser strategisches Kriterium ist.
Beachten Sie das:
- Eine endliche Halbwertszeit setzt voraus, dass die Ausbreitung stationär ist. Prüfen Sie `stability_stat` (ADF-Statistik), um zu bestätigen, dass die Spanne außerhalb der Stichprobe stationär bleibt. Ein weniger negatives `stability_stat` deutet darauf hin, dass der Kointegrationsvektor in der Zukunft möglicherweise nicht bestehen wird.
- Probleme wie konstante oder nahezu konstante Preisreihen können zu `NaN`- oder `inf`-Werten führen. Dies deutet auf schlechte Daten oder geringe Liquidität hin.
- Die Halbwertszeit ist an den Zeitrahmen gebunden. In einem anderen Zeitrahmen könnte derselbe Korb eine andere Halbwertszeit aufweisen. Vergleichen Sie die Halbwertszeiten innerhalb desselben Zeitrahmens.
Das diesem Artikel beigefügte Skript coint_ranker_auto.py hilft beim Verständnis des Halbwertszeitverhaltens, indem es die Streuung für die Top-Körbe aufzeichnet, sodass wir visuell bestätigen können, dass die Umkehrgeschwindigkeit der berechneten Halbwertszeit entspricht.

Abb. 8. Darstellung des kointegrierten Spreads für einen Korb von vier Nasdaq-Aktien mit der mittleren Halbwertszeit der Rückkehr zum Mittelwert
In unserem Punktesystem verwenden wir neben rank_score und stability_stat (siehe unten) auch die Halbwertszeit, um die Körbe zu bewerten. Wir werden Körbe mit hohem rank_score (starke Kointegration), kurzer Halbwertszeit (schnelle Umkehrung) und negativer stability_stat (stabiler Vektor) bevorzugen.
Stabilität der Portfoliogewichte
Die Portfoliogewichte sind der Eckpfeiler der statistischen Arbitrage durch Kointegration. Sie bestimmen nicht nur den Umfang jeder gleichzeitig eröffneten abgesicherten Position, sondern auch deren Richtung. Wir haben bei der Einführung des Johansen-Kointegrationstests viel über sie gesprochen und werden uns hier nicht wiederholen, um Ihre Zeit zu sparen. Es genügt, sich daran zu erinnern, dass sie der Ursprung der Berechnung der mittleren Umkehrspanne sind.
Sie stammen aus den Eigenvektoren, die sich aus dem Johansen-Test ergeben, der multivariaten statistischen Methode, die wir zur Ermittlung der Kointegration zwischen mehreren Aktienkursreihen (nicht-stationäre Zeitreihen) verwendet haben. Der Test schätzt die Anzahl der kointegrierenden Vektoren und liefert die zugehörigen Eigenvektoren, die die linearen Kombinationen von Vermögenswerten darstellen, die ein stationäres, handelbares Portfolio bilden.
In unserem letzten Backtest haben wir beispielsweise für einen Korb, der sich aus „NVDA“, „INTC“, „AVGO“ und „ASX“ zusammensetzt, {1.0, -1.9598590335874817, -0.36649674991957104, 21.608207065113874} als Portfoliogewichtung verwendet.
| NVDA | INTC | AVGO | ASX |
|---|---|---|---|
| 1.0 | -1.9598590335874817 | -0.36649674991957104 | 21.608207065113874 |
Tabelle 1. Ein Aktienkorb mit entsprechenden Portfoliogewichten (Kointegrationsvektoren)
Das bedeutet, dass wir für jede Einheit einer Kaufposition in NVDA Folgendes eröffnen würden:
- Eine Verkaufsposition von ~1,96 Einheiten von INTC
- Eine Verkaufsposition von ~0,37 Einheiten von AVGO
- Eine weitere Kaufposition von ~21,6 Einheiten des ASX
Dieses gewichtete Absicherungsschema gewährleistet im Idealfall die erwartete Stat arb-Marktneutralität. (Im Idealfall, denn Marktneutralität ist ein Ziel, das nie ganz erreicht wird. Es wird immer Restrisiken geben, die wir hier nicht berücksichtigen können.) Die Annahme, dass diese Gewichte im Laufe der Zeit fest sind, kann jedoch auf dynamischen Finanzmärkten riskant sein. Bei der Auswertung müssen wir die Stabilität dieser Johansen-Eigenvektoren aus mindestens drei Gründen überprüfen:
Erstens, weil die Kointegration ein langfristiges Gleichgewicht impliziert, sich die Wirtschaftssysteme jedoch aufgrund von Faktoren wie politischen Veränderungen, Marktzusammenbrüchen oder Sektorumstellungen verschieben. Wenn die Eigenvektoren im Laufe der Zeit über rollierende Schätzfenster hinweg erheblich schwanken, bedeutet dies, dass die zugrunde liegende Beziehung möglicherweise nicht stabil ist. Instabile Gewichte könnten zu einem Zusammenbruch der Rückkehr zum Mittelwert führen. Sie können eine profitable Arbitragestrategie in eine Strategie verwandeln, die für anhaltende Abweichungen und Verluste anfällig ist.
Zweitens, weil stabile Gewichte sicherstellen, dass das Portfolio gegen allgemeine Faktoren, wie z. B. Markt-Beta, abgesichert bleibt. Instabilität könnte unsere Anfälligkeit für unbeabsichtigte Risiken wie Volatilitätsspitzen oder Kointegrationsstörungen verstärken. So haben sich beispielsweise während der Finanzkrise 2008 viele kointegrierte Paare bei Finanztiteln entkoppelt, wodurch frühere Eigenvektoren obsolet wurden. Die regelmäßige Überprüfung der Stabilität trägt dazu bei, ein übermäßiges Vertrauen in historische Schätzungen zu vermeiden, und führt zu rechtzeitigen Strategieanpassungen, wie z. B. der Neuschätzung von Gewichten oder dem Ausstieg aus Positionen. Dies ist der Hauptgrund, warum wir die „Datenbank als einzige Wahrheitsquelle“ für Modellaktualisierungen in Echtzeit eingeführt haben.
Und drittens, weil instabile Portfoliogewichte mit ziemlicher Sicherheit zu höheren Drawdowns und niedrigeren Sharpe-Ratios führen werden. Wenn eine Instabilität festgestellt wird, müssen wir den Korb in unserem Punktesystem aggressiv zurückstufen.
Was ist `stability_stat`?
Wie der Name schon sagt, speichert das Feld stability_stat in der Tabelle coint_rank die Stabilitätsstatistik der Portfoliogewichte. Sein Wert kann mit zwei verschiedenen Methoden berechnet werden:
Rollender Fenster-Eigenvektorvergleich – Hierbei handelt es sich um eine direkte Methode, bei der wir den Kointegrationsvektor über aufeinanderfolgende Rolling-Windows hinweg vergleichen. Wir erwarten, dass der Kosinusabstand innerhalb eines bestimmten Schwellenwerts liegt, damit die Portfoliogewichte als stabil gelten.
Durch die ADF-Validierung durch In-Sample/Out-of-Sample – Dies ist ein vierstufiger Prozess. Wir beginnen mit der Aufteilung der Preisdaten in zwei Teile für In-Sample- (IS) und Out-of-Sample-Tests (OOS) und berechnen den Kointegrationsvektor für die IS-Daten. Anhand dieses Kointegrationsvektors berechnen wir dann den Spread der OOS-Daten. Schließlich werten wir die ADF-Statistik für diese OOS-Spanne aus. Wir erwarten, dass sowohl die IS- als auch die OOS-ADF-Statistiken Stationarität anzeigen, um die Portfoliogewichte als stabil zu betrachten.
Das heißt:
- Aufteilen der Daten von In-Sample und Out-of-Sample
- Abrufen von dem der Vektor Coint, der Streuung und der Statistik von ADF von in-sample
- Verwenden desselben Coint-Vektor zur Berechnung der Streuung und der ADF-Statistiken für Out-of-Sample-Daten
Bei diesen ADF-Statistiken handelt es sich um denselben Augmented-Dickey-Fuller-Stationaritätstest (ADF), den wir seit Beginn dieser Reihe verwenden. Auch hier haben wir bereits über den ADF-Test gesprochen, und Sie, der aufmerksame Leser, sind herzlich eingeladen, den Artikel zu lesen, in dem wir den ADF-Test vorgestellt haben.
Es gibt keine „bessere“ oder empfohlene Methode. Beide Methoden sind für unterschiedliche Zwecke nützlich. Der rollender Fenster-Eigenvektorvergleich eignet sich besser für Echtzeit-Modellaktualisierungen und Portfolio-Neugewichtung bei der Überwachung des Online-Handels. Die ADF-Validierung von In-Sample/Out-of-Sample eignet sich besser für Pre-Trading-Szenarien, wie die Scoring-Phase, und für die Bewertung der Portfolio-Lebensfähigkeit bei Backtests. Dies ist die Methode, die wir zur Schätzung der Portfoliostabilität verwenden sollten. Das ist also die Methode, die wir hier in unserem Punktesystem verwenden.
(In Anbetracht der Bedeutung einer kontinuierlichen Neubewertung der Portfoliogewichte für die Kontosicherung werden wir zu gegebener Zeit einen Artikel mit den Ergebnissen vorlegen, die wir beim Benchmarking der Verwendung dieser beiden Methoden in verschiedenen Szenarien erhalten haben).
In dem beigefügten Python-Skript finden Sie die Funktion compute_stability, die wir verwenden. Wie gesagt, sie basiert auf der zweiten oben genannten Methode.
1. Aufteilung der Preisdaten in In-Sample- (standardmäßig die ersten 70%, gesteuert durch `split_ratio=0.7`) und Out-of-Sample-Perioden (die restlichen 30%).
def compute_stability( self, prices: pd.DataFrame, method: str, split_ratio: float = 0.7, det_order: int = 0, k_ar_diff: int = 1 ) -> float: """ Check the stability of the cointegration vector by computing it on in-sample data and evaluating the ADF statistic on the out-of-sample spread. Parameters ---------- prices : pd.DataFrame DataFrame with columns as asset tickers and rows as time-indexed prices. method : str 'Engle-Granger' for pairs or 'Johansen' for baskets. split_ratio : float Fraction of data to use as in-sample (default 0.7). det_order : int Deterministic order for Johansen (default 0). k_ar_diff : int Lag order for Johansen (default 1). Returns ------- float The out-of-sample ADF statistic (more negative indicates stronger stability/stationarity). Returns np.nan if insufficient data. """ n = len(prices) if n < 50: return np.nan split = int(n * split_ratio) if n - split < 30: return np.nan train = prices.iloc[:split] test = prices.iloc[split:]
2. Berechnen Sie den Kointegrationsvektor mit „get_coint_vector“ auf den Daten der Stichprobe (entweder mit Engle-Granger für Paare oder mit Johansen für Körbe).
try:
vec_is = self.get_coint_vector(train, method, det_order, k_ar_diff) 3. Wenden Sie diesen Vektor auf die Preise außerhalb der Stichprobe an, um den Spread zu bilden
spread_oos = pd.Series(np.dot(test.values, vec_is), index=test.index) if spread_oos.std() < 1e-10 or spread_oos.isnull().any(): self.logger.debug("Out-of-sample spread is effectively constant or contains NaNs") raise ValueError("Out-of-sample spread is effectively constant or contains NaNs")
4. Führen Sie den ADF-Test für diese Streuung außerhalb der Stichprobe durch, indem Sie `adfuller` aus `statsmodels` verwenden und die Teststatistik extrahieren (das erste Element des Ergebnisses, `adfuller(spread_oos)[0]`)
adf_stat = adfuller(spread_oos)[0] 5. Rückgabe der ADF-Statistik als `stability_stat`, oder `np.nan`, wenn die Berechnung fehlschlägt (z.B. unzureichende Daten oder numerische Probleme).
return float(adf_stat) except Exception as e: self.logger.warning(f"Stability computation error: {e}") return np.nan
Wie Sie vielleicht schon bemerkt haben, messen wir letztlich nur, wie robust die Kointegrationsbeziehung ist, wenn sie auf Daten außerhalb der Stichprobe angewendet wird. Es handelt sich um die ADF-Statistik, die auf der Out-of-Sample-Spanne berechnet wird und prüft, ob die Spanne stationär bleibt, wenn der (aus In-Sample-Daten abgeleitete) Kointegrationsvektor auf ungesehene Daten angewendet wird. Wir bewerten, ob die Kointegrationsbeziehung wahrscheinlich auch in Zukunft bestehen bleibt.
Es sei daran erinnert, dass eine negativere ADF-Statistik auf eine stärkere Stationarität hinweist, d. h. auf einen stärkeren Hinweis auf eine Mittelwertumkehr in der Out-of-Sample-Spanne. Die üblichen kritischen Werte für ADF bei 5 % Signifikanz liegen zwischen -2,86 und -3,5 (je nach Stichprobengröße und Modell). Wenn `stability_stat` negativer als diese Werte ist, ist die Streuung mit 95%iger Wahrscheinlichkeit stationär.
Andererseits weisen Werte nahe Null oder positive Werte darauf hin, dass die Spanne außerhalb der Stichprobe wahrscheinlich nicht stationär ist, was darauf hindeutet, dass der Kointegrationsvektor in den nicht beobachteten Daten keine Spanne für die Rückkehr zum Mittelwert erzeugt. Dies bedeutet, dass die Kointegrationsbeziehung instabil ist und für den Handel möglicherweise nicht zuverlässig ist.
Die Methode erfordert ausreichende Daten außerhalb der Stichprobe. In diesem Fall werden mindestens 30 Balken erzwungen (n – split < 30). Bei einem Rückblick von 180 Tagen im H4-Zeitrahmen (~1080 Stunden effektiver Börsenhandel, ~270 Balken) beträgt der Zeitraum außerhalb der Stichprobe ~81 Balken. Kürzere Rückblicke können die Zuverlässigkeit verringern.
Bitte beachten Sie, dass der stability_stat spezifisch für den Zeitrahmen ist (in unserem Fall H4). Ein auf H4 stabiler Korb muss nicht unbedingt auf D1 oder M15 stabil sein, vergleichen Sie also innerhalb desselben Zeitrahmens. Berücksichtigen Sie auch, dass die Stabilität der Portfoliogewichte allein nicht ausreicht. Ein Korb mit einer sehr negativen Stabilitätsstat, aber einer langen Halbwertszeit ist zwar stabil, aber zu langsam für den praktischen Handel. Sie sollte immer mit der Halbwertszeit und dem p-Wert (Engle-Granger) oder der Eigenstärke (Johansen) kombiniert werden.
In unserer coint_rank-Tabelle priorisieren wir Körbe mit stability_stat, die negativer als -2,86 für 5% Signifikanz sind, und wir kombinieren sie mit dem half_life und dem p_value für den Paarhandel, oder mit dem half_life und der eigen_strength für Körbe.
Im Folgenden werden diese Filter auf Aktienkörbe angewandt, wobei die Eigenstärke aus dem Johansen-Kointegrationstest verwendet wird.
SELECT timeframe as tf, lookback as lb, assets as basket, coint_vector as weights, eigen_strength as strength, stability_stat as stability, half_life as rev_bars FROM 'coint_rank' WHERE stability_stat < -2.86 ORDER BY eigen_strength DESC;

Abb. 9. Metaeditor-Datenbankschnittstelle mit Anzeige der coint_rank-Tabelle mit ausgewählten Körben für den Backtest
Wir wählen die Körbe mit der höchsten Kointegrationsstärke unter denjenigen aus, deren Stabilitätsstatistik unter unserem Schwellenwert von -2,86 liegt. In hellblauer Farbe haben wir die fünf wichtigsten hervorgehoben, die in Frage kommen. Man beachte, dass die ersten vier der fünf Spitzenreiter mindestens ein Gewicht (d. h. einen Kointegrationsvektor) aufweisen, das im Vergleich zum normalisierten Gewicht (1,0) außergewöhnlich hoch ist.
Nehmen wir den dritten Korb als Beispiel:
| Ticker (Symbol) | AVGO | LAES | MRVL | ASX |
|---|---|---|---|---|
| Gerundetes Gewicht des Portfolios | 1.0 | 47.6 | -3.1 | -78.4 |
Tabelle 2. Ein Aktienkorb mit sehr hohen Leerverkaufsanforderungen
Das bedeutet, dass wir für jede AVGO-Aktie in unserer Position 47 LAES-Aktien kaufen und gleichzeitig 3 MRVL- und 78 ASX-Aktien verkaufen müssten. Als kleine Einzelhändler sollten wir diese Art von Portfolio vermeiden, denn wenn wir nicht über viel Geld und einen schnellen Zugang (z. B. einen „Prioritäts“-Zugang) zu unserem Broker verfügen, könnten wir beim Versuch, dieses Volumen an relativ wenig liquiden Aktien leerverkaufen zu wollen, auf harte Zeiten und hohe Transaktionskosten stoßen. Auch hier ist die „geringe Liquidität“ relativ zu den Aktien mit der höchsten Liquidität in der Halbleiterbranche, wie z. B. NVDA, INTC und AVGO.
Wir würden lieber ein homogeneres Portfolio wählen, wie das in der fünften Zeile.
| Ticker (Symbol) | INTC | WOLF | NVTS | ASX |
|---|---|---|---|---|
| Gerundetes Gewicht des Portfolios | 1.0 | -0.6 | 1.46 | -0.46 |
Tabelle 3. Ein Aktienkorb mit ähnlicher Portfoliogewichtung
Die geringfügig schwächere Kointegrationsstärke wird durch die gute Stabilitätsstatistik (-3,72) weitgehend kompensiert. Außerdem beträgt die Halbwertszeit bis zur mittleren Umkehr nur drei H4-Balken bzw. fast zwölf Stunden, was perfekt zu der Haltezeit passt, die wir für unseren statistischen Arbitrage-Swing-Handel erwarten.
Um dieses Ungleichgewicht in unseren Top-Körben zu vermeiden, können wir die Ticker herausfiltern, die dieses Ungleichgewicht verursachen: LAES und ASX. Man beachte, dass in Tabelle 3 ASX nicht die Ursache für das Ungleichgewicht ist. Das liegt daran, dass er im Vergleich zu NVDA sehr unausgewogen ist, aber wir wollen NVDA nicht herausfiltern, da es Teil unserer Kernstrategie, unserer ursprünglichen Hypothese, ist.
SELECT timeframe as tf, lookback as lb, assets as basket, coint_vector as weights, eigen_strength as strength, stability_stat as stability, half_life as rev_bars FROM 'coint_rank' WHERE stability < -2.86 AND basket NOT LIKE '%LAES%' AND basket NOT LIKE '%ASX%' ORDER BY strength DESC;

Abb. 10. Metaeditor-Datenbankschnittstelle zeigt die coint_rank-Tabelle mit ausgewählten Körben für den Backtest ohne LAES und ASX-Ticker
Jetzt sind die Portfolios unter den fünf führenden Unternehmen ausgewogener. Insbesondere die zweite Reihe zeigt einen vielversprechenden Korb (ohne NVDA), aber mit hoher Kointegrationsstabilität (-4,29) und mit geschätzten Umkehrbalken (half_life) von etwa 48 Stunden (12 x H4), was perfekt zu unserem Swing-Trading-Stil passt.
Lassen Sie uns einen Backtest durchführen und sehen, ob wir eine Verbesserung gegenüber unserem vorherigen Backtest ohne diese beiden zusätzlichen Kriterien erzielen. (Später können wir das mit NVDA in der dritten Reihe überprüfen).
Backtest es
Wie schon im vorherigen Backtest werden die Handelsparameter in unserem EA für das Backtesting fest kodiert. Um zu verstehen, warum das so ist, sehen Sie sich bitte den Backtest des vorherigen Artikels an.
// check if we are backtesting if(MQLInfoInteger(MQL_TESTER)) { Print("Running on Tester"); ArrayResize(symbols, 4); ArrayResize(weights, 4); // "H4",120,"INTC,AMD,AVGO,MU" // "[1.0, -0.5005745550348311, 0.7458706676501435, -0.3108921739081775]" symbols[0] = "INTC"; symbols[1] = "AMD"; symbols[2] = "AVGO"; symbols[3] = "MU"; //--- weights[0] = 1.0; weights[1] = -0.5005745550348311; weights[2] = 0.7458706676501435; weights[3] = -0.3108921739081775; timeframe = PERIOD_H4; //InpLookbackPeriod = 120; } else { // Load strategy parameters from database if(!LoadStrategyFromDB(InpDbFilename, InpStrategyName, symbols, weights, timeframe, InpLookbackPeriod))
Beachten Sie, dass wir den Rückblickzeitraum als mögliche Nutzereingabe (InpLookbackPeriod) aufgenommen haben. Von nun an trennen wir den Lookback, der für die Berechnung des mittleren Spreads/der Standardabweichung (für den Einstieg in/den Ausstieg aus Positionen) verwendet wird, von der Lookback-Period, die für die Bewertung der Kointegration verwendet wird. Außerdem können der Mittelwert und der Standardabweichungsrückblick jetzt in unseren Optimierungen verwendet werden, wie es hier der Fall war.

Fig. 11. Screenshot mit den für den Backtest verwendeten Eingaben
Unten sehen Sie die Optimierungsergebnisse sortiert nach Sharpe Ratio.

Abb. 12. Screenshot mit den Ergebnissen der Backtest-Optimierung, sortiert nach Sharpe Ratio
Wir haben einen einzigen Test mit dem zweiten, hellblau markierten Gerät durchgeführt. Im Vergleich zur ersten Reihe hat dieser zweite Platz eine etwas niedrigere Sharpe Ratio (6,87). Dies wird jedoch durch den höheren Erholungsfaktor (5,72), den geringeren Drawdown (3,36) und die mehr als doppelt so hohe erwartete Auszahlung bei fast der Hälfte der Handelsgeschäfte, d. h. geringere Transaktionskosten, weitgehend ausgeglichen.
Beachten Sie, dass in diesem Optimierungsdurchgang die Positionen nach 3,0 Standardabweichungen vom Mittelwert eingegeben und bei 0,8 Standardabweichungen verlassen werden. Außerdem führen wir einen Backtest mit einem Rückblickzeitraum von 120 Tagen für die Kointegration und einem Rückblickzeitraum von 90 Tagen für die Berechnungen des mittleren Spreads und der Standardabweichung durch.
Der Backtest erstreckte sich über einen Zeitraum von vier Monaten.

Abb. 13. Screenshot mit den im Backtest verwendeten Einstellungen
Der konsolidierte Bericht zeigt vielversprechende Zahlen.

Abb. 14. Screenshot mit den konsolidierten Backtest-Berichtsstatistiken
Beachten Sie die schlechte Qualität der Historie und die Konzentration auf die Grundlagen der statistischen Arbitrage, nicht auf diese speziellen Ergebnisse.
Neben den bereits erwähnten Statistiken ist auch das Verhältnis zwischen Gewinn- (51,67%) und Verlustgeschäften (48,33%) zu beachten. Dieser kleine Vorsprung ist charakteristisch für statistische Arbitrage.
Die Kapitalkurven zeigen eine nachhaltige Entwicklung der Kapitalkurve mit geringen Rückgängen.

Abb. 15. Screenshot der Backtest-Grafik Salden/Kapital
Es scheint, als hätten wir die seltsame Konzentration von Handelsgeschäften, die wir im vorherigen Backtest gefunden haben, in den Griff bekommen, nachdem wir die Stabilität der Portfoliogewichte und die Mean-Reversion-Halbzeit in unser Scoring-System aufgenommen haben.

Abb. 16. Screenshot mit den Zeiten, Tagen und Monaten der Backtest-Positionen
Jetzt sind unsere Handelsgeschäfte auf alle Wochentage und alle vier ausgewählten Monate gut verteilt. Die einzige Konzentration lag in der Handelssitzung (USA), was zu erwarten war, da wir Backtests für Nasdaq-Aktien durchführten.
Die MFE- und MAE-Gewinnverteilung ist verbesserungswürdig.

Abb. 17. Screenshot der Backtest-MFE- und MAE-Gewinnverteilung
Wahrscheinlich können wir bessere Ergebnisse erzielen, wenn wir die dynamische Neugewichtung des Portfolios umsetzen, die das Hauptthema unserer nächsten Ausgabe ist.
Und schließlich das Hauptproblem unseres vorherigen Backtests: die Positionshaltezeiten.

Abb. 18. Screenshot mit den Haltezeiten der Backtest-Positionen
Die maximalen und durchschnittlichen Positionshaltezeiten deuten darauf hin, dass die Einbeziehung der Halbwertszeit der Rückkehr zum Mittelwert in unser Bewertungssystem zu der hier festgestellten Gesamtverbesserung beigetragen hat. Zuvor lag die durchschnittliche Haltedauer einer Position bei etwa einem Monat, wobei mindestens eine Position mehr als fünfzehn Wochen lang offen blieb. Jetzt haben wir einen Durchschnitt von etwa 48 Stunden, wobei nur drei Stellen länger als eine Woche offen blieben. Klassischer Swing-Handel!
Schlussfolgerung
In diesem Artikel haben wir zwei Ranking-Kriterien für ein minimales Scoring-System beschrieben, implementiert und einem Backtest unterzogen, das auf das Ranking von Aktienkörben für statistische Arbitrage durch Kointegration abzielt. Diese beiden Kriterien sind Teil des Bewertungssystems, das wir im vorherigen Artikel beschrieben und entwickelt haben. Das heißt, die beiden hier beschriebenen Kriterien und die drei anderen, die im vorigen Artikel beschrieben wurden, bilden ein einzigartiges Punktesystem, das bei der Qualifizierung der Aktienkörbe für das Backtesting auf einmal angewendet wird.
Das erste beschriebene Kriterium ist die Stabilität des Kointegrationsvektors, mit dem die Stabilität der Portfoliogewichte im Zeitverlauf bewertet wird. Da die Portfoliogewichte die Portfolioabsicherung bestimmen, hängt die gewünschte Marktneutralität von Stat Arb-Strategien in hohem Maße davon ab, dass sie stabil sind. Dieses Kriterium hilft dabei, Körbe zu eliminieren, in denen sie über einen bestimmten Schwellenwert hinaus schwanken.
Das zweite Kriterium, das hier vorgestellt wird, ist die Zeit bis zur Rückkehr zum Mittelwert (Halbwertszeit), die die durchschnittliche Haltedauer der Position schätzt. Dieses Kriterium steht in direktem Zusammenhang mit den strategischen Kriterien, die im vorangegangenen Artikel beschrieben wurden, in dem wir festgelegt haben, ob wir in kurzen oder längeren Zeiträumen handeln wollen, d.h. ob wir intraday, täglich oder über längere Zeiträume handeln wollen. Indem wir die geschätzte Zeit bis zur Umkehrung des Mittelwerts berücksichtigen, vermeiden wir die Aufnahme von Körben, die übermäßig viel Zeit bis zum Ausstieg aus dem Markt benötigen würden, und halten so unser Risiko innerhalb der zulässigen Grenzen.
Schließlich haben wir einen Backtest durchgeführt, nachdem wir diese beiden Kriterien zusammen mit den drei zuvor beschriebenen verwendet hatten. Die Ergebnisse deuten darauf hin, dass wir eine viel bessere Korbauswahl haben können, wenn die Zeit bis zur mittleren Umkehr und die Stabilität der Portfoliogewichte Teil des Bewertungssystems sind.
Wir fügen hier alle Dateien bei, die zur Reproduktion des Experiments erforderlich sind, einschließlich der Backtest-Einstellungen (*.ini-Datei) und der Optimierungskonfiguration (*.set-Datei).
| Datei | Beschreibung |
|---|---|
| config\CointNasdaq.INTC.H4.20250701_20251031.020.ini | Einstellungen der Backtest-Konfiguration |
| config\CointNasdaq.set | Optimierungsparameter SET-Datei |
| Experts\StatArb\CointNasdaq.mq5 | Beispiel eines Expertenberaters |
| Files\StatArb\schema-0.5.sql | Aktualisiertes Datenbankschema (DDL) |
| Include\StatArb\CointNasdaq.mqh | Beispiel einer Expert Advisor-Kopfzeile |
| Python\coint_ranker_auto.py | Python-Klasse für das Ranking und die Speicherung der Kointegrationstests |
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/20173
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Markets Positioning Codex in MQL5 (Teil 2): Bitweises Lernen, mit Multi-Patterns für Nvidia
Markets Positioning Codex in MQL5 (Teil 1): Bitwise Learning für Nvidia
Klassische Strategien neu interpretieren (Teil 13): Unsere Kreuz-Strategie in neue Dimensionen führen (Teil 2)
MetaTrader 5 Machine Learning Blueprint (Teil 5): Sequentielles Bootstrapping – Verzicht auf Kennzeichen, Verbesserung der Ergebnisse
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.
Zunächst einmal schätze ich Ihren Versuch, dieses Thema einfach zu erklären.
Aber ich denke, Ihre Backtests sind weit von der Realität entfernt.
"Every tick" liegt daran, dass wir im Standard-Demokonto ohne Exchange-Abonnement mit sehr minderwertigen historischen Daten für Aktiensymbole arbeiten müssen. Every tick" liefert, wenn auch von geringer Qualität, etwas bessere Verlaufsdaten.
Was die 0-Verzögerung angeht, so liegt das daran, dass ich mich in diesem Artikel auf die Beschreibung des vorgeschlagenen Scoring-System-Skeletts konzentriert habe und nicht auf die Leistung der Strategie im realen Handel. Ich habe also gar nicht darüber nachgedacht.
Verstehen Sie mich nicht falsch. Sie haben Recht und **Ihre Warnung für die Leser ist berechtigt**. Diese "Strategie" muss zwischen Anführungszeichen gelesen werden. Sie war nie als Strategie für die reale Welt gedacht.
Danke.