Pairing-Trading auf Basis von datengetriebenen Technologien

Schriftsteller:Gutes, Erstellt: 2019-08-21 13:50:23, Aktualisiert: 2023-10-19 21:01:31

img

Pairing-Trading ist ein gutes Beispiel für die Entwicklung einer Handelsstrategie, die auf mathematischer Analyse basiert. In diesem Artikel zeigen wir, wie wir Daten nutzen können, um Pairing-Trading-Strategien zu erstellen und zu automatisieren.

Grundprinzipien

假设你有一对投资标的X和Y具有一些潜在的关联,例如两家公司生产相同的产品,如百事可乐和可口可乐。你希望这两者的价格比率或基差(也称为差价)随时间的变化而保持不变。然而,由于临时供需变化,如一个投资标的的大买/卖订单,对其中一家公司的重要新闻的反应等,这两对之间的价差可能会不时出现分歧。在这种情况下,一只投资标的向上移动而另一只投资标的相对于彼此向下移动。如果你希望这种分歧随着时间的推移恢复正常,你就可以发现交易机会(或套利机会)。此种套利机会在数字货币市场或者国内商品期货市场比比皆是,比如BTC与避险资产的关系;期货中豆粕,豆油与大豆品种之间的关系.

Wenn es eine temporäre Preisdifferenz gibt, wird der Handel auf die hervorragend performenden Anleger verkaufen und die schlecht performenden Anleger kaufen. Sie können sicher sein, dass die Differenz zwischen den beiden Anlegern letztendlich durch einen Rückgang der hervorragend performenden Anleger oder eine Erholung der schlecht performenden Anleger oder beides entsteht. Ihr Handel wird in allen ähnlichen Szenarien Geld verdienen.

Daher ist Pairing Trading eine marktneutrale Handelsstrategie, die es den Tradern ermöglicht, von nahezu allen Marktbedingungen zu profitieren: Aufwärtstrends, Abwärtstrends oder horizontalen Anpassungen.

Definition des Konzepts: Zwei hypothetische Anlagemärkte

  • Wir bauen unsere Forschungsumgebung auf der Quantifizierungsplattform für Erfinder.

Zunächst einmal müssen wir unsere Forschungsumgebung bauen, um die Arbeit reibungslos durchführen zu können, und in diesem Artikel nutzen wir die Quantifizierungsplattform der Erfinder.FMZ.COMDie Entwicklung von Docker-Systemen, die für die Entwicklung von Docker-Systemen verwendet werden, wurde in der Vergangenheit in der Forschung durchgeführt, vor allem für eine einfache und schnelle API-Schnittstelle und ein vollständiges Docker-System.

In der offiziellen Bezeichnung der Quantifizierungsplattform der Erfinder wird das Docker-System als Host-System bezeichnet.

Über die Implementierung von Administratoren und Roboter lesen Sie in meinem früheren Artikel:https://www.fmz.com/bbs-topic/4140

Leser, die sich einen Cloud-Deployment-Manager kaufen möchten, können sich an diesen Artikel wenden:https://www.fmz.com/bbs-topic/2848

Nachdem wir einen guten Cloud-Dienst und ein gutes Administrator-System erfolgreich bereitgestellt haben, werden wir nun den größten Templar von Python installieren: Anaconda

Der einfachste Weg, um alle hierfür notwendigen Programmierumgebungen (Dependenzen, Versionsmanagement usw.) zu realisieren, ist Anaconda.

Über die Installation von Anaconda lesen Sie im offiziellen Anaconda-Handbuch:https://www.anaconda.com/distribution/

本文还将用到numpy和pandas这两个目前在Python科学计算方面十分流行且重要的库.

Diese grundlegenden Arbeiten können auch auf meinen früheren Artikel über die Einrichtung der Anaconda-Umgebung und der Bibliotheken numpy und pandas zurückgeführt werden:https://www.fmz.com/digest-topic/4169

Als nächstes wollen wir mit dem Code eine "Zwei-Annahmen-Investitionsmarke" realisieren.

import numpy as np
import pandas as pd

import statsmodels
from statsmodels.tsa.stattools import coint
# just set the seed for the random number generator
np.random.seed(107)

