Strategi perdagangan intraday yang menggunakan regressi rata-rata antara SPY dan IWM

Penulis:Kebaikan, Dibuat: 2019-07-01 11:47:08, Diperbarui: 2023-10-26 20:07:32

img

Dalam artikel ini, kita akan menulis strategi trading intraday. Ini akan menggunakan konsep perdagangan klasik, yaitu mata uang yang memiliki nilai setara dengan mata uang asing. Dalam contoh ini, kita akan menggunakan dua ETF, SPY, dan IWM, yang berdagang di New York Stock Exchange (NYSE) dan mencoba mewakili indeks pasar saham AS, yaitu mata uang S&P 500 dan mata uang Russell 2000".

Strategi ini menciptakan sebuah margin keuntungan dengan melakukan lebih dari satu ETF dan tidak melakukan ETF lain. Rasio banyak ruang dapat didefinisikan dalam banyak cara, seperti menggunakan metode urutan waktu koheren statistik. Dalam skenario ini, kita akan menghitung rasio hedging antara SPY dan IWM dengan regresi linier bergulir. Ini akan memungkinkan kita untuk menciptakan margin keuntungan antara SPY dan IWM, yang ditandatangani sebagai z-score. Ketika z-score melebihi batas tertentu, sinyal perdagangan akan dihasilkan karena kita percaya bahwa margin keuntungan akan kembali ke rata-rata.

Prinsip dasar dari strategi ini adalah bahwa SPY dan IWM mewakili kondisi pasar yang sama, yaitu kinerja harga saham dari sekelompok perusahaan besar dan kecil di Amerika Serikat. Asumsi bahwa jika harga yang diterima adalah nilai moneter yang sama dengan teori moneter regresi, maka selalu ada regresi, karena moneter peristiwa moneter mungkin mempengaruhi S&P 500 dan Russell 2000 masing-masing dalam waktu yang sangat singkat, tetapi perbedaan laba antara mereka selalu kembali ke rata-rata normal, dan kedua urutan harga jangka panjang selalu terintegrasi secara keseluruhan.

Strategi

Strategi ini dilakukan dengan langkah-langkah berikut:

Data - 1 menit grafik k dari SPY dan IWM diperoleh dari April 2007 sampai Februari 2014.

Pengolahan - menyetel data dengan benar dan menghapus k-string yang saling hilang.

Perbedaan - rasio hedging antara dua ETF menggunakan perhitungan regresi linier bergulir. Diartikan sebagai koefisien regresi β menggunakan jendela regresi, yang bergerak ke depan 1 root k dan menghitung kembali koefisien regresi. Oleh karena itu, rasio hedging βi, root k adalah dengan menghitung titik melintasi dari bi-1-k ke bi-1, untuk digunakan untuk membalikkan k line.

Z-Score - nilai dari standar margin dihitung dengan cara yang biasa. Ini berarti mengurangi rata-rata margin (sampel) dan mengurangi standar margin (sampel). Alasan untuk melakukan ini adalah untuk membuat parameter ambang batas lebih mudah dipahami, karena Z-Score adalah kuantitas tanpa dimensi.

Perdagangan - sinyal melakukan lebih banyak ketika nilai z-score negatif turun di bawah ambang batas yang dijadwalkan (atau dioptimalkan), sedangkan sinyal melakukan kosong sebaliknya. Ketika nilai absolut z-score turun di bawah ambang batas tambahan, sinyal penyebaran akan dihasilkan. Untuk strategi ini, saya (agak acak) memilih z-score = 2 sebagai ambang batas bukaan dan z-score = 1 sebagai ambang batas penyebaran.

Mungkin cara terbaik untuk memahami strategi secara mendalam adalah dengan mengimplementasikannya secara praktis. Bagian berikut memberikan penjelasan lengkap tentang kode Python yang digunakan untuk mengimplementasikan strategi pengembalian nilai rata ini ("file tunggal"). Saya telah menambahkan catatan kode yang terperinci untuk membantu Anda memahami lebih baik.

Implementasi Python

Seperti semua tutorial Python/pandas, harus diatur sesuai dengan lingkungan Python yang dijelaskan dalam tutorial ini. Setelah pengaturan selesai, tugas pertama adalah mengimpor perpustakaan Python yang diperlukan. Ini diperlukan untuk menggunakan matplotlib dan pandas.

Saya menggunakan versi khusus dari perpustakaan berikut:

Python - 2.7.3 NumPy - 1.8.0 panda - 0.12.0 Matplotlib - 1.1.0

Mari kita lanjutkan dan mengimpor perpustakaan ini:

# mr_spy_iwm.py

