Prueba posterior de una estrategia de pronóstico para el S&P500 en Python con pandas

El autor:La bondad, Creado: 2019-03-29 14:24:50, Actualizado:

Las bibliotecas Python maduras como matplotlib, pandas y scikit-learn también reducen la necesidad de escribir código de boilerplate o crear nuestras propias implementaciones de algoritmos bien conocidos.

La estrategia de pronóstico

La estrategia de pronóstico en sí se basa en una técnica de aprendizaje automático conocida como analizador discriminante cuadrático, que está estrechamente relacionada con un analizador discriminante lineal.

El pronosticador utiliza los dos rendimientos diarios anteriores como un conjunto de factores para predecir la dirección del mercado de valores de hoy. Si la probabilidad de que el día sea up excede el 50%, la estrategia compra 500 acciones del SPY ETF y las vende al final del día. si la probabilidad de un día de baja excede el 50%, la estrategia vende 500 acciones del SPY ETF y luego las vuelve a comprar al cierre. Por lo tanto, es nuestro primer ejemplo de una estrategia de negociación intradiaria.

Tenga en cuenta que esta no es una estrategia comercial particularmente realista! Es poco probable que logremos un precio de apertura o cierre debido a muchos factores, como la volatilidad excesiva de apertura, el enrutamiento de órdenes por parte de la correduría y los posibles problemas de liquidez alrededor de la apertura / cierre. Además, no hemos incluido los costos de transacción. Estos probablemente serían un porcentaje sustancial de los rendimientos, ya que hay un comercio de ida y vuelta realizado todos los días. Por lo tanto, nuestro pronosticador debe ser relativamente preciso para predecir los rendimientos diarios, de lo contrario, los costos de transacción se comerán todos nuestros rendimientos comerciales.

Aplicación

Al igual que con los otros tutoriales relacionados con Python / pandas he utilizado las siguientes bibliotecas:

  • Python - 2.7.3
  • NumPy - 1.8.0
  • Los pandas - 0.12.0
  • - el número de personas que han sido objeto de una investigación
  • - el desarrollo de las tecnologías de la información y la comunicación

La implementación de snp_forecast.py a continuación requierebacktest.pyde este tutorial anterior. Ademásforecast.pyEl primer paso es importar los módulos y objetos necesarios:

# 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

Una vez que se han incluido todas las bibliotecas y módulos relevantes, es hora de subclasificar la clase base abstracta de Estrategia, como hemos realizado en tutoriales anteriores. SNPForecastingStrategy está diseñado para adaptar un analizador de discriminantes cuadráticos al índice bursátil S&P500 como un medio para predecir su valor futuro.

Los detalles de cómo funciona un analizador de discriminantes cuadráticos, así como la implementación de Python a continuación, se describen en detalle en el artículo anterior sobre pronóstico de series temporales financieras.

# 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

Ahora que el motor de pronóstico ha producido las señales, podemos crear un MarketIntradayPortfolio.

La cartera está diseñada para "ir largo" (comprar) 500 acciones de SPY al precio de apertura si la señal indica que se producirá un día de alza y luego vender al cierre.

Para lograr esto, la diferencia de precio entre los precios de apertura y cierre del mercado se determina todos los días, lo que conduce a un cálculo de la ganancia diaria de las 500 acciones compradas o vendidas. Esto luego conduce naturalmente a una curva de capital mediante la suma acumulada de la ganancia / pérdida de cada día. También tiene la ventaja de permitirnos calcular estadísticas de ganancia / pérdida para cada día.

Este es el listado de la cartera 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

El paso final consiste en vincular los objetivos de la Estrategia y de la Cartera con unael principalLa función obtiene los datos para el instrumento SPY y luego crea la estrategia de generación de señales en el propio índice S&P500. Esto es proporcionado por el ticker ^GSPC. Luego se genera una cartera MarketIntraday con un capital inicial de 100,000 USD (como en los tutoriales anteriores). Finalmente, se calculan los rendimientos y se traza la curva de renta variable.

Observe cuán poco código se requiere en esta etapa porque todo el cálculo pesado se lleva a cabo en las subclases de Estrategia y Cartera.

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

El resultado del programa se muestra a continuación. En este período, el mercado de valores devolvió el 4% (suponiendo una estrategia de compra y retención totalmente invertida), mientras que el algoritmo mismo también devolvió el 4%. Tenga en cuenta que los costos de transacción (como las comisiones) no se han agregado a este sistema de backtesting.

Performance de la estrategia de pronóstico del S&P500 desde 2005-01-01 hasta 2006-12-31

En los artículos siguientes añadiremos costes de transacción realistas, utilizaremos motores de pronóstico adicionales, determinaremos métricas de rendimiento y proporcionaremos herramientas de optimización de cartera.


Más.