Python-Parameteroptimierung für die TA-Lib-Handelsstrategie

So testen Sie eine Handelsstrategie und optimieren ihre Parameter

Einführung

Ein wichtiger Schritt in der quantitativen Forschung besteht darin, die besten Parametersätze für eine Handelsstrategie zu finden. Ein Satz (nahezu) optimierter Parameter kann eine Gewinnstrategie von einer mittelmäßigen unterscheiden. Dies wird häufig nicht gewürdigt und führt zu einer Underperformance der Handelsstrategien.

In diesem Python-Tutorial werden wir einige Codes erstellen und den Prozess automatisieren. Ich habe als Beispiel eine einfache M oving Average Crossover-Strategie gewählt. Sie werden sehen, dass es mehr gibt, als man denkt. Während dieses Vorgangs erfahren Sie einige Fakten, die Sie überraschen können.

I wurde oft die Frage gestellt: Warum nicht die Werkzeuge verwenden, die üblicherweise für maschinelles Lernen und KI verfügbar sind? Meine Antwort ist zweifach: Erstens besteht tatsächlich die Gefahr einer Überanpassung Für finanzielle Zeitreihen so hoch, dass diese Tools einfach nicht zutreffen. Zweitens sind für jemanden, der mit der Dynamik des Handels vertraut ist, die meisten computergenerierten Modelle selbst nach den unkonventionellsten, aufgeschlossensten Standards nicht sinnvoll, und ihre Leistung ist erwartungsgemäß schlecht.

Beachten Sie, dass der Moving Average Crossover nicht die profitabelste Strategie der Welt ist und nur ein Beispiel. Mit einigen kleinen Änderungen können Sie diese Methode jedoch in großem Umfang auf Ihre eigenen Strategien anwenden.

Heute werden wir also Folgendes diskutieren:

Marktdaten herunterladen

Zunächst laden wir die für unsere Analyse erforderlichen Python-Pakete herunter, z. B. ‘ TA-Lib ‘ für die technische Analyse und ‘ yfinance ‘ für die Marktdaten.

Dann definieren wir der Einfachheit halber eine Liste von Aktien, bei denen es sich um US-Aktien mit hoher Marktkapitalisierung handelt. Als Nächstes verwenden wir das Python-Paket yfinance , um Marktdaten für diese Aktien herunterzuladen.

Beachten Sie, dass wir bei Verwendung der Funktion ‘ yfinance.download’ den Zeitraum definieren müssen, den wir betrachten möchten, in diesem Fall 20 Jahre, sowie die Granularität, die ausgewählt wird 1 Tag. Wenn wir auto_adjust = False wählen, wird in unseren Daten eine Spalte “Adjusted Close” angezeigt, was für uns am bequemsten ist.

Dieser Code gibt einen Datenrahmen mit mehrfach indizierten Spalten zurück. Wir müssen sicherstellen, dass dies korrekt gehandhabt wird. Nachdem wir die Daten haben, sind wir bereit, eine Strategie zu entwickeln.

Die gleitende durchschnittliche Crossover-Strategie

Der Movering Average Crossover ist wahrscheinlich eine der ältesten systematischen Handelsstrategien. Ich habe dieses Modell eher wegen seiner Einfachheit als wegen seiner Effektivität ausgewählt, da sich dieses Tutorial hauptsächlich auf die Strategieoptimierung konzentriert, anstatt das beste Handelssystem zu finden.

Die Strategie funktioniert folgendermaßen: Wir geben einen Trade ein, wenn ein gleitender Durchschnitt den anderen kreuzt, und kippen den Trade beim nächsten Cross und so weiter. Auf diese Weise erhalten wir eine kontinuierliche Folge von Long- und Short-Trades.

Die Implementierung ist ziemlich einfach, wie hier in wenigen Zeilen dargestellt. Zuerst erhalten wir die Daten und verwenden TA-Lib, um zwei gleitende Durchschnitte mit unterschiedlichen Lookback-Fenstern zu erstellen. Wir möchten sicherstellen, dass dem ersten gleitenden Durchschnitt immer der kürzere Lookback und dem zweiten gleitenden Durchschnitt der längere Lookback zugewiesen wird. Als nächstes berechnen wir die Differenz zwischen den beiden.

Jetzt können wir die Handelslogik erstellen. Wir durchlaufen alle verfügbaren Daten ab 2 Jahren, um anfängliche NaNs oder ungültige Datenpunkte zu vermeiden. Ein Trade wird eingegeben, wenn sich das Vorzeichen der aktuellen Differenz des gleitenden Durchschnitts vom Vorzeichen der vorherigen Differenz des gleitenden Durchschnitts unterscheidet. Je nachdem, in welche Richtung sich unsere MAs kreuzen, handeln wir von der langen oder der kurzen Seite. Dies wird durch die Variable “Polarität” (1 für lang und -1 für kurz) angegeben, die uns die Flexibilität gibt, das System von der gegenüberliegenden Seite aus zu betreiben.