import matplotlib.pyplot as plt
import numpy as np
import os, os.path
import pandas as pd

Fungsi berikut create_pairs_dataframe mengimpor dua file CSV dalam k baris yang berisi dua simbol. Dalam contoh kami, ini akan menjadi SPY dan IWM. Kemudian akan membuat pasangan data pairs yang terpisah, yang akan menggunakan indeks dari dua file asli. Karena transaksi dan kesalahan yang terlewatkan, waktu mereka mungkin berbeda.

# mr_spy_iwm.py
def create_pairs_dataframe(datadir, symbols):
    """Creates a pandas DataFrame containing the closing price
    of a pair of symbols based on CSV files containing a datetime
    stamp and OHLCV data."""
 
    # Open the individual CSV files and read into pandas DataFrames
    print "Importing CSV data..."
    sym1 = pd.io.parsers.read_csv(os.path.join(datadir, '%s.csv' % symbols[0]),
                                  header=0, index_col=0, 
                                  names=['datetime','open','high','low','close','volume','na'])
    sym2 = pd.io.parsers.read_csv(os.path.join(datadir, '%s.csv' % symbols[1]),
                                  header=0, index_col=0, 
                                  names=['datetime','open','high','low','close','volume','na'])
 
    # Create a pandas DataFrame with the close prices of each symbol
    # correctly aligned and dropping missing entries
    print "Constructing dual matrix for %s and %s..." % symbols    
    pairs = pd.DataFrame(index=sym1.index)
    pairs['%s_close' % symbols[0].lower()] = sym1['close']
    pairs['%s_close' % symbols[1].lower()] = sym2['close']
    pairs = pairs.dropna()
    return pairs

Langkah selanjutnya adalah melakukan regresi linear bergulir antara SPY dan IWM. Dalam skenario ini, IWM adalah predictor (x) dan SPY adalah respon (y). Saya mengatur jendela regresi default dengan 100 baris k. Seperti yang disebutkan di atas, ini adalah parameter kebijakan. Untuk membuat kebijakan dianggap kuat, kita idealnya ingin melihat laporan yang dikembalikan menunjukkan status fungsi konveksi (x) atau kinerja metrik lainnya pada periode regresi. Oleh karena itu, pada tahap akhir kode, kita akan melakukan analisis sensitivitas melalui periode regresi dalam rentang perubahan.

Setelah menghitung koefisien bergulir β dalam model regresi linear SPY-IWM, tambahkan ke DataFrame pair dan hapus baris kosong. Ini akan membangun garis K pertama, yang sama dengan ukuran pemotongan panjang regresi. Kemudian, kita membuat dua perbedaan ETF, masing-masing unit SPY dan unit -βi IWM. Jelas, ini tidak realistis karena kita menggunakan jumlah kecil IWM, yang tidak mungkin dalam implementasi praktis.

Akhirnya, kita membuat z-score dari selisih, yang dihitung dengan mengurangi rata-rata selisih dan menggunakan standar selisih. Perlu dicatat bahwa ada selisih prospektif yang cukup halus di sini. Saya sengaja menyimpannya dalam kode karena saya ingin menekankan betapa mudahnya membuat kesalahan dalam penelitian. Menghitung rata-rata dan standar selisih dari seluruh urutan waktu selisih.

# mr_spy_iwm.py
 
def calculate_spread_zscore(pairs, symbols, lookback=100):
    """Creates a hedge ratio between the two symbols by calculating
    a rolling linear regression with a defined lookback period. This
    is then used to create a z-score of the 'spread' between the two
    symbols based on a linear combination of the two."""
     
    # Use the pandas Ordinary Least Squares method to fit a rolling
    # linear regression between the two closing price time series
    print "Fitting the rolling Linear Regression..."
    model = pd.ols(y=pairs['%s_close' % symbols[0].lower()], 
                   x=pairs['%s_close' % symbols[1].lower()],
                   window=lookback)
 
    # Construct the hedge ratio and eliminate the first 
    # lookback-length empty/NaN period
    pairs['hedge_ratio'] = model.beta['x']
    pairs = pairs.dropna()
 
    # Create the spread and then a z-score of the spread
    print "Creating the spread/zscore columns..."
    pairs['spread'] = pairs['spy_close'] - pairs['hedge_ratio']*pairs['iwm_close']
    pairs['zscore'] = (pairs['spread'] - np.mean(pairs['spread']))/np.std(pairs['spread'])
    return pairs

Dalam create_long_short_market_signals, sinyal perdagangan dibuat. Ini dihitung dengan nilai z-score melebihi ambang batas. Ketika nilai absolut z-score lebih kecil dari atau sama dengan ambang batas lain (lebih kecil), sinyal berduaan diberikan.

Untuk mencapai hal ini, perlu untuk menetapkan strategi perdagangan untuk setiap k-line yang terbuka atau terbuka. long_market dan short_market adalah dua variabel yang didefinisikan untuk melacak posisi multihead dan blank. Sayangnya, pemrograman dengan cara iteratif lebih mudah dan karena itu perhitungan lambat dibandingkan dengan metode kuantifikasi. Meskipun k-line chart 1 menit membutuhkan sekitar 700.000 titik data per file CSV, perhitungan di desktop lama saya masih relatif cepat!

Untuk mengiritasi sebuah panda DataFrame (yang tentu saja merupakan operasi yang tidak biasa), perlu menggunakan metode iterrows, yang menyediakan generator yang beriritasi:

# mr_spy_iwm.py
 
def create_long_short_market_signals(pairs, symbols, 
                                     z_entry_threshold=2.0, 
                                     z_exit_threshold=1.0):
    """Create the entry/exit signals based on the exceeding of 
    z_enter_threshold for entering a position and falling below
    z_exit_threshold for exiting a position."""
 
    # Calculate when to be long, short and when to exit
    pairs['longs'] = (pairs['zscore'] <= -z_entry_threshold)*1.0
    pairs['shorts'] = (pairs['zscore'] >= z_entry_threshold)*1.0
    pairs['exits'] = (np.abs(pairs['zscore']) <= z_exit_threshold)*1.0
 
    # These signals are needed because we need to propagate a
    # position forward, i.e. we need to stay long if the zscore
    # threshold is less than z_entry_threshold by still greater
    # than z_exit_threshold, and vice versa for shorts.
    pairs['long_market'] = 0.0
    pairs['short_market'] = 0.0
 
    # These variables track whether to be long or short while
    # iterating through the bars
    long_market = 0
    short_market = 0
 
    # Calculates when to actually be "in" the market, i.e. to have a
    # long or short position, as well as when not to be.
    # Since this is using iterrows to loop over a dataframe, it will
    # be significantly less efficient than a vectorised operation,
    # i.e. slow!
    print "Calculating when to be in the market (long and short)..."
    for i, b in enumerate(pairs.iterrows()):
        # Calculate longs
        if b[1]['longs'] == 1.0:
            long_market = 1            
        # Calculate shorts
        if b[1]['shorts'] == 1.0:
            short_market = 1
        # Calculate exists
        if b[1]['exits'] == 1.0:
            long_market = 0
            short_market = 0
        # This directly assigns a 1 or 0 to the long_market/short_market
        # columns, such that the strategy knows when to actually stay in!
        pairs.ix[i]['long_market'] = long_market
        pairs.ix[i]['short_market'] = short_market
    return pairs

