SPY और IWM के बीच सममूल्य परावर्तन का उपयोग करने वाली एक दिन-प्रतिदिन की रणनीति

लेखक:अच्छाई, बनाया गयाः 2019-07-01 11:47:08, अद्यतनः 2023-10-26 20:07:32

img

इस लेख में, हम एक दिन के भीतर व्यापार रणनीति लिखेंगे; यह क्लासिक व्यापारिक विचार का उपयोग करेगा, जो कि एक सममूल्य पर लौटने वाले व्यापारिक जोड़े को जोड़ता है; इस उदाहरण में, हम दो ट्रेडेड ओपन-ट्रेडेड इंडेक्स फंडों (ईटीएफ), एसपीवाई और आईडब्ल्यूएम का उपयोग करेंगे, जो न्यूयॉर्क स्टॉक एक्सचेंज (एनवाईएसई) में कारोबार करते हैं और अमेरिकी शेयर बाजार सूचकांक, एसएंडपी 500 और रसेल 2000 को प्रतिस्थापित करने का प्रयास करते हैं।

यह रणनीति कई ईटीएफ बनाने और दूसरे ईटीएफ को खाली करने के द्वारा एक लाभ अंतर उत्पन्न करती है। बहु-अवकाश अनुपात को कई तरीकों से परिभाषित किया जा सकता है, जैसे कि सांख्यिकीय सहसंबंध समय अनुक्रम का उपयोग करना। इस परिदृश्य में, हम रोलिंग लाइनर रिगोनेंस के माध्यम से एसपीवाई और आईडब्ल्यूएम के बीच एक हेजिंग अनुपात की गणना करेंगे। यह हमें एसपीवाई और आईडब्ल्यूएम के बीच एक लाभ अंतर बनाने की अनुमति देगा, जिसे z-स्कोर के रूप में मानकीकृत किया गया है। जब z-स्कोर किसी सीमा से अधिक होता है, तो एक व्यापार संकेत उत्पन्न होता है क्योंकि हमें विश्वास है कि लाभ अंतर औसत पर वापस आ जाएगा।

इस रणनीति का मूल सिद्धांत यह है कि एसपीवाई और आईडब्ल्यूएम लगभग एक ही बाजार की स्थिति का प्रतिनिधित्व करते हैं, जो कि बड़ी और छोटी अमेरिकी कंपनियों के एक समूह के शेयरों का प्रदर्शन है। यह माना जाता है कि यदि मूल्य के लिए स्वीकार किए जाने वाले घन-समानता के लिए घन-समानता की वापसी होती है, तो यह हमेशा वापस आता है, क्योंकि घन घटना घन S&P 500 और रसेल 2000 को बहुत कम समय में प्रभावित कर सकती है, लेकिन उनके बीच लाभ अंतर हमेशा सामान्य औसत पर लौटता है, और दोनों के बीच दीर्घकालिक मूल्य अनुक्रम हमेशा सहसंबद्ध होते हैं।

रणनीति

इस रणनीति को निम्नलिखित चरणों में लागू किया गया हैः

डेटा - अप्रैल 2007 से फरवरी 2014 तक, SPY और IWM के लिए 1 मिनट के स्ट्रिंग ग्राफ प्राप्त किए गए।

संसाधित करना - डेटा को सही ढंग से संरेखित करना और एक-दूसरे से गायब k-strings को हटाना.

अंतर - दो ईटीएफ के बीच हेजिंग अनुपात का उपयोग रोलिंग लाइनर रिग्रोनमेंट के साथ किया जाता है। यह एक रिवर्स विंडो का उपयोग करके परिभाषित किया गया है, जो कि एक रिवर्स विंडो का उपयोग करके 1 रूट k लाइन को आगे बढ़ाता है और रिवर्स को फिर से गणना करता है। इसलिए, एक हेजिंग अनुपात βi, एक रूट K लाइन का उपयोग करने के लिए k लाइन को वापस करने के लिए bi-1-k से bi-1 तक क्रॉसिंग बिंदुओं की गणना करके किया जाता है।

Z-Score - मानक लाभ के मान को सामान्य तरीके से गणना की जाती है── इसका अर्थ है कि ब्याज के औसत (नमूना) को घटाकर ब्याज के मानक (नमूना) को घटाया जाता है── ऐसा करने का कारण यह है कि थ्रेशोल्ड पैरामीटर को समझना आसान है, क्योंकि Z-Score एक dimensionless quantity है── मैंने गणना में पूर्वदृष्टि विकृति का एक छोटा सा हिस्सा शामिल किया है ताकि यह दिखाया जा सके कि यह कितना सूक्ष्म हो सकता है── इसे आज़माएं!