import matplotlib.pyplot as plt

Ja, wir werden auch mit der sehr bekannten Matplotlib-Tabelle in Python arbeiten.

Lassen Sie uns eine hypothetische Investitionsmarke X erzeugen und ihre tägliche Rendite durch eine normale Verteilung simulieren.

# Generate daily returns
Xreturns = np.random.normal(0, 1, 100) 
# sum them and shift all the prices up
X = pd.Series(np.cumsum(
    Xreturns), name='X') 
    + 50
X.plot(figsize=(15,7))
plt.show()

img

X der Anlage, simuliert seine tägliche Rendite durch eine normale Verteilung

Jetzt haben wir Y und X, die wir erzeugen, stark miteinander verknüpft, also sollte der Preis von Y sehr ähnlich sein wie die Veränderung von X. Wir modellieren dies, indem wir X nehmen, es nach oben bewegen und einige zufällige Geräusche, die aus der normalen Verteilung extrahiert wurden, hinzufügen.

noise = np.random.normal(0, 1, 100)
Y = X + 5 + noise
Y.name = 'Y'
pd.concat([X, Y], axis=1).plot(figsize=(15,7))
plt.show()

img

X und Y der Koordination von Investitionsindikatoren

Zusammenarbeiten

协整非常类似于相关性,意味着两个数据系列之间的比率将在平均值附近变化.Y和X这两个系列遵循以下内容:

Y = X + e

Hierbei ist die R = Konstante und e = Rauschen.

Für Transaktionspaare zwischen zwei Zeitreihen muss dieser Verhältnis mit der Zeit zu den erwarteten Werten konvergieren, d.h. sie sollten koherent sein. Die Zeitreihen, die wir oben aufgebaut haben, sind koherent.

(Y/X).plot(figsize=(15,7)) 
plt.axhline((Y/X).mean(), color='red', linestyle='--') 
plt.xlabel('Time')
plt.legend(['Price Ratio', 'Mean'])
plt.show()

img

Verhältnis und Durchschnitt zwischen den Preisen zweier Koordinierungsmarken

Ko-Tests

Eine praktische Testmethode ist die Verwendung von statsmodels.tsa.stattools. Wir sollten einen sehr niedrigen p-Wert sehen, da wir zwei möglichst zusammenhängende Datensätze künstlich erstellt haben.

# compute the p-value of the cointegration test
# will inform us as to whether the ratio between the 2 timeseries is stationary
# around its mean
score, pvalue, _ = coint(X,Y)
print pvalue

Das Ergebnis lautet: 1.81864477307e-17

Hinweis: Relevanz und Integration

Relativität und Koordination sind zwar theoretisch ähnlich, aber nicht gleich. Lassen Sie uns Beispiele von relativen, aber nicht koordinatorischen Datensätzen und umgekehrt betrachten.

X.corr(Y)

Das Ergebnis ist: 0.951.

Wie wir erwartet haben, ist das sehr hoch. Aber was ist mit zwei zusammenhängenden, aber nicht zusammenhängenden Sätzen? Ein einfaches Beispiel ist zwei abweichende Datensätze.

ret1 = np.random.normal(1, 1, 100)
ret2 = np.random.normal(2, 1, 100)

s1 = pd.Series( np.cumsum(ret1), name='X')
s2 = pd.Series( np.cumsum(ret2), name='Y')

pd.concat([s1, s2], axis=1 ).plot(figsize=(15,7))
plt.show()
print 'Correlation: ' + str(X_diverging.corr(Y_diverging))
score, pvalue, _ = coint(X_diverging,Y_diverging)
print 'Cointegration test p-value: ' + str(pvalue)

img

Zwei verwandte Serien (nicht zusammengefasst)

Koeffizienten: 0.998 Kointegrationsprüfungs-P-Wert: 0.258

Ein einfaches Beispiel für eine Kointegration ohne Korrelation sind Ordnungsverteilungssequenzen und Quadratwellen.

