Backtesting Strategi Peramalan untuk S & P500 di Python dengan panda

Penulis:Kebaikan, Dibuat: 2019-03-29 14:24:50, Diperbarui:

Perpustakaan Python yang matang seperti matplotlib, pandas dan scikit-learn juga mengurangi kebutuhan untuk menulis kode boilerplate atau datang dengan implementasi kita sendiri dari algoritma terkenal.

Strategi Peramalan

Strategi peramalan itu sendiri didasarkan pada teknik pembelajaran mesin yang dikenal sebagai analizer diskriminan kuadrat, yang terkait erat dengan analizer diskriminan linier.

Forecaster menggunakan dua pengembalian harian sebelumnya sebagai satu set faktor untuk memprediksi arah pasar saham hari ini. Jika probabilitas hari ini up melebihi 50%, strategi membeli 500 saham SPY ETF dan menjualnya pada akhir hari. jika probabilitas down day melebihi 50%, strategi menjual 500 saham SPY ETF dan kemudian membeli kembali pada saat penutupan. Dengan demikian, ini adalah contoh pertama dari strategi perdagangan intraday.

Perhatikan bahwa ini bukan strategi perdagangan yang sangat realistis! Kita tidak mungkin pernah mencapai harga pembukaan atau penutupan karena banyak faktor seperti volatilitas pembukaan yang berlebihan, perutean pesanan oleh pialang dan masalah likuiditas potensial di sekitar buka/tutup. Selain itu kita tidak memasukkan biaya transaksi. Ini kemungkinan akan menjadi persentase yang substansial dari pengembalian karena ada perdagangan pulang pergi yang dilakukan setiap hari. Oleh karena itu, peramal kita perlu relatif akurat dalam memprediksi pengembalian harian, jika tidak biaya transaksi akan memakan semua pengembalian perdagangan kita.

Pelaksanaan

Seperti halnya tutorial terkait Python/pandas lainnya, saya telah menggunakan perpustakaan berikut:

  • Python - 2.7.3
  • NumPy - 1.8.0
  • panda - 0.12.0
  • Matplotlib - 1.1.0
  • Sikit-belajar - 0.14.1

Implementasi snp_forecast.py di bawah ini membutuhkanbacktest.pydari tutorial sebelumnya.forecast.py(yang terutama berisi fungsi create_lagged_series) dibuat dari tutorial sebelumnya. Langkah pertama adalah mengimpor modul dan objek yang diperlukan:

# 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

Setelah semua perpustakaan dan modul yang relevan telah disertakan, saatnya untuk subkelaskan kelas dasar abstrak Strategi, seperti yang telah kami lakukan dalam tutorial sebelumnya. SNPForecastingStrategy dirancang untuk menyesuaikan Quadratic Discriminant Analyzer dengan indeks saham S&P500 sebagai sarana untuk memprediksi nilai masa depannya.

Rincian tentang cara kerja analizer diskriminan kuadrat, serta implementasi Python di bawah ini, dijelaskan secara rinci dalam artikel sebelumnya tentang peramalan deret waktu keuangan.

# 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

Sekarang mesin peramalan telah menghasilkan sinyal, kita dapat membuat MarketIntradayPortfolio. objek portofolio ini berbeda dari contoh yang diberikan dalam artikel backtest Moving Average Crossover karena melakukan perdagangan pada dasar intraday.

Portofolio dirancang untuk go long (beli) 500 saham SPY pada harga pembukaan jika sinyal menyatakan bahwa hari naik akan terjadi dan kemudian menjual pada saat penutupan. Sebaliknya, portofolio dirancang untuk go short (menjual) 500 saham SPY jika sinyal menyatakan bahwa hari turun akan terjadi dan kemudian ditutup pada harga penutupan.

Untuk mencapai hal ini perbedaan harga antara harga pasar terbuka dan pasar tutup ditentukan setiap hari, yang mengarah ke perhitungan keuntungan harian pada 500 saham yang dibeli atau dijual. Ini kemudian secara alami mengarah ke kurva ekuitas dengan mengumpulkan keuntungan / kerugian untuk setiap hari.

Berikut daftar untuk 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

Langkah terakhir adalah untuk mengikat obyek Strategi dan Portofolio bersama denganutamafungsi. Fungsi ini memperoleh data untuk instrumen SPY dan kemudian membuat strategi generasi sinyal pada indeks S&P500 itu sendiri. Ini disediakan oleh ticker ^GSPC. Kemudian MarketIntradayPortfolio dihasilkan dengan modal awal 100.000 USD (seperti dalam tutorial sebelumnya). Akhirnya, pengembalian dihitung dan kurva ekuitas digambarkan.

Perhatikan betapa sedikitnya kode yang diperlukan pada tahap ini karena semua perhitungan berat dilakukan di subkelas Strategi dan Portofolio.

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

Hasil dari program ini diberikan di bawah ini. Pada periode ini pasar saham kembali 4% (mengasumsikan strategi beli dan tahan yang diinvestasikan sepenuhnya), sementara algoritma itu sendiri juga kembali 4%. Perhatikan bahwa biaya transaksi (seperti biaya komisi) tidak ditambahkan ke sistem backtesting ini. Karena strategi melakukan perdagangan pulang pergi sekali sehari, biaya ini kemungkinan akan mengurangi pengembalian secara signifikan.

Prestasi Strategi Peramalan S&P500 dari 2005-01-01 sampai 2006-12-31

Dalam artikel berikutnya, kami akan menambahkan biaya transaksi yang realistis, menggunakan mesin peramalan tambahan, menentukan metrik kinerja dan menyediakan alat optimasi portofolio.


Lebih banyak