ट्रेडिंग - जब नकारात्मक z-score का मूल्य निर्धारित (या बाद में अनुकूलित) सीमा से नीचे गिरता है तो अधिक संकेत उत्पन्न होते हैं, जबकि शून्य संकेत इसके विपरीत होते हैं; जब z-score का पूर्ण मूल्य अतिरिक्त सीमा से नीचे गिरता है, तो एक समतल संकेत उत्पन्न होता है। इस रणनीति के लिए, मैंने (कुछ हद तक यादृच्छिक रूप से) चुनिंदा z-score = 2 को शुरुआती सीमा के रूप में और z-score = 1 को समतल सीमा के रूप में चुना है। मान लीजिए कि समतल रिटर्न लाभ में भूमिका निभाता है, इन सभी को इस लाभ संबंध को पकड़ने के लिए एक अच्छा लाभ प्रदान करने की उम्मीद है।

शायद नीति को समझने का सबसे अच्छा तरीका है इसे लागू करना. निम्नलिखित भाग में इस समान मूल्य वापसी नीति को लागू करने के लिए उपयोग किए जाने वाले पूर्ण पायथन कोड (एक एकल फ़ाइल) का विस्तार से वर्णन किया गया है. मैंने आपको बेहतर समझने में मदद करने के लिए विस्तृत कोड टिप्पणी जोड़ी है.

पायथन कार्यान्वयन

सभी पायथन/पंडास ट्यूटोरियल की तरह, इसे इस ट्यूटोरियल में वर्णित पायथन वातावरण के अनुसार सेट करना होगा. सेटअप पूरा होने के बाद, पहला कार्य आवश्यक पायथन लाइब्रेरी आयात करना है. यह matplotlib और पांडास का उपयोग करने के लिए आवश्यक है.

मेरे द्वारा उपयोग किए जाने वाले विशिष्ट संस्करण इस प्रकार हैंः

पायथन - 2.7.3 NumPy - 1.8.0 पांडा - 0.12.0 matplotlib - 1.1.0

हम आगे बढ़ते हैं और इन पुस्तकालयों को आयात करते हैंः

# mr_spy_iwm.py

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

निम्न फ़ंक्शन create_pairs_dataframe दो प्रतीक युक्त इनसाइड k-लाइन CSV फ़ाइलों को आयात करता है. हमारे उदाहरण में यह SPY और IWM होगा. फिर यह एक अलग एरे डेटा पेयर जोड़ी बनाता है, जो एरे डेटा पेयर जोड़ी को दो मूल फ़ाइलों के सूचकांक का उपयोग करने के लिए बनाएगा. उनके समयरेखा में चूक और त्रुटियों के कारण भिन्नता हो सकती है. यह एक डेटा एनालिटिक्स जैसे डेटाबेस का उपयोग करने के मुख्य लाभों में से एक है। हम बहुत कुशल तरीके से नमूना एरे कोड को एरे करते हैं।

# 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