In jedem Zeitschritt erfassen wir den nicht realisierten PnL als prozentuale Differenz zwischen dem aktuellen und dem vorherigen Preis mal der Handelsrichtung. Dies muss erfolgen, bevor wir die Handelspolarität ändern. Andernfalls sind unsere Ergebnisse falsch.

Das ist alles, was wir brauchen, um die Strategie zu definieren, aber seien Sie vorsichtig. Kleine Fehler in Zeichen und Sequenzen können zu völlig falschen Ergebnissen führen. Überprüfen Sie daher Ihre Trades und Ergebnisse sorgfältig, wenn Sie eine neue Strategie entwerfen.

Lassen Sie uns eine Beispielstrategie für Oracle mit beliebigen Lookback-Fenstern von 20 und 60 ausführen. Um die PnL-Kurve zu zeichnen, müssen wir die kumulative Summe berechnen. Dies setzt voraus, dass wir unsere Gewinne nicht reinvestieren.

Unsere einfache Strategie hat 2 Parameter – – die beiden Lookback-Fenster! Im nächsten Abschnitt werden wir ein System erstellen, das eine Vielzahl von Parametern testet, um die besten herauszufinden.


Parameter Sweep

T Hier finden Sie verschiedene Möglichkeiten, um Parameter für unsere Strategie zu generieren. Am naheliegendsten ist es, einen Bereich auszuwählen und dann alle möglichen Parameter innerhalb dieses Bereichs auszuprobieren. Da unsere Lookback-Fenster Ganzzahlen sind, können wir die Lookbacks von 2 bis 200 durchlaufen und die Ergebnisse sammeln. Dies würde jedoch bedeuten, dass wir fast 20000 Backtests durchführen müssen, was lange dauern kann. Wenn wir unseren Parameter-Sweep vorzeitig beenden, hätten wir nur ein kleines Quadrat des gesamten Parameterraums abgedeckt.

Oft ist es besser, zufällige Parameter auszuwählen. Dadurch wird der Parameterraum schnell gleichmäßiger abgebildet, und wenn wir vorzeitig beenden, haben wir eine gute Abdeckung, nur mit geringerer Dichte. Diese Abbildung zeigt, wie wir den Parameterraum von 100 Werten mit einem festen Raster in Blau und zufälligen Werten in Rot abbilden:

So implementieren Sie den Parameter-Sweep:

Hier ist r ein Nx2-Array von N -Parameterpaaren. Wir haben zwei Schleifen: eine, die die Parameterpaare durchläuft, und eine, die die Bestände durchläuft. Danach berechnen wir die tägliche Rendite unseres Portfolios als Mittelwert aller Renditen. Dies bedeutet, dass unser Portfolio gleich gewichtet ist. Es ist natürlich möglich, Gewichtungsschemata zu haben, die für die Strategie besser geeignet sind.

Schließlich berechnen wir das Sharpe-Verhältnis S der Strategie als das Verhältnis zwischen dem Mittelwert und der Standardabweichung der Renditen. Der Wert 16 ist die Quadratwurzel der ungefähren Anzahl von Handelstagen in einem Jahr, die typischerweise zwischen 252 und 256 liegt.

Wir können jetzt die Funktion “ Sweep” ausführen. Aber zuerst erstellen wir ein Array von 500 zufälligen Lookback-Paaren mit Werten zwischen 2 und 199. Diese werden dann in die Sweep-Funktion eingesteckt und können loslegen.

Tipps zu Optimierern und Sweeps

Python verfügt auch über eine Reihe von Optimierern, die wir verwenden können, beispielsweise das Paket scipy.minimize . Es gibt auch Pakete für simuliertes Annealing und genetische Algorithmen. Es gibt jedoch verschiedene Gründe, warum Sie Ihren eigenen Sweep erstellen würden.

Erstens sind die meisten Optimierer nicht so gut mit der Optimierung für ganzzahlige Werte wie in unserem Fall. Zweitens ist die Antwortfunktion unseres Backtests diskontinuierlich und voller Spitzen, sodass jeder gradientenbasierte Optimierer bestenfalls Probleme haben würde. Schließlich ist es oft schneller und einfacher, eigene zu erstellen. Es ist in mancher Hinsicht möglicherweise nicht so effizient, aber Sie haben die volle Kontrolle über seine Funktionalität. Wie wir später sehen werden, erfordert das Finden der besten Strategie normalerweise einige Vorsicht und manuelle Arbeit.

Strategieüberprüfung

