Reife Python-Bibliotheken wie matplotlib, pandas und scikit-learn reduzieren auch die Notwendigkeit, Boilerplate-Code zu schreiben oder eigene Implementierungen bekannter Algorithmen zu entwickeln.
Die Prognose-Strategie selbst basiert auf einer maschinellen Lerntechnik, die als quadratischer Diskriminanten-Analysator bekannt ist und eng mit einem linearen Diskriminanten-Analysator verwandt ist.
Der Prognostiker verwendet die beiden vorherigen Tagesrenditen als eine Reihe von Faktoren, um die heutige Richtung des Aktienmarktes vorherzusagen. Wenn die Wahrscheinlichkeit, dass der Tag
Beachten Sie, dass dies keine besonders realistische Handelsstrategie ist! Es ist unwahrscheinlich, dass wir jemals einen Eröffnungs- oder Schlusskurs aufgrund vieler Faktoren wie übermäßiger Eröffnungsvolatilität, Bestellvermittlung durch die Makler und potenzieller Liquiditätsprobleme rund um das Öffnen / Schließen erzielen. Darüber hinaus haben wir die Transaktionskosten nicht berücksichtigt. Dies wäre wahrscheinlich ein erheblicher Prozentsatz der Renditen, da täglich ein Hin- und Rückhandel durchgeführt wird. Daher muss unser Prognoseur relativ genau bei der Vorhersage der täglichen Renditen sein, sonst werden die Transaktionskosten alle unsere Handelsrenditen fressen.
Wie bei den anderen Python/Pandas-Tutorials habe ich folgende Bibliotheken verwendet:
Die Implementierung von snp_forecast.py benötigtbacktest.pyDas ist eine sehr schwierige Aufgabe.forecast.py(die hauptsächlich die Funktion create_lagged_series enthält) wird aus diesem vorherigen Tutorial erstellt.
# snp_forecast.py
import datetime
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import sklearn
from pandas.io.data import DataReader
from sklearn.qda import QDA
from backtest import Strategy, Portfolio
from forecast import create_lagged_series
Sobald alle relevanten Bibliotheken und Module eingeschlossen sind, ist es an der Zeit, die Strategie-Abstrakte-Basisklasse zu unterteilen, wie wir es in früheren Tutorials durchgeführt haben. SNPForecastingStrategy wurde entwickelt, um einen Quadratischen Diskriminationsanalysator an den S&P500-Aktienindex anzupassen, um seinen zukünftigen Wert vorherzusagen. Die Anpassung des Modells erfolgt in der nachstehenden fit_model-Methode, während die tatsächlichen Signale aus der generate_signals-Methode generiert werden. Dies entspricht der Schnittstelle einer Strategie-Klasse.
Die Details, wie ein quadratischer Diskriminanten-Analysator funktioniert, sowie die Python-Implementierung unten, werden im vorherigen Artikel zur Prognose von Finanzzeitreihen ausführlich beschrieben.
# snp_forecast.py
class SNPForecastingStrategy(Strategy):
"""
Requires:
symbol - A stock symbol on which to form a strategy on.
bars - A DataFrame of bars for the above symbol."""
def __init__(self, symbol, bars):
self.symbol = symbol
self.bars = bars
self.create_periods()
self.fit_model()
def create_periods(self):
"""Create training/test periods."""
self.start_train = datetime.datetime(2001,1,10)
self.start_test = datetime.datetime(2005,1,1)
self.end_period = datetime.datetime(2005,12,31)
def fit_model(self):
"""Fits a Quadratic Discriminant Analyser to the
US stock market index (^GPSC in Yahoo)."""
# Create a lagged series of the S&P500 US stock market index
snpret = create_lagged_series(self.symbol, self.start_train,
self.end_period, lags=5)
# Use the prior two days of returns as
# predictor values, with direction as the response
X = snpret[["Lag1","Lag2"]]
y = snpret["Direction"]
# Create training and test sets
X_train = X[X.index < self.start_test]
y_train = y[y.index < self.start_test]
# Create the predicting factors for use
# in direction forecasting
self.predictors = X[X.index >= self.start_test]
# Create the Quadratic Discriminant Analysis model
# and the forecasting strategy
self.model = QDA()
self.model.fit(X_train, y_train)
def generate_signals(self):
"""Returns the DataFrame of symbols containing the signals
to go long, short or hold (1, -1 or 0)."""
signals = pd.DataFrame(index=self.bars.index)
signals['signal'] = 0.0
# Predict the subsequent period with the QDA model
signals['signal'] = self.model.predict(self.predictors)
# Remove the first five signal entries to eliminate
# NaN issues with the signals DataFrame
signals['signal'][0:5] = 0.0
signals['positions'] = signals['signal'].diff()
return signals
Jetzt, da die Prognose-Engine die Signale erzeugt hat, können wir ein MarketIntradayPortfolio erstellen.
Das Portfolio ist so konzipiert, dass
Um dies zu erreichen, wird täglich die Preisdifferenz zwischen den Marktöffnungs- und den Marktschlusskurs bestimmt, was zu einer Berechnung des täglichen Gewinns für die 500 gekauften oder verkauften Aktien führt. Dies führt dann natürlich zu einer Eigenkapitalkurve, indem die Gewinn-/Verlustsumme für jeden Tag kumuliert wird.
Hier ist die Liste für das MarketIntradayPortfolio:
# snp_forecast.py
class MarketIntradayPortfolio(Portfolio):
"""Buys or sells 500 shares of an asset at the opening price of
every bar, depending upon the direction of the forecast, closing
out the trade at the close of the bar.
Requires:
symbol - A stock symbol which forms the basis of the portfolio.
bars - A DataFrame of bars for a symbol set.
signals - A pandas DataFrame of signals (1, 0, -1) for each symbol.
initial_capital - The amount in cash at the start of the portfolio."""
def __init__(self, symbol, bars, signals, initial_capital=100000.0):
self.symbol = symbol
self.bars = bars
self.signals = signals
self.initial_capital = float(initial_capital)
self.positions = self.generate_positions()
def generate_positions(self):
"""Generate the positions DataFrame, based on the signals
provided by the 'signals' DataFrame."""
positions = pd.DataFrame(index=self.signals.index).fillna(0.0)
# Long or short 500 shares of SPY based on
# directional signal every day
positions[self.symbol] = 500*self.signals['signal']
return positions
def backtest_portfolio(self):
"""Backtest the portfolio and return a DataFrame containing
the equity curve and the percentage returns."""
# Set the portfolio object to have the same time period
# as the positions DataFrame
portfolio = pd.DataFrame(index=self.positions.index)
pos_diff = self.positions.diff()
# Work out the intraday profit of the difference
# in open and closing prices and then determine
# the daily profit by longing if an up day is predicted
# and shorting if a down day is predicted
portfolio['price_diff'] = self.bars['Close']-self.bars['Open']
portfolio['price_diff'][0:5] = 0.0
portfolio['profit'] = self.positions[self.symbol] * portfolio['price_diff']
# Generate the equity curve and percentage returns
portfolio['total'] = self.initial_capital + portfolio['profit'].cumsum()
portfolio['returns'] = portfolio['total'].pct_change()
return portfolio
Der letzte Schritt besteht darin, die Ziele der Strategie und des PortfoliosHauptDie Funktion erhält die Daten für das SPY-Instrument und erstellt dann die Signalgenerierungsstrategie für den S&P500-Index selbst. Dies wird durch den ^GSPC-Ticker bereitgestellt. Anschließend wird ein MarketIntradayPortfolio mit einem Anfangskapital von 100.000 USD (wie in früheren Tutorials) generiert. Schließlich werden die Renditen berechnet und die Eigenkapitalkurve gezeichnet.
Beachten Sie, wie wenig Code in diesem Stadium benötigt wird, da alle schweren Berechnungen in den Unterklassen Strategie und Portfolio durchgeführt werden.
if __name__ == "__main__":
start_test = datetime.datetime(2005,1,1)
end_period = datetime.datetime(2005,12,31)
# Obtain the bars for SPY ETF which tracks the S&P500 index
bars = DataReader("SPY", "yahoo", start_test, end_period)
# Create the S&P500 forecasting strategy
snpf = SNPForecastingStrategy("^GSPC", bars)
signals = snpf.generate_signals()
# Create the portfolio based on the forecaster
portfolio = MarketIntradayPortfolio("SPY", bars, signals,
initial_capital=100000.0)
returns = portfolio.backtest_portfolio()
# Plot results
fig = plt.figure()
fig.patch.set_facecolor('white')
# Plot the price of the SPY ETF
ax1 = fig.add_subplot(211, ylabel='SPY ETF price in $')
bars['Close'].plot(ax=ax1, color='r', lw=2.)
# Plot the equity curve
ax2 = fig.add_subplot(212, ylabel='Portfolio value in $')
returns['total'].plot(ax=ax2, lw=2.)
fig.show()
Der Ausgang des Programms ist unten angegeben. In diesem Zeitraum brachte die Börse 4% zurück (vorausgesetzt, eine vollständig investierte Buy-and-Hold-Strategie), während der Algorithmus selbst auch 4% zurückbrachte. Beachten Sie, dass Transaktionskosten (wie Provisionsgebühren) diesem Backtesting-System nicht hinzugefügt wurden. Da die Strategie einen Hin- und Rückverkehr täglich durchführt, werden diese Gebühren wahrscheinlich die Rendite erheblich reduzieren.
S&P500 Prognose-Strategie-Performance von 2005-01-01 bis 2006-12-31
In den folgenden Artikeln werden wir realistische Transaktionskosten hinzufügen, zusätzliche Prognose-Engines verwenden, Leistungsindikatoren ermitteln und Werkzeuge zur Optimierung des Portfolios bereitstellen.