अगला कदम SPY और IWM के बीच रैखिक पुनरावृत्ति करना है. इस परिदृश्य में, IWM पूर्वानुमानकर्ता है ((x), SPY प्रतिक्रिया है ((y) । मैंने 100 k लाइनों की एक डिफ़ॉल्ट पुनरावृत्ति खिड़की सेट की है। जैसा कि ऊपर बताया गया है, यह नीति के लिए एक पैरामीटर है। ताकि नीति को मजबूत माना जा सके, हम आदर्श रूप से एक वापसी रिपोर्ट को पुनरावृत्ति अवधि में उभरा फ़ंक्शन की स्थिति ((या अन्य प्रदर्शन मीट्रिक प्रदर्शन) प्रदर्शित करते हुए देखना चाहते हैं। इसलिए, कोड के बाद के चरणों में, हम परिवर्तन के दायरे के भीतर पुनरावृत्ति अवधि के माध्यम से संवेदनशीलता विश्लेषण करेंगे।

SPY-IWM के रैखिक पुनरावृत्ति मॉडल में रोलिंग बीटा गुणांक की गणना करने के बाद, इसे डेटाफ्रेम जोड़े में जोड़ा जाता है और रिक्त पंक्तियों को हटा दिया जाता है। यह पहली K-लाइन सेट का निर्माण करता है, जो कि पुनरावृत्ति लंबाई के लिए कटाई के माप के बराबर है। फिर, हम दो ईटीएफ के लिए लाभ का अंतर बनाते हैं, जो कि SPY की इकाई और IWM की -i इकाई है। जाहिर है, यह वास्तविक नहीं है, क्योंकि हम कम मात्रा में IWM का उपयोग कर रहे हैं, जो कि वास्तविक कार्यान्वयन में असंभव है।

अंत में, हम ब्याज के अंतर का एक z-score बनाते हैं, जिसे ब्याज के अंतर के औसत को घटाकर और मानक ब्याज के मानक अंतर को मानकीकृत करके गणना की जाती है। ध्यान दें कि यहां एक काफी सूक्ष्म पूर्वानुमान विसंगति मौजूद है। मैंने इसे कोड में जानबूझकर छोड़ दिया है क्योंकि मैं इस बात पर जोर देना चाहता हूं कि अध्ययन में इस तरह की गलतियों को करना कितना आसान है। पूरे ब्याज के समय अनुक्रम के औसत और मानक विचलन की गणना करें। यदि यह वास्तविक ऐतिहासिक सटीकता को प्रतिबिंबित करने के लिए है, तो यह जानकारी प्राप्त नहीं की जा सकती है क्योंकि यह भविष्य की जानकारी का निहित रूप से उपयोग करता है। इसलिए, हमें स्क्रॉल औसत और devst का उपयोग करके z-score की गणना करनी चाहिए।

# 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

create_long_short_market_signals में, ट्रेडिंग सिग्नल बनाए जाते हैं. ये z-score के मूल्य के threshold से अधिक होने के द्वारा गणना की जाती हैं. जब z-score का निरपेक्ष मूल्य किसी अन्य (छोटे पैमाने पर) threshold से कम या बराबर हो, तो एक पक्की स्थिति संकेत दिया जाता है.

ऐसा करने के लिए, प्रत्येक k-string के लिए एक ट्रेडिंग रणनीति स्थापित करना आवश्यक है, जो कि खुला-बंद या समतल-बंद है. long_market और short_market दो परिभाषित चर हैं, जो बहु-आयामी और रिक्त-आयामी पदों को ट्रैक करने के लिए उपयोग किए जाते हैं. दुर्भाग्य से, वेक्टर विधि की तुलना में पुनरावर्ती रूप से प्रोग्रामिंग करना बहुत आसान है, इसलिए गणना धीमी है. यद्यपि 1 मिनट के k-string चित्र में प्रति CSV फ़ाइल लगभग 700,000 डेटा बिंदुओं की आवश्यकता होती है, फिर भी मेरे पुराने डेस्कटॉप पर यह अपेक्षाकृत तेज़ है!

एक पांडा डेटाफ्रेम (यह निश्चित रूप से एक असामान्य ऑपरेशन है) को इररेक्ट करने के लिए, iterrows विधि का उपयोग करना आवश्यक है, जो एक इररेक्टिव जनरेटर प्रदान करता हैः

# 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

इस चरण में, हमने जोड़े को अपडेट किया है ताकि वास्तविक बहु, खाली संकेतों को शामिल किया जा सके, जो हमें यह निर्धारित करने में सक्षम बनाता है कि क्या हमें पदों को खोलने की आवश्यकता है। अब हमें पदों के बाजार मूल्य को ट्रैक करने के लिए एक पोर्टफोलियो बनाने की आवश्यकता है। पहला कार्य बहु संकेतों और खाली संकेतों के साथ एक स्थिति कॉलम बनाना है। इसमें एक पंक्ति के तत्वों का एक सेट होगा, जिसमें "1", "0, -1", जिसमें 1 बहु पदों का प्रतिनिधित्व करता है, 0 कोई स्थिति नहीं है, और -1 खाली पदों का प्रतिनिधित्व करता है। sym1 और sym2 कॉलम प्रत्येक k लाइन के अंत में SPY और IWM की स्थिति का प्रतिनिधित्व करते हैं। बाजार मूल्य।

एक बार जब ईटीएफ का बाजार मूल्य बनाया जाता है, तो हम उन्हें एक-दूसरे से जोड़ते हैं और प्रत्येक k लाइन के अंत में कुल बाजार मूल्य उत्पन्न करते हैं। फिर इसे pct_change विधि के माध्यम से वापस मूल्य में परिवर्तित करते हैं। इसके बाद के कोड की पंक्तियों में गलत प्रविष्टियों (NaN और inf तत्वों) को हटा दिया जाता है, और अंत में पूर्ण हित वक्र की गणना की जाती है।

# 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

मुख्य फ़ंक्शन उन्हें एक साथ जोड़ता है. दिन के भीतर CSV फ़ाइल datadir पथ पर स्थित है. कृपया नीचे दिए गए कोड को अपने विशिष्ट निर्देशिका को इंगित करने के लिए संशोधित करें.

यह निर्धारित करने के लिए कि रणनीति लुकबैक चक्रों के प्रति संवेदनशील है, लुकबैक के प्रदर्शन के कुछ संकेतकों की गणना करना आवश्यक है। मैंने पोर्टफोलियो के लिए अंतिम कुल रिटर्न प्रतिशत को प्रदर्शन संकेतकों और लुकबैक सीमा[50,200] के रूप में चुना है, जो कि 10 है। आप नीचे दिए गए कोड में देख सकते हैं कि पिछले फ़ंक्शन को इस सीमा के भीतर शामिल किया गया है for लूप, अन्य सीमाएं अपरिवर्तित हैं। अंतिम कार्य matplotlib का उपयोग करके लुकबैक बनाम रिटर्न के लिए एक लाइन फ़ोल्डिंग ग्राफ बनाना हैः

# 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

अब आप lookbacks और returns का चार्ट देख सकते हैं. ध्यान दें कि lookback में एक ऐड-ऑन ऐड-ऑन का अधिकतम मान है, जो कि 110 k-strings के बराबर है. यदि हम lookbacks को returns से अलग देखते हैं, तो इसका कारण यह हैः

SPY-IWM रैखिक प्रतिगमन हेजिंग बनाम लुकबैक अवधि संवेदनशीलता विश्लेषण

यदि कोई ऊपर की ओर झुकाव वाला लाभ वक्र नहीं है, तो कोई भी पुनरीक्षण लेख अधूरा है! इसलिए, यदि आप समय के साथ संचयी लाभ रिटर्न की एक वक्र को चित्रित करना चाहते हैं, तो निम्न कोड का उपयोग कर सकते हैं। यह लुकबैक पैरामीटर अध्ययन से उत्पन्न अंतिम निवेश पोर्टफोलियो को चित्रित करेगा। इसलिए, आप जिस चार्ट को देखना चाहते हैं, उसके आधार पर लुकबैक चुनना आवश्यक है। यह चार्ट तुलना करने में मदद करने के लिए एक ही अवधि के SPY रिटर्न को भी चित्रित करता हैः

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

निम्नलिखित अधिकार-लाभ वक्र के चार्ट में 100 दिनों का लुकबैक अवधि हैः

img

SPY-IWM रैखिक प्रतिगमन हेजिंग बनाम लुकबैक अवधि संवेदनशीलता विश्लेषण

कृपया ध्यान दें कि वित्तीय संकट के दौरान, 2009 में SPY में भारी कमी आई थी। यह रणनीति इस समय भी अस्थिर थी। कृपया ध्यान दें कि पिछले साल SPY के प्रदर्शन में गिरावट आई थी क्योंकि इस अवधि के दौरान SPY का मजबूत ट्रेंडिंग स्वभाव स्टैंडर्ड 500 सूचकांक को दर्शाता था।

कृपया ध्यान दें कि z-score के लिए ब्याज दरों की गणना करते समय, हमें अभी भी आगे की ओर देखने वाले विचलन को ध्यान में रखने की आवश्यकता है। इसके अलावा, ये सभी गणनाएं लेनदेन की लागत के बिना की गई हैं। एक बार इन कारकों को ध्यान में रखते हुए, यह रणनीति निश्चित रूप से खराब प्रदर्शन करती है। प्रसंस्करण शुल्क और स्लाइड बिंदु अभी तक निश्चित नहीं हैं। इसके अलावा, यह रणनीति ईटीएफ के अंशों में व्यापार करने के लिए है, जो बहुत अवास्तविक है।

बाद के लेखों में, हम एक और अधिक जटिल घटना-संचालित बैकटेस्टर बनाएंगे जो इन कारकों को ध्यान में रखेगा, जिससे हम पूंजी वक्र और प्रदर्शन संकेतकों में अधिक आत्मविश्वास दिखाएंगे।


संबंधित

अधिक