Es ist zwar großartig, die perfekten Parameter zu finden, aber wir wissen zu gut, dass dies nur zu einer Überanpassung führt und die Ergebnisse der Vergangenheit keineswegs ein Hinweis auf die zukünftige Leistung sind! Wie können wir sicherer sein, dass unsere Strategie tatsächlich funktioniert?

Die erste Frage lautet: Wird der historische PnL unserer Strategie unabhängig von der Leistung einzelner Parametersätze mit der zukünftigen Leistung korrelieren?

Wir erwarten nicht unbedingt eine perfekte Korrelation, sondern zumindest einen Hinweis auf Konsistenz. Der Weg, dies zu tun, besteht darin, unsere Ergebnisse in In-Sample- und Out-of-Sample-Sets aufzuteilen. Anstatt jedoch das beste In-Sample-Set zu finden und zu testen, ob es auch mit den Out-of-Sample-Daten funktioniert, korrelieren wir Alle Ergebnisse innerhalb der Stichprobe (IS) mit allen Ergebnissen außerhalb der Stichprobe (OOS), unabhängig von ihrer Leistung. Auf diese Weise werfen wir keine nützlichen Informationen weg und erhalten insgesamt einen viel besseren Hinweis darauf, ob die Strategie einen Wert hat.

Diese Abbildung zeigt die Korrelationen für unsere IS- und OOS-Sharpe-Verhältnisse. Der Zeitraum beträgt 20 Jahre bei einer Stichprobenlänge von 3000 Tagen. Wir können sehen, dass es eindeutig eine Korrelation gibt. Wir erwarten nicht, dass dies eine perfekt gerade Linie mit einem R-Quadrat von 0,99 ist. Zu sehen, dass die Strategie die Tendenz hat, die vergangene Performance mit einigem Vertrauen in die Zukunft fortzusetzen, ist ein so gutes Ergebnis, wie wir es größtenteils erwarten können.

Ein Problem besteht jedoch darin, dass die Ergebnisse je nach Auswahl eines bestimmten Grenzwerts für unsere Datenlänge in der Stichprobe sehr unterschiedlich aussehen können. Eine Möglichkeit, dies zu überprüfen, besteht darin, eine Reihe verschiedener Grenzperioden zu durchlaufen.

Die Abbildung zeigt, dass wir für Oracle (ORCL) für verschiedene Zeiträume ein konsistentes Verhalten erhalten. Natürlich können wir dies auch für mehrere Unternehmen tun.

Um dies in Code zu implementieren, erstellen wir zunächst eine Funktion zum Zeichnen unserer linearen Regressionslinie. Es gibt mehrere Möglichkeiten, dies mit scipy, sklearn oder numpy zu tun, wie hier gezeigt. Zunächst passen wir unsere IS- und OOS-Metriken an, in unserem Fall das Sharpe-Verhältnis. Als nächstes erstellen wir einen Satz gleichmäßig verteilter x-Werte mit Linspace und schließlich die y-Werte, die die Regressionskoeffizienten m einfügen.

Jetzt können wir unsere Testfunktion erstellen. Wir teilen unsere Daten in IS und OOS auf und wenden dann eine Metrikfunktion auf beide an. Diese Metrikfunktion kann die Berechnung des Gesamtgewinns, des täglichen PnL oder einer anderen Metrik sein. In unserem Fall haben wir das Sharpe-Verhältnis gewählt und sehen später, wie wir es an die Strategie weitergeben können.

Wir zeichnen dann unsere lineare Regressionslinie und die Datenpunkte wie in den vorherigen Darstellungen dargestellt.

Um die Funktion auszuführen, müssen wir unsere Metrik angeben, was wir hier in Form einer Lambda-Funktion tun.

Gut gemacht! Wir haben einen Parameter-Sweep für eine Crossover-Strategie mit gleitendem Durchschnitt durchgeführt und festgestellt, dass für Oracle die Ergebnisse in der Stichprobe gut mit den Ergebnissen außerhalb der Stichprobe korrelieren, und dies ist wahr für eine Reihe von Grenzpunkten. Nachdem wir nun wissen, dass unsere Strategie für Oracle von Nutzen ist, werden wir im zweiten Teil des Tutorials sehen, ob wir die besten Parametersätze extrahieren und daraus eine stabile Strategie erstellen können.

Denken Sie daran, dass Sie das zugehörige Jupyter-Notizbuch für dieses Tutorial in unserem Kurs Quantitative Trading Like a Pro finden:

Seien Sie gespannt auf Teil 2, wir werden tiefer in das Thema eintauchen und die besten Parametersätze aus unseren Beobachtungen extrahieren, um eine stabile, profitable Strategie zu erstellen.

Mehr von Dr. Tom Starke: