Retour sur une stratégie de prévision pour le S&P500 en Python avec des pandas

Auteur:La bonté, Créé: 2019-03-29 14:24:50, mis à jour:

Les bibliothèques Python matures telles que matplotlib, pandas et scikit-learn réduisent également la nécessité d'écrire du code classique ou de créer nos propres implémentations d'algorithmes bien connus.

La stratégie de prévision

La stratégie de prévision elle-même est basée sur une technique d'apprentissage automatique connue sous le nom d'analyseur discriminant quadratique, qui est étroitement liée à un analyseur discriminant linéaire.

Le prévisionniste utilise les deux rendements quotidiens précédents comme un ensemble de facteurs pour prédire la direction actuelle du marché boursier. Si la probabilité de la journée est supérieure à 50%, la stratégie achète 500 actions du SPY ETF et les vend à la fin de la journée. si la probabilité d'un jour de baisse est supérieure à 50%, la stratégie vend 500 actions du SPY ETF, puis les rachète à la clôture. C'est donc notre premier exemple de stratégie de négociation intradienne.

Notez que ce n'est pas une stratégie de trading particulièrement réaliste! Il est peu probable que nous atteignions un prix d'ouverture ou de clôture en raison de nombreux facteurs tels que la volatilité d'ouverture excessive, le routage des ordres par le courtage et les problèmes de liquidité potentiels autour de l'ouverture / clôture. En outre, nous n'avons pas inclus les coûts de transaction. Ce serait probablement un pourcentage substantiel des rendements car il y a un commerce aller-retour effectué tous les jours. Ainsi, notre prévisionniste doit être relativement précis pour prédire les rendements quotidiens, sinon les coûts de transaction mangeront tous nos rendements de trading.

Mise en œuvre

Comme pour les autres tutoriels liés à Python / pandas, j'ai utilisé les bibliothèques suivantes:

  • Python - 2.7.3
  • NumPy - 1.8.0
  • Les pandas - 0.12.0
  • Le projet de loi est en cours de révision.
  • Les résultats de l'enquête

La mise en œuvre de snp_forecast.py ci-dessous nécessitebacktest.pyde ce tutoriel précédent.forecast.py(qui contient principalement la fonction create_lagged_series) est créé à partir de ce tutoriel précédent.

# 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

Une fois que toutes les bibliothèques et modules pertinents ont été inclus, il est temps de sous-classer la classe de base abstraite de la stratégie, comme nous l'avons fait dans les tutoriels précédents.

Les détails sur le fonctionnement d'un analyseur de discriminant quadratique, ainsi que l'implémentation Python ci-dessous, sont décrits en détail dans l'article précédent sur la prévision des séries temporelles financières.

# 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

Maintenant que le moteur de prévision a produit les signaux, nous pouvons créer un MarketIntradayPortfolio.

Le portefeuille est conçu pour aller long (acheter) 500 actions de SPY au prix d'ouverture si le signal indique qu'une journée haussière se produira, puis vendre à la clôture.

Pour ce faire, la différence de prix entre les prix d'ouverture et de clôture du marché est déterminée chaque jour, ce qui conduit à un calcul du bénéfice quotidien sur les 500 actions achetées ou vendues. Cela conduit ensuite naturellement à une courbe de fonds propres en résumant cumulativement le bénéfice/perte pour chaque jour.

Voici la liste du portefeuille MarketIntraday:

# 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

L'étape finale consiste à relier les objectifs de la stratégie et du portefeuille à unle principalLa fonction obtient les données pour l'instrument SPY et crée ensuite la stratégie de génération de signal sur l'indice S&P500 lui-même. Ceci est fourni par le ticker ^GSPC. Ensuite, un portefeuille MarketIntraday est généré avec un capital initial de 100 000 USD (comme dans les tutoriels précédents). Enfin, les rendements sont calculés et la courbe des actions est tracée.

Notez combien peu de code est nécessaire à ce stade car tous les calculs lourds sont effectués dans les sous-classes Stratégie et Portfolio.

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()

Le résultat du programme est indiqué ci-dessous. Au cours de cette période, le marché boursier a rapporté 4% (en supposant une stratégie d'achat et de détention entièrement investie), tandis que l'algorithme lui-même a également rapporté 4%. Notez que les coûts de transaction (tels que les frais de commission) n'ont pas été ajoutés à ce système de backtesting.

Performance de la stratégie de prévision du S&P500 de 2005-01-01 à 2006-12-31

Dans les articles suivants, nous ajouterons des coûts de transaction réalistes, utiliserons des moteurs de prévision supplémentaires, déterminerons des indicateurs de performance et fournirons des outils d'optimisation du portefeuille.


Plus de