Y2 = pd.Series(np.random.normal(0, 1, 800), name='Y2') + 20
Y3 = Y2.copy()
Y3[0:100] = 30
Y3[100:200] = 10
Y3[200:300] = 30
Y3[300:400] = 10
Y3[400:500] = 30
Y3[500:600] = 10
Y3[600:700] = 30
Y3[700:800] = 10
Y2.plot(figsize=(15,7))
Y3.plot()
plt.ylim([0, 40])
plt.show()
# correlation is nearly zero
print 'Correlation: ' + str(Y2.corr(Y3))
score, pvalue, _ = coint(Y2,Y3)
print 'Cointegration test p-value: ' + str(pvalue)

img

Siehe auch: P-Wert der Kointegrationsprüfung: 0.0

Die Korrelation ist sehr gering, aber der P-Wert zeigt perfekte Koordination!

Wie wird der Pairing getätigt?

Da zwei koordinierte Zeitreihen (z. B. X und Y) aufeinander ausgerichtet und voneinander abweichen, kann es manchmal zu einem hohen und einem niedrigen Marginal kommen. Wir paaren den Handel, indem wir einen Anlagewert kaufen und den anderen verkaufen. Wenn also beide Anlagen gleichzeitig sinken oder steigen, machen wir weder Geld noch verlieren wir, also sind wir neutral im Markt.

