Ujian semula Strategi Ramalan untuk S & P500 di Python dengan panda

Penulis:Kebaikan, Dicipta: 2019-03-29 14:24:50, Dikemas kini:

Perpustakaan Python yang matang seperti matplotlib, pandas dan scikit-learn juga mengurangkan keperluan untuk menulis kod boilerplate atau datang dengan pelaksanaan algoritma yang terkenal.

Strategi Ramalan

Strategi ramalan itu sendiri berdasarkan teknik pembelajaran mesin yang dikenali sebagai penganalisis diskriminan kuadrat, yang berkaitan rapat dengan penganalisis diskriminan linear. Kedua-dua model ini diterangkan secara terperinci dalam artikel mengenai ramalan siri masa kewangan.

Penganalisis menggunakan dua pulangan harian sebelumnya sebagai satu set faktor untuk meramalkan hala tuju pasaran saham hari ini. Jika kebarangkalian hari ini up melebihi 50%, strategi membeli 500 saham SPY ETF dan menjualnya pada akhir hari. jika kebarangkalian hari turun melebihi 50%, strategi menjual 500 saham SPY ETF dan kemudian membeli kembali pada penutupan. Oleh itu, ini adalah contoh pertama strategi perdagangan intraday.

Perhatikan bahawa ini bukan strategi perdagangan yang sangat realistik! Kita tidak mungkin mencapai harga pembukaan atau penutupan kerana banyak faktor seperti turun naik pembukaan yang berlebihan, laluan pesanan oleh broker dan potensi masalah kecairan di sekitar bukaan / penutupan. Di samping itu, kita tidak memasukkan kos transaksi. Ini mungkin merupakan peratusan besar pulangan kerana terdapat perdagangan pulang pergi yang dilakukan setiap hari. Oleh itu, peramal kita perlu agak tepat dalam meramalkan pulangan harian, jika tidak, kos transaksi akan memakan semua pulangan perdagangan kita.

Pelaksanaan

Seperti tutorial lain yang berkaitan dengan Python / panda, 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

Pelaksanaan snp_forecast.py di bawah memerlukanbacktest.pydaripada tutorial sebelumnya.forecast.pyLangkah pertama adalah untuk mengimport 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

Apabila semua perpustakaan dan modul yang berkaitan telah disertakan, sudah tiba masanya untuk mengelaskan kelas asas abstrak Strategi, seperti yang telah kita lakukan dalam tutorial sebelumnya. SNPForecastingStrategy direka untuk menyesuaikan Quadratic Discriminant Analyzer dengan indeks saham S&P500 sebagai cara meramalkan nilai masa depannya.

Perincian bagaimana pengkaji diskriminan kuadrat berfungsi, serta pelaksanaan Python di bawah, diterangkan secara terperinci dalam artikel sebelumnya mengenai ramalan siri masa kewangan.

# 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 bahawa enjin ramalan telah menghasilkan isyarat, kita boleh membuat MarketIntradayPortfolio. objek portfolio ini berbeza dari contoh yang diberikan dalam artikel backtest Moving Average Crossover kerana ia menjalankan perdagangan pada asas intraday.

Portofolio direka untuk pergi panjang (beli) 500 saham SPY pada harga pembukaan jika isyarat menyatakan bahawa hari naik akan berlaku dan kemudian menjual pada waktu penutupan. Sebaliknya, portfolio direka untuk pergi pendek (jual) 500 saham SPY jika isyarat menyatakan bahawa hari turun akan berlaku dan kemudian ditutup pada harga penutupan.

Untuk mencapai ini, perbezaan harga antara harga buka pasaran dan harga penutupan pasaran ditentukan setiap hari, yang membawa kepada pengiraan keuntungan harian pada 500 saham yang dibeli atau dijual. Ini kemudian membawa secara semula jadi kepada lengkung ekuiti dengan mengumpulkan keuntungan / kerugian untuk setiap hari. Ia juga mempunyai kelebihan yang membolehkan kita mengira statistik keuntungan / kerugian untuk setiap hari.

Berikut adalah senarai 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 objek Strategi dan Portfolio bersama-sama denganutamafungsi. Fungsi ini memperoleh data untuk instrumen SPY dan kemudian membuat strategi penjana isyarat 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, pulangan dikira dan kurva ekuiti digambarkan.

Perhatikan betapa sedikit kod yang diperlukan pada peringkat ini kerana semua pengiraan berat dilakukan dalam subkelas Strategi dan Portfolio. Ini menjadikannya sangat mudah untuk membuat strategi perdagangan baru dan menguji mereka dengan cepat untuk digunakan dalam pelantar strategi.

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 program diberikan di bawah. Dalam tempoh ini pasaran saham kembali 4% (dengan mengandaikan strategi beli dan memegang yang dilaburkan sepenuhnya), sementara algoritma itu sendiri juga kembali 4%. Perhatikan bahawa kos transaksi (seperti yuran komisen) tidak ditambah kepada sistem backtesting ini. Oleh kerana strategi menjalankan perdagangan perjalanan pulang sekali sehari, yuran ini mungkin akan mengurangkan pulangan dengan ketara.

Prestasi Strategi Ramalan S&P500 dari 2005-01-01 hingga 2006-12-31

Dalam artikel seterusnya, kami akan menambah kos transaksi yang realistik, menggunakan enjin ramalan tambahan, menentukan metrik prestasi dan menyediakan alat pengoptimuman portfolio.


Lebih lanjut