您好,登錄后才能下訂單哦!
這篇文章主要介紹如何利用Python實(shí)現(xiàn)隨機(jī)相對(duì)強(qiáng)弱指數(shù)StochRSI,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
隨機(jī)相對(duì)強(qiáng)弱指數(shù)簡稱為StochRSI
,是一種技術(shù)分析指標(biāo),用于確定資產(chǎn)是否處于超買或超賣狀態(tài),也用于確定當(dāng)前市場的態(tài)勢(shì)。顧名思義,StochRSI
是標(biāo)準(zhǔn)相對(duì)強(qiáng)弱指數(shù)(RSI)的衍生,因此被視為是一種能夠衡量指數(shù)的指數(shù)。它是一種振蕩器,在中心線的上方和下方波動(dòng)。
StochRSI
最初是在1994年由Stanley Kroll
和Tushar Chande
撰寫的題為《The NewTechnical Trader》的書中描述。它經(jīng)常被股票交易者使用。
通過應(yīng)用隨機(jī)振蕩器生成公式,從標(biāo)準(zhǔn)RSI生成StochRSI
。其生成結(jié)果是單個(gè)數(shù)字評(píng)級(jí),圍繞中心線(0.5)在0-1的值域范圍內(nèi)上下擺動(dòng)。但是,StochRSI的修改版本將結(jié)果乘以100,因此該值是介于0和100之間而不是0和1之間。通常還會(huì)參考3天內(nèi)的簡單移動(dòng)平均線(SMA)以及StochRSI
趨勢(shì),作為信號(hào)線,旨在降低虛假信號(hào)交易的風(fēng)險(xiǎn)。
標(biāo)準(zhǔn)隨機(jī)震蕩指數(shù)公式取決于資產(chǎn)的收盤價(jià)以及設(shè)定周期內(nèi)的最高價(jià)和最低價(jià)。但是,當(dāng)使用公式計(jì)算StochRSI時(shí),它直接使用RSI數(shù)據(jù)(不考慮價(jià)格)。
Stoch RSI = (Current RSI - Lowest RSI)/(Highest RSI - Lowest RSI)
與標(biāo)準(zhǔn)RSI一樣,StochRSI
使用的最常見時(shí)間周期為14。StochRSI
計(jì)算中涉及的14個(gè)周期基于圖表時(shí)間范圍。因此,每日?qǐng)D表會(huì)顯示過去14天(K線圖),每小時(shí)圖表會(huì)顯示過去14小時(shí)生成的StochRSI
。
周期可以設(shè)置為幾天、幾小時(shí)甚至幾分鐘,并且它們的使用方式也因交易者而異(根據(jù)他們的情況和策略而定)。還可以向上或向下調(diào)整周期數(shù),以確定長期或短期趨勢(shì)。將周期值設(shè)置為20,是StochRSI指標(biāo)一個(gè)相當(dāng)受歡迎的選擇。
如上所述,某些StochRSI
圖表模式指定的范圍值為0到100而不是0到1。在這些圖表中,中心線為50而不是0.5。因此,通常在0.8處出現(xiàn)的超買信號(hào)將表示為80,而超賣信號(hào)表示為20而不是0.2。具有0-100設(shè)置的圖表可能看起來略有不同,但實(shí)際原理解釋是基本相同的。
StochRSI指數(shù)如果出現(xiàn)在其范圍的上限和下限附近,此時(shí)的意義是最重大的。因此,該指標(biāo)的主要用途是確定潛在的買入和賣出點(diǎn),以及價(jià)格發(fā)生的逆轉(zhuǎn)。因此,0.2或以下的數(shù)值,會(huì)表明資產(chǎn)可能發(fā)生超賣,而0.8或以上的數(shù)值則表明該資產(chǎn)可能會(huì)發(fā)生超買。
此外,更接近中心線的數(shù)值也可以為交易者提供有關(guān)市場趨勢(shì)的信息。例如,當(dāng)中心線作為支撐線并且StochRSI線穩(wěn)定移動(dòng)到0.5以上時(shí),尤其是數(shù)值趨近于0.8,則可能表明其繼續(xù)看漲或呈上升趨勢(shì)。同樣,當(dāng)數(shù)值始終低于0.5,趨近于0.2時(shí),則表明下跌或呈下降趨勢(shì)趨勢(shì)。
我們將通過 Python 中的回測來介紹 RSI
和 StochRSI
這兩種方法。
最常見的 StochRSI
策略基于均值回歸。與 RSI 一樣,StochRSI
通常使用 80 來表示做空的超買水平,使用 20 來表示要買入的超賣水平。此外,14 天的回顧和平滑期很常見。出于我們的目的,我們將堅(jiān)持使用這些標(biāo)準(zhǔn)值。
現(xiàn)在編寫代碼,讓我們?cè)?Python 中導(dǎo)入一些標(biāo)準(zhǔn)包。
import numpy as np import pandas as pd import matplotlib.pyplot as plt import yfinance as yf
接下來,我們將構(gòu)建一個(gè)函數(shù)來計(jì)算我們的指標(biāo)。我們將其稱為 calcStochRSI(),
它將依靠一些函數(shù)來計(jì)算 RSI 和隨機(jī)振蕩器,以獲得我們選擇的指標(biāo)。
def calcRSI(data, P=14): # Calculate gains and losses data['diff_close'] = data['Close'] - data['Close'].shift(1) data['gain'] = np.where(data['diff_close']>0, data['diff_close'], 0) data['loss'] = np.where(data['diff_close']<0, np.abs(data['diff_close']), 0) # Get initial values data[['init_avg_gain', 'init_avg_loss']] = data[ ['gain', 'loss']].rolling(P) # Calculate smoothed avg gains and losses for all t > P avg_gain = np.zeros(len(data)) avg_loss = np.zeros(len(data)) for i, _row in enumerate(data.iterrows()): row = _row[1] if i < P - 1: last_row = row.copy() continue elif i == P-1: avg_gain[i] += row['init_avg_gain'] avg_loss[i] += row['init_avg_loss'] else: avg_gain[i] += ((P - 1) * avg_gain[i] + row['gain']) / P avg_loss[i] += ((P - 1) * avg_loss[i] + row['loss']) / P last_row = row.copy() data['avg_gain'] = avg_gain data['avg_loss'] = avg_loss # Calculate RS and RSI data['RS'] = data['avg_gain'] / data['avg_loss'] data['RSI'] = 100 - 100 / (1 + data['RS']) return data def calcStochOscillator(data): data['low_N'] = data['RSI'].rolling(N).min() data['high_N'] = data['RSI'].rolling(N).max() data['StochRSI'] = 100 * (data['RSI'] - data['low_N']) / \ (data['high_N'] - data['low_N']) return data def calcStochRSI(data, P=14, N=14): data = calcRSI(data) data = calcStochOscillator(data) return data def calcReturns(df): # Helper function to avoid repeating too much code df['returns'] = df['Close'] / df['Close'].shift(1) df['log_returns'] = np.log(df['returns']) df['strat_returns'] = df['position'].shift(1) * df['returns'] df['strat_log_returns'] = df['position'].shift(1) * df['log_returns'] df['cum_returns'] = np.exp(df['log_returns'].cumsum()) - 1 df['strat_cum_returns'] = np.exp(df['strat_log_returns'].cumsum()) - 1 df['peak'] = df['cum_returns'].cummax() df['strat_peak'] = df['strat_cum_returns'].cummax() return df
有了這些功能,我們只需要為我們的策略構(gòu)建邏輯就可以了。還要注意,我們有一個(gè)名為 calcReturns
的輔助函數(shù),我們可以快速將其應(yīng)用于回測的結(jié)果以從中獲取所有返回值。
這意味著回歸模型將在 StochRSI
高于 80 時(shí)做空或賣出,并在低于 20 時(shí)買入。
def StochRSIReversionStrategy(data, P=14, N=14, short_level=80, buy_level=20, shorts=True): '''Buys when the StochRSI is oversold and sells when it's overbought''' df = calcStochRSI(data, P, N) df['position'] = np df['position'] = np.where(df['StochRSI']<buy_level, 1, df['position']) if shorts: df['position'] = np.where(df['StochRSI']>short_level, -1, df['position']) else: df['position'] = np.where(df['StochRSI']>short_level, 0, df['position']) df['position'] = df['position'].ffill() return calcReturns(df) table = pd.read_html('https://en.wikipedia.org/wiki/List_of_S%26P_500_companies') df = table[0] syms = df['Symbol'] # Sample symbols # ticker = np.random.choice(syms.values) ticker = "BSX" print(f"Ticker Symbol: {ticker}") start = '2000-01-01' end = '2020-12-31' # Get Data yfyfObj = yf.Ticker(ticker) data = yfObj.history(startstart=start, endend=end) data.drop(['Open', 'High', 'Low', 'Volume', 'Dividends', 'Stock Splits'], inplace=True, axis=1) # Run test df_rev = StochRSIReversionStrategy(data.copy()) # Plot results colors = plt.rcParams['axes.prop_cycle'].by_key()['color'] fig, ax = plt.subplots(2, figsize=(12, 8)) ax[0].plot(df_rev['strat_cum_returns']*100, label='Mean Reversion') ax[0].plot(df_rev['cum_returns']*100, label='Buy and Hold') ax[0].set_ylabel('Returns (%)') ax[0].set_title('Cumulative Returns for Mean Reversion and' + f' Buy and Hold Strategies for {ticker}') ax[0].legend(bbox_to_anchor=[1, 0.6]) ax[1].plot(df_rev['StochRSI'], label='StochRSI', linewidth=0.5) ax[1].plot(df_rev['RSI'], label='RSI', linewidth=1) ax[1].axhline(80, label='Over Bought', color=colors[1], linestyle=':') ax[1].axhline(20, label='Over Sold', color=colors[2], linestyle=':') ax[1].axhline(50, label='Centerline', color='k', linestyle=':') ax[1].set_ylabel('Stochastic RSI') ax[1].set_xlabel('Date') ax[1].set_title(f'Stochastic RSI for {ticker}') ax[1].legend(bbox_to_anchor=[1, 0.75]) plt.tight_layout() plt.show()
在我們研究的 21 年期間,均值回歸策略擊敗了Boston Scientific(BSX
)的買入和持有策略,回報(bào)率為 28 倍,而后者為 2 倍。
在第二個(gè)圖中顯示了 StochRSI
和一些關(guān)鍵指標(biāo)。我還添加了 RSI 以與更不穩(wěn)定的 StochRSI
進(jìn)行比較。這導(dǎo)致交易頻繁,如果您的賬戶較小且交易成本相對(duì)較高,這可能會(huì)嚴(yán)重影響您的實(shí)際回報(bào)。我們只是在一個(gè)工具上運(yùn)行它,所以最終進(jìn)行了 443 筆交易,或者每 12 天交易一次,這看起來并不多。但是,如果我們要使用該指標(biāo)管理適當(dāng)?shù)墓ぞ呓M合并頻繁進(jìn)行交易,我們每天可能會(huì)進(jìn)出多筆交易,交易成本會(huì)變得很高。
# Get trades diff = df_rev['position'].diff().dropna() trade_idx = diff.index[np.where(diff!=0)] fig, ax = plt.subplots(figsize=(12, 8)) ax.plot(df_rev['Close'], linewidth=1, label=f'{ticker}') ax.scatter(trade_idx, df_rev[trade_idx]['Close'], c=colors[1], marker='^', label='Trade') ax.set_ylabel('Price') ax.set_title(f'{ticker} Price Chart and Trades for' + 'StochRSI Mean Reversion Strategy') ax.legend() plt.show()
要查看整體策略的一些關(guān)鍵指標(biāo),讓我們看看使用以下 getStratStats
函數(shù)。
def getStratStats(log_returns: pd.Series, risk_free_rate: float = 0.02): stats = {} # Total Returns stats['tot_returns'] = np.exp(log_returns.sum()) - 1 # Mean Annual Returns stats['annual_returns'] = np.exp(log_returns.mean() * 252) - 1 # Annual Volatility stats['annual_volatility'] = log_returns * np.sqrt(252) # Sortino Ratio annualized_downside = log_returns.loc[log_returns<0].std() * np.sqrt(252) stats['sortino_ratio'] = (stats['annual_returns'] - risk_free_rate) \ / annualized_downside # Sharpe Ratio stats['sharpe_ratio'] = (stats['annual_returns'] - risk_free_rate) \ / stats['annual_volatility'] # Max Drawdown cum_returns = log_returns.cumsum() - 1 peak = cum_returns.cummax() drawdown = peak - cum_returns stats['max_drawdown'] = drawdown.max() # Max Drawdown Duration strat_dd = drawdown[drawdown==0] strat_ddstrat_dd_diff = strat_dd.index[1:] - strat_dd.index[:-1] strat_dd_days = strat_dd_diff.map(lambda x: x.days) strat_dd_days = np.hstack([strat_dd_days, (drawdown.index[-1] - strat_dd.index[-1]).days]) stats['max_drawdown_duration'] = strat_dd_days.max() return stats rev_stats = getStratStats(df_rev['strat_log_returns']) bh_stats = getStratStats(df_rev['log_returns']) pd.concat([pd.DataFrame(rev_stats, index=['Mean Reversion']), pd.DataFrame(bh_stats, index=['Buy and Hold'])])
在這里,我們看到該策略的回報(bào)率為 28 倍,而基礎(chǔ)資產(chǎn)的年度波動(dòng)率大致相同。此外,根據(jù) Sortino
和 Sharpe
Ratios 衡量,我們有更好的風(fēng)險(xiǎn)調(diào)整回報(bào)。
在 2020 年的新冠疫情中,我們確實(shí)看到了均值回歸策略的潛在問題之一。該策略的總回報(bào)大幅下降,因?yàn)樵摬呗缘亩ㄎ皇窍蛏匣貧w,但市場繼續(xù)低迷,該模型只是保持不變 . 它恢復(fù)了其中的一部分,但在這次測試中從未達(dá)到過疫情之前的高點(diǎn)。正確使用止損有助于限制這些巨大的損失,并有可能增加整體回報(bào)。
我們之前提到的另一個(gè)基本策略是使用 StochRSI
作為動(dòng)量指標(biāo)。當(dāng)指標(biāo)穿過中心線時(shí),我們會(huì)根據(jù)其方向買入或做空股票。
def StochRSIMomentumStrategy(data, P=14, N=14, centerline=50, shorts=True): ''' Buys when the StochRSI moves above the centerline, sells when it moves below ''' df = calcStochRSI(data, P) df['position'] = np.nan df['position'] = np.where(df['StochRSI']>50, 1, df['position']) if shorts: df['position'] = np.where(df['StochRSI']<50, -1, df['position']) else: df['position'] = np.where(df['StochRSI']<50, 0, df['position']) df['position'] = df['position'].ffill() return calcReturns(df)
運(yùn)行我們的回測:
# Run test df_mom = StochRSIMomentumStrategy(data.copy()) # Plot results colors = plt.rcParams['axes.prop_cycle'].by_key()['color'] fig, ax = plt.subplots(2, figsize=(12, 8)) ax[0].plot(df_mom['strat_cum_returns']*100, label='Momentum') ax[0].plot(df_mom['cum_returns']*100, label='Buy and Hold') ax[0].set_ylabel('Returns (%)') ax[0].set_title('Cumulative Returns for Momentum and' + f' Buy and Hold Strategies for {ticker}') ax[0].legend(bbox_to_anchor=[1, 0.6]) ax[1].plot(df_mom['StochRSI'], label='StochRSI', linewidth=0.5) ax[1].plot(df_mom['RSI'], label='RSI', linewidth=1) ax[1].axhline(50, label='Centerline', color='k', linestyle=':') ax[1].set_ylabel('Stochastic RSI') ax[1].set_xlabel('Date') ax[1].set_title(f'Stochastic RSI for {ticker}') ax[1].legend(bbox_to_anchor=[1, 0.75]) plt.tight_layout() plt.show()
在這種情況下,我們的動(dòng)量策略表現(xiàn)非常糟糕,在我們假設(shè)的時(shí)間段內(nèi)幾乎損失了我們所有的初始投資。
查看我們策略的統(tǒng)計(jì)數(shù)據(jù),該模型的唯一優(yōu)勢(shì)是比買入并持有方法的回撤時(shí)間略短。
mom_stats = getStratStats(df_mom['strat_log_returns']) bh_stats = getStratStats(df_mom['log_returns']) pd.concat([pd.DataFrame(mom_stats, index=['Momentum']), pd.DataFrame(rev_stats, index=['Mean Reversion']), pd.DataFrame(bh_stats, index=['Buy and Hold'])])
這并不意味著StochRSI
不適合此類應(yīng)用。一次糟糕的回測并不意味著該策略毫無價(jià)值。相反,一個(gè)很好的回測并不意味著你有一些你應(yīng)該立即開始交易的東西。我們需要與其他指標(biāo)結(jié)合使用以改善結(jié)果。
以上是“如何利用Python實(shí)現(xiàn)隨機(jī)相對(duì)強(qiáng)弱指數(shù)StochRSI”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。