Zurück zu Y = X + e in X und Y, so dass das Verhältnis ((Y / X) sich um seinen Durchschnittswert bewegt, und wir machen Geld durch das Verhältnis, das durch den Durchschnittswert zurückkehrt.

  • Multi-Ratio: Dies ist, wenn die Ratio sehr klein ist und wir erwarten, dass sie größer wird. In dem obigen Beispiel haben wir eine Position eröffnet, indem wir mehr Y und weniger X tun.

  • Leerlaufquote: Dies ist, wenn die Rate sehr hoch ist und wir erwarten, dass sie stündlich ändert. In dem Beispiel oben haben wir eine Position eröffnet, indem wir Y leeren und mehr als X tun.

Bitte beachten Sie, dass wir immer eine heikle Hedging-Position haben: Wenn der Kauf-Verlust-Wert der Handelsmarke liegt, macht die Leerposition Geld, und umgekehrt, so dass wir immun gegen die Gesamtmarktbewegung sind.

Wenn X und Y der Markierung in Relation zueinander bewegen, dann machen wir Geld oder verlieren.

Mit Hilfe von Daten suchen Sie nach ähnlichen Handelszeichen.

Der beste Weg, dies zu tun, ist, mit dem zu beginnen, was Sie vermuten, dass es sich um ein möglicherweise zusammenhängendes Handelszeichen handelt, und einen statistischen Test durchzuführen.MehrfachvergleicheDie Opfer der Gewalt.

MehrfachvergleicheBezeichnet die Situation, in der die Chancen erhöht werden, dass bei vielen Tests ein wichtigerer P-Wert falsch erzeugt wird, da wir viele Tests durchführen müssen. Wenn wir 100 Mal auf die zufälligen Daten testen, sollten wir 5 P-Werte unter 0.05 sehen. Wenn Sie n Transaktionen vergleichen, um eine Gesamtkoordination durchzuführen, werden Sie viele falsche P-Werte sehen, die mit zunehmender Anzahl Ihrer Testproben steigen. Um dies zu vermeiden, wählen Sie eine kleine Anzahl von Transaktionen aus, für die Sie einen Grund haben, sicherzustellen, dass sie Koordination haben, und testen Sie sie dann einzeln.Mehrfachvergleiche

Lassen Sie uns also versuchen, einige Indizes zu finden, die Koordinationen zeigen. Nehmen wir zum Beispiel einen Korb von großen US-amerikanischen Tech-Aktien im S&P 500, die in ähnlichen Segmenten der Märkte operieren und Koordinationen haben.

Die zurückgegebene Kointegration prüft die Komponentenmatrix, die p-Wert-Matrix und alle Paare mit einem p-Wert kleiner als 0.05.Diese Methode ist anfällig für mehrere Abweichungen, so dass sie tatsächlich eine zweite Überprüfung benötigen.In diesem Artikel, um es uns leichter zu erklären, haben wir uns dafür entschieden, dies in den Beispielen zu ignorieren.

def find_cointegrated_pairs(data):
    n = data.shape[1]
    score_matrix = np.zeros((n, n))
    pvalue_matrix = np.ones((n, n))
    keys = data.keys()
    pairs = []
    for i in range(n):
        for j in range(i+1, n):
            S1 = data[keys[i]]
            S2 = data[keys[j]]
            result = coint(S1, S2)
            score = result[0]
            pvalue = result[1]
            score_matrix[i, j] = score
            pvalue_matrix[i, j] = pvalue
            if pvalue < 0.02:
                pairs.append((keys[i], keys[j]))
    return score_matrix, pvalue_matrix, pairs

Hinweis: Wir haben die Marktbenchmarks (SPX) in den Daten enthalten - die Märkte treiben die Bewegung vieler Handelsindizes voran, und normalerweise können Sie zwei scheinbar zusammenhängende Handelsindizes finden; aber in Wirklichkeit sind sie nicht zusammenhängend, sondern mit dem Markt. Dies wird als Mischvariable bezeichnet. Es ist wichtig, die Marktbeteiligung in jeder Beziehung zu überprüfen, die Sie finden.

from backtester.dataSource.yahoo_data_source import YahooStockDataSource
from datetime import datetime
startDateStr = '2007/12/01'
endDateStr = '2017/12/01'
cachedFolderName = 'yahooData/'
dataSetId = 'testPairsTrading'
instrumentIds = ['SPY','AAPL','ADBE','SYMC','EBAY','MSFT','QCOM',
                 'HPQ','JNPR','AMD','IBM']
ds = YahooStockDataSource(cachedFolderName=cachedFolderName,
                            dataSetId=dataSetId,
                            instrumentIds=instrumentIds,
                            startDateStr=startDateStr,
                            endDateStr=endDateStr,
                            event='history')
data = ds.getBookDataByFeature()['Adj Close']
data.head(3)

img

Lassen Sie uns jetzt versuchen, unsere Methode zu verwenden, um eine koherente Transaktionspaarung zu finden.

# Heatmap to show the p-values of the cointegration test
# between each pair of stocks
scores, pvalues, pairs = find_cointegrated_pairs(data)
import seaborn
m = [0,0.2,0.4,0.6,0.8,1]
seaborn.heatmap(pvalues, xticklabels=instrumentIds, 
                yticklabels=instrumentIds, cmap=’RdYlGn_r’, 
                mask = (pvalues >= 0.98))
plt.show()
print pairs
[('ADBE', 'MSFT')]

img

Es sieht so aus, als ob die ADBE- und die MSFT-Kombinationen zusammenarbeiten. Lassen Sie uns den Preis betrachten, um sicherzustellen, dass es wirklich Sinn macht.

S1 = data['ADBE']
S2 = data['MSFT']
score, pvalue, _ = coint(S1, S2)
print(pvalue)
ratios = S1 / S2
ratios.plot()
plt.axhline(ratios.mean())
plt.legend([' Ratio'])
plt.show()

img

Preisverhältnis zwischen MSFT und ADBE von 2008 bis 2017

Diese Ratio sieht tatsächlich wie ein stabiler Durchschnitt aus. Absolute Ratio ist nicht sehr nützlich in der Statistik. Es hilft uns, unser Signal zu standardisieren, indem wir es als Z-Score betrachten.

Z-Score (Wert) = (Wert Mittelwert) / Standardabweichung

Warnung

In der Praxis versuchen wir normalerweise, die Daten etwas zu vergrößern, vorausgesetzt, sie sind normal verteilt. Aber viele Finanzdaten sind nicht normal verteilt, also müssen wir sehr vorsichtig sein, wenn wir Statistiken erstellen, nicht einfach die Normalisierung oder irgendeine bestimmte Verteilung anzunehmen. Die wahre Verteilung der Proportionen kann einen Fettrücken haben, und diejenigen, die zu den Extremen neigen, können unser Modell verwirren und zu großen Verlusten führen.

def zscore(series):
    return (series - series.mean()) / np.std(series)
zscore(ratios).plot()
plt.axhline(zscore(ratios).mean())
plt.axhline(1.0, color=’red’)
plt.axhline(-1.0, color=’green’)
plt.show()

img

Z-Preis-Verhältnis zwischen MSFT und ADBE zwischen 2008 und 2017

Es ist jetzt leichter zu beobachten, wie sich die Raten in der Nähe des Mittelwerts bewegen, aber manchmal ist es leicht, große Unterschiede zum Mittelwert zu erzielen, und wir können davon profitieren.

Jetzt, da wir die Grundlagen der Pairing-Handelsstrategie besprochen haben und eine gemeinsame, integrierte Handelsmarke anhand historischer Preise festgelegt haben, versuchen wir, ein Handelssignal zu entwickeln. Zuerst wollen wir die Schritte zur Entwicklung von Handelssignalen mit Hilfe von Datentechnologie überprüfen:

  • Verwenden Sie Daten, um zuverlässige Daten zu sammeln und zu reinigen

  • Funktionen aus Daten erstellen, um Transaktionssignale/Logik zu erkennen

  • Funktionen können beispielsweise bewegliche Durchschnitte oder Preisdaten, Relevanz oder Verhältnisse von komplexeren Signalen sein - kombiniert werden, um neue Funktionen zu erzeugen

  • Diese Funktionen werden verwendet, um Handelssignale zu erzeugen, d.h. welche Signale zu kaufen, zu verkaufen oder zu kaufen sind.

Glücklicherweise haben wir eine Plattform für die Quantifizierung von Erfindern.fmz.comDas ist ein großes Glück für die Strategieentwickler, dass wir unsere Energie und Zeit auf die Gestaltung der Strategielogik und die Erweiterung der Funktionalität verwenden können.

Auf der Inventor Quantum-Plattform, wo alle Interfaces der Mainstream-Börsen verpackt sind, müssen wir nur diese API-Interfaces aufrufen, und der Rest der Grundlogik implementiert wird, die von einem professionellen Team ausgefeilt wurde.

Für die Vollständigkeit der Logik und die Erklärung der Prinzipien werden wir diese Grundlogik hier in einer leichten Darstellung darstellen, aber in der Praxis können die Leser die vier Aspekte direkt über die von den Erfindern quantifizierte API-Schnittstelle aufrufen.

Lassen Sie uns anfangen:

Schritt 1: Stellen Sie Ihre Probleme fest

Hier versuchen wir, ein Signal zu erzeugen, das uns sagt, ob die Rate im nächsten Moment gekauft oder verkauft wird, also unsere Prognose-Variable Y:

Y = Verhältnis ist Kauf (1) oder Verkauf (-1)

Y(t)= Zeichen(Verhältnis(t+1) Verhältnis(t))

Bitte beachten Sie, dass wir nicht den Preis für die tatsächliche Handelsmarke prognostizieren müssen, oder sogar den tatsächlichen Wert der Rate prognostizieren müssen (obwohl wir das können), sondern nur die Richtung der nächsten Rate.

Schritt 2: Sammeln von zuverlässigen und genauen Daten

Erfinderquantifizierung ist dein Freund! Du musst nur die Handelsmarke angeben, die du handeln möchtest, und die Datenquelle, die du verwenden möchtest, und es extrahiert die Daten, die du benötigst, und reinigt sie, um Dividenden und Handelsmarken zu unterteilen.

Wir haben die folgenden Daten aus den Handelstagen der letzten 10 Jahre (ca. 2500 Datenpunkte) mit Hilfe von Yahoo Finance erhalten: Öffnungspreis, Schlusspreis, Höchstpreis, Tiefpreis und Handelsvolumen.

Schritt 3: Daten aufteilen

Vergessen Sie nicht diesen sehr wichtigen Schritt zur Prüfung der Modellgenauigkeit.

  • Ausbildung 7 Jahre ~ 70%

  • Test ~ 3 Jahre 30%

ratios = data['ADBE'] / data['MSFT']
print(len(ratios))
train = ratios[:1762]
test = ratios[1762:]

Idealerweise sollten wir auch ein Verifizierungssatz erstellen, aber das tun wir momentan nicht.

Schritt vier: Charakteristik

Was könnte eine relevante Funktion sein? Wir möchten die Richtung der Veränderung der Rate vorhersagen. Wir haben gesehen, dass unsere beiden Handelszeichen zusammenhängen, so dass die Rate oft verschoben und zum Mittelwert zurückkehrt. Es scheint, dass unsere Eigenschaft eine bestimmte Messung des Mittelwerts der Rate sein sollte, die die Differenz zwischen dem aktuellen Wert und dem Mittelwert erzeugt, um unser Handelssignal zu erzeugen.

Wir nutzen folgende Funktionen:

  • 60 Tage gleitender Durchschnitt: Messung des gleitenden Durchschnitts

  • 5-tägige gleitende Durchschnittsquote: Messung des aktuellen Wertes des Durchschnitts

  • 60 Tage Standardabweichung

  • Z-Punkte: ((5d MA - 60d MA) / 60d SD

ratios_mavg5 = train.rolling(window=5,
                               center=False).mean()
ratios_mavg60 = train.rolling(window=60,
                               center=False).mean()
std_60 = train.rolling(window=60,
                        center=False).std()
zscore_60_5 = (ratios_mavg5 - ratios_mavg60)/std_60
plt.figure(figsize=(15,7))
plt.plot(train.index, train.values)
plt.plot(ratios_mavg5.index, ratios_mavg5.values)
plt.plot(ratios_mavg60.index, ratios_mavg60.values)
plt.legend(['Ratio','5d Ratio MA', '60d Ratio MA'])
plt.ylabel('Ratio')
plt.show()

img

Preisverhältnis zwischen 60d und 5d MA

plt.figure(figsize=(15,7))
zscore_60_5.plot()
plt.axhline(0, color='black')
plt.axhline(1.0, color='red', linestyle='--')
plt.axhline(-1.0, color='green', linestyle='--')
plt.legend(['Rolling Ratio z-Score', 'Mean', '+1', '-1'])
plt.show()

img

60-5 Z-Punkte Preisverhältnis

Der Z-Wert der rollenden Maßeinheiten bringt die Maßeinheitsregression des Verhältnisses wirklich hervor!

Schritt 5: Modellwahl

Lassen Sie uns mit einem sehr einfachen Modell beginnen. Schauen wir uns das Z-Score-Graph an und wir können sehen, dass es zurückkehrt, wenn der Z-Score zu hoch oder zu niedrig ist. Lassen Sie uns mit +1/-1 als unser Schwellenwert definieren, ob zu hoch und zu niedrig, und dann können wir das folgende Modell verwenden, um Handelssignale zu generieren:

  • Wenn z weniger als -1.0 ist, dann ist der Prozentsatz ein Kauf ((1) weil wir erwarten, dass z zurück zu 0 kommt, also steigt der Prozentsatz.

  • Wenn z größer als 1.0 ist, ist die Rate ein Verkauf ((−1)), weil wir erwarten, dass z zurück zu 0, so dass die Rate sinkt.

Schritt 6: Trainieren, überprüfen und optimieren

Lassen Sie uns nun sehen, wie sich das Signal in einem tatsächlichen Verhältnis zeigt.

# Plot the ratios and buy and sell signals from z score
plt.figure(figsize=(15,7))
train[60:].plot()
buy = train.copy()
sell = train.copy()
buy[zscore_60_5>-1] = 0
sell[zscore_60_5<1] = 0
buy[60:].plot(color=’g’, linestyle=’None’, marker=’^’)
sell[60:].plot(color=’r’, linestyle=’None’, marker=’^’)
x1,x2,y1,y2 = plt.axis()
plt.axis((x1,x2,ratios.min(),ratios.max()))
plt.legend([‘Ratio’, ‘Buy Signal’, ‘Sell Signal’])
plt.show()

img

Kauf- und Verkaufspreis-Ratio-Signale

Dieses Signal scheint vernünftig zu sein, wir verkaufen den Prozentsatz, wenn er hoch ist oder steigt (roter Punkt) und kaufen ihn zurück, wenn er niedrig ist (grüner Punkt) und sinkt.

# Plot the prices and buy and sell signals from z score
plt.figure(figsize=(18,9))
S1 = data['ADBE'].iloc[:1762]
S2 = data['MSFT'].iloc[:1762]
S1[60:].plot(color='b')
S2[60:].plot(color='c')
buyR = 0*S1.copy()
sellR = 0*S1.copy()
# When buying the ratio, buy S1 and sell S2
buyR[buy!=0] = S1[buy!=0]
sellR[buy!=0] = S2[buy!=0]
# When selling the ratio, sell S1 and buy S2 
buyR[sell!=0] = S2[sell!=0]
sellR[sell!=0] = S1[sell!=0]
buyR[60:].plot(color='g', linestyle='None', marker='^')
sellR[60:].plot(color='r', linestyle='None', marker='^')
x1,x2,y1,y2 = plt.axis()
plt.axis((x1,x2,min(S1.min(),S2.min()),max(S1.max(),S2.max())))
plt.legend(['ADBE','MSFT', 'Buy Signal', 'Sell Signal'])
plt.show()

img

Signale zum Kauf und Verkauf von MSFT- und ADBE-Aktien

Beachten Sie, wie wir manchmal mit "kurzen Beinen" Geld verdienen, manchmal mit "langen Beinen" und manchmal beides.

Wir sind mit den Signalen der Trainingsdaten zufrieden. Lassen Sie uns sehen, welche Profitwirkung dieses Signal erzielen kann. Wenn die Rate niedrig ist, können wir einen einfachen Rückmelder herstellen, in dem wir ein Verhältnis kaufen (kauft 1 ADBE-Aktie und verkauft 1 x MSFT-Aktie), und wenn es hoch ist, verkaufen wir ein Verhältnis (kauft 1 ADBE-Aktie und kauft 1 x MSFT-Aktie) und berechnen die PnL-Transaktionen dieser Verhältnisse.

# Trade using a simple strategy
def trade(S1, S2, window1, window2):
    
    # If window length is 0, algorithm doesn't make sense, so exit
    if (window1 == 0) or (window2 == 0):
        return 0
    
    # Compute rolling mean and rolling standard deviation
    ratios = S1/S2
    ma1 = ratios.rolling(window=window1,
                               center=False).mean()
    ma2 = ratios.rolling(window=window2,
                               center=False).mean()
    std = ratios.rolling(window=window2,
                        center=False).std()
    zscore = (ma1 - ma2)/std
    
    # Simulate trading
    # Start with no money and no positions
    money = 0
    countS1 = 0
    countS2 = 0
    for i in range(len(ratios)):
        # Sell short if the z-score is > 1
        if zscore[i] > 1:
            money += S1[i] - S2[i] * ratios[i]
            countS1 -= 1
            countS2 += ratios[i]
            print('Selling Ratio %s %s %s %s'%(money, ratios[i], countS1,countS2))
        # Buy long if the z-score is < 1
        elif zscore[i] < -1:
            money -= S1[i] - S2[i] * ratios[i]
            countS1 += 1
            countS2 -= ratios[i]
            print('Buying Ratio %s %s %s %s'%(money,ratios[i], countS1,countS2))
        # Clear positions if the z-score between -.5 and .5
        elif abs(zscore[i]) < 0.75:
            money += S1[i] * countS1 + S2[i] * countS2
            countS1 = 0
            countS2 = 0
            print('Exit pos %s %s %s %s'%(money,ratios[i], countS1,countS2))
            
            
    return money
trade(data['ADBE'].iloc[:1763], data['MSFT'].iloc[:1763], 60, 5)

Das Ergebnis: 1783.375

Jetzt können wir die Leistungsverbesserungen der Verifizierungsdaten überprüfen, indem wir das bewegliche Durchschnittszeitfenster ändern, die PEA-Schwelle ändern und so weiter optimieren.

Wir können auch mit komplexeren Modellen wie Logistic Regression, SVM usw. versuchen, um 1/-1 Prognosen zu machen.

Nun, lassen Sie uns das Modell vorantreiben, und das bringt uns zu

Schritt 7: Überprüfung der Testdaten

Die Quantifizierungsplattform der Erfinder nutzt eine leistungsstarke QPS/TPS-Rückmessungseinheit, die historische Umgebungen realisiert, die häufigen Quantifizierungs-Rückmessungsschwierigkeiten beseitigt und Strategienfehler frühzeitig entdeckt, um Real-Time-Investitionen besser zu unterstützen.

In diesem Artikel wird das Prinzip erläutert, die grundlegende Logik dargestellt und in der Praxis empfohlen, die Quantifizierungsplattform der Erfinder zu verwenden, um Zeit zu sparen und die Fehlerquote zu erhöhen.

Die Requestion ist einfach, wir können die Funktion oben verwenden, um die PnL der Testdaten zu sehen.

trade(data[‘ADBE’].iloc[1762:], data[‘MSFT’].iloc[1762:], 60, 5)

Das Ergebnis ist: 5262.868.

Das Modell ist sehr gut! Es ist unser erstes einfaches Pairing-to-Transaction-Modell.

Vermeiden Sie Überanpassung

Bevor ich die Diskussion abschließe, möchte ich speziell über die Über-Fitness sprechen. Über-Fitness ist die gefährlichste Falle in einer Handelsstrategie. Über-Fitness-Algorithmen können bei Rückmeldungen sehr gut funktionieren, aber bei neuen, unsichtbaren Daten scheitern - was bedeutet, dass sie keine Trends in den Daten wirklich aufdecken und keine echte Vorhersagefähigkeit haben.

In unserem Modell schätzen wir die Länge der Zeitfenster mit Hilfe von Rollparametern voraus und wollen sie optimieren. Wir können uns einfach entscheiden, alle Möglichkeiten, die angemessene Zeitfensterlänge zu wiederholen und die Zeitlänge zu wählen, die am besten für unsere Modelle funktioniert. Im Folgenden schreiben wir eine einfache Schleife, um die Zeitfensterlänge von pnl anhand der Trainingsdaten zu bewerten und die beste Schleife zu finden.

# Find the window length 0-254 
# that gives the highest returns using this strategy
length_scores = [trade(data['ADBE'].iloc[:1762], 
                data['MSFT'].iloc[:1762], l, 5) 
                for l in range(255)]
best_length = np.argmax(length_scores)
print ('Best window length:', best_length)
('Best window length:', 40)

Nun, als wir die Modellleistung auf die Testdaten überprüften, stellten wir fest, dass die Länge dieses Zeitfensters nicht optimal war!

# Find the returns for test data
# using what we think is the best window length
length_scores2 = [trade(data['ADBE'].iloc[1762:], 
                  data['MSFT'].iloc[1762:],l,5) 
                  for l in range(255)]
print (best_length, 'day window:', length_scores2[best_length])
# Find the best window length based on this dataset, 
# and the returns using this window length
best_length2 = np.argmax(length_scores2)
print (best_length2, 'day window:', length_scores2[best_length2])
(40, 'day window:', 1252233.1395)
(15, 'day window:', 1449116.4522)

Offensichtlich ist es nicht immer möglich, dass die Daten, die für unsere Stichprobe geeignet sind, in Zukunft gute Ergebnisse liefern.

plt.figure(figsize=(15,7))
plt.plot(length_scores)
plt.plot(length_scores2)
plt.xlabel('Window length')
plt.ylabel('Score')
plt.legend(['Training', 'Test'])
plt.show()

img

Wir können sehen, dass alles zwischen 20 und 50 eine gute Option für das Zeitfenster ist.

Um zu vermeiden, dass wir übermäßig fit sind, können wir die Länge der Zeitfenster durch die Natur der ökonomischen Argumentation oder der Algorithmen wählen. Wir können auch einen Karman-Filter verwenden, der nicht von uns verlangt, dass wir die Länge angeben; diese Methode wird später in einem anderen Artikel beschrieben.

Der nächste Schritt

In diesem Artikel haben wir einige einfache Einführungsmethoden vorgestellt, um den Prozess der Entwicklung von Handelsstrategien zu demonstrieren. In der Praxis sollten kompliziertere Statistiken verwendet werden.

  • Der Hurst Index

  • Halbwertszeit der durchschnittlichen Regression aus dem Ornstein-Uhlenbeck-Prozess

  • Der Karman-Filter


Verwandt

Mehr

bk_fundDas Paket wurde nicht gefunden.

bk_fundIch möchte Ihnen mitteilen, wo ich den Backtester.dataSource.yahoo_data_source installieren kann.