Pada tahap ini, kita memperbarui pasangan untuk memasukkan sinyal multi, kosong yang sebenarnya, yang memungkinkan kita untuk menentukan apakah kita perlu membuka posisi. Sekarang kita perlu membuat portofolio untuk melacak nilai pasar posisi. Tugas pertama adalah untuk membuat kolom posisi yang menggabungkan sinyal multi dan sinyal kosong. Ini akan berisi elemen baris dari ((1, 0, -1), di mana 1 mewakili posisi multi, 0 mewakili posisi tanpa posisi (seharusnya flat), dan -1 mewakili posisi kosong.

Setelah nilai pasar ETF dibuat, kami mengelompokkannya untuk menghasilkan nilai pasar total pada akhir setiap baris k. Kemudian kami mengubahnya menjadi nilai balik melalui metode pct_change dari objek tersebut. Baris kode berikutnya membersihkan entri yang salah (NaN dan elemen inf) dan akhirnya menghitung kurva hak istimewa yang lengkap.

# mr_spy_iwm.py
 
def create_portfolio_returns(pairs, symbols):
    """Creates a portfolio pandas DataFrame which keeps track of
    the account equity and ultimately generates an equity curve.
    This can be used to generate drawdown and risk/reward ratios."""
     
    # Convenience variables for symbols
    sym1 = symbols[0].lower()
    sym2 = symbols[1].lower()
 
    # Construct the portfolio object with positions information
    # Note that minuses to keep track of shorts!
    print "Constructing a portfolio..."
    portfolio = pd.DataFrame(index=pairs.index)
    portfolio['positions'] = pairs['long_market'] - pairs['short_market']
    portfolio[sym1] = -1.0 * pairs['%s_close' % sym1] * portfolio['positions']
    portfolio[sym2] = pairs['%s_close' % sym2] * portfolio['positions']
    portfolio['total'] = portfolio[sym1] + portfolio[sym2]
 
    # Construct a percentage returns stream and eliminate all 
    # of the NaN and -inf/+inf cells
    print "Constructing the equity curve..."
    portfolio['returns'] = portfolio['total'].pct_change()
    portfolio['returns'].fillna(0.0, inplace=True)
    portfolio['returns'].replace([np.inf, -np.inf], 0.0, inplace=True)
    portfolio['returns'].replace(-1.0, 0.0, inplace=True)
 
    # Calculate the full equity curve
    portfolio['returns'] = (portfolio['returns'] + 1.0).cumprod()
    return portfolio

Fungsi utama menggabungkannya. Pada hari ini, file CSV berada di jalur datadir. Pastikan untuk mengubah kode di bawah ini untuk mengarahkan ke direktori khusus Anda.

Untuk menentukan seberapa sensitif strategi terhadap siklus lookback, perlu untuk menghitung sejumlah indikator kinerja lookback. Saya memilih persentase pengembalian total akhir dari portofolio sebagai indikator kinerja dan rentang lookback[50,200], yang meningkat menjadi 10.

# mr_spy_iwm.py
 
if __name__ == "__main__":
    datadir = '/your/path/to/data/'  # Change this to reflect your data path!
    symbols = ('SPY', 'IWM')
 
    lookbacks = range(50, 210, 10)
    returns = []
 
    # Adjust lookback period from 50 to 200 in increments
    # of 10 in order to produce sensitivities
    for lb in lookbacks: 
        print "Calculating lookback=%s..." % lb
        pairs = create_pairs_dataframe(datadir, symbols)
        pairs = calculate_spread_zscore(pairs, symbols, lookback=lb)
        pairs = create_long_short_market_signals(pairs, symbols, 
                                                z_entry_threshold=2.0, 
                                                z_exit_threshold=1.0)
 
        portfolio = create_portfolio_returns(pairs, symbols)
        returns.append(portfolio.ix[-1]['returns'])
 
    print "Plot the lookback-performance scatterchart..."
    plt.plot(lookbacks, returns, '-o')
    plt.show()

img

Sekarang kita dapat melihat grafik tentang lookbacks dan returns. Perhatikan bahwa lookback memiliki nilai maksimum dari lookback global yang sama dengan 110 root k. Jika kita melihat bahwa lookbacks tidak terkait dengan returns, itu karena:

SPY-IWM linear regression hedge versus lookback period sensitivity analysis

Tanpa kurva laba yang miring ke atas, setiap artikel review tidak lengkap! Oleh karena itu, jika Anda ingin menggambar kurva laba laba yang terkumpul dan waktu, Anda dapat menggunakan kode berikut ini. Ini akan menggambar portofolio akhir yang dihasilkan dari studi parameter lookback. Oleh karena itu, perlu untuk memilih lookback berdasarkan grafik yang ingin Anda visualisasikan.

# mr_spy_iwm.py
 
    # This is still within the main function
    print "Plotting the performance charts..."
    fig = plt.figure()
    fig.patch.set_facecolor('white')
 
    ax1 = fig.add_subplot(211,  ylabel='%s growth (%%)' % symbols[0])
    (pairs['%s_close' % symbols[0].lower()].pct_change()+1.0).cumprod().plot(ax=ax1, color='r', lw=2.)
 
    ax2 = fig.add_subplot(212, ylabel='Portfolio value growth (%%)')
    portfolio['returns'].plot(ax=ax2, lw=2.)
 
    fig.show()

Di bawah ini adalah grafik kurva hak dan manfaat untuk periode 100 hari:

img

SPY-IWM linear regression hedge versus lookback period sensitivity analysis

Harap dicatat bahwa SPY 2009 mengalami penyusutan yang sangat besar selama krisis keuangan. Strategi ini juga berada dalam periode yang bergejolak pada saat ini. Juga dicatat bahwa kinerja tahun lalu memburuk karena sifat tren yang kuat dari SPY selama periode ini, yang mencerminkan indeks S&P 500.

Harap dicatat bahwa kita masih perlu mempertimbangkan bias prospektif saat menghitung margin z-score. Selain itu, semua perhitungan ini dilakukan tanpa biaya transaksi. Sekali faktor-faktor ini dipertimbangkan, strategi ini pasti akan berkinerja buruk. Biaya prosedur dan titik geser saat ini belum pasti.

Dalam artikel selanjutnya, kami akan membuat backtester yang lebih kompleks yang akan mempertimbangkan faktor-faktor di atas, sehingga kami dapat menunjukkan lebih banyak kepercayaan pada kurva dana dan indikator kinerja.


Berkaitan

Lebih banyak