Python中文社区(ID:python-china)随机相对强弱指数,简称StochRSI,是一种用于判断资产是否超买或超卖的技术分析指标,也用于确定市场的当前状态。顾名思义,StochRSI是标准相对强弱指数(RSI)的衍生产品,因此被视为衡量指数的指数。它是一个振荡器,在中心线上方和下方波动。StochRSI在1994年由StanleyKroll和TusharChande撰写的名为《The NewTechnical Trader》的书中首次被描述。它经常被股票交易者使用。StochRSI如何运作?通过应用随机震荡指标生成公式从标准RSI生成StochRSI。结果是一个单一的数字评级,围绕中心线(0.5)在0-1的范围内上下摆动。然而,StochRSI的修改版本将结果乘以100,因此该值介于0和100之间,而不是0和1。通常也参考3天内的简单移动平均线(SMA)和StochRSI趋势作为信号线,旨在降低虚假信号交易的风险。标准的随机震荡指标公式基于资产的收盘价以及一段时间内的最高价和最低价。但是,在使用公式计算StochRSI时,它直接使用RSI数据(不考虑价格)。StochRSI=(当前RSI-最低RSI)/(最高RSI-最低RSI)与标准RSI一样,StochRSI使用的最常见时间范围是14。StochRSI计算中涉及的14个周期基于图表时间范围。因此,日线图将显示过去14天(烛台图),小时线图将显示过去14小时内生成的StochRSI。周期可以设置为几天、几小时甚至几分钟,它们的使用方式因交易者而异(取决于他们的情况和策略)。还可以向上或向下调整周期以确定长期或短期趋势。将周期值设置为20是StochRSI指标的一个相当流行的选择。如上所述,一些StochRSI图表模式指定范围值是0到100而不是0到1。在这些图表中,中心线是50而不是0.5。因此,通常出现在0.8的超买信号将表示为80,而超卖信号将表示为20而不是0.2。0-100设置的图表可能看起来略有不同,但实际原理解释基本相同。如何使用StochRSI?当StochRSI指数出现在其范围的上限和下限附近时,它最为重要。因此,该指标的主要用途是识别潜在的买卖点和价格反转。因此,0.2或以下的值表示资产可能超卖,而0.8或以上的值表示资产可能超买。此外,接近中线的数值也可以为交易者提供有关市场趋势的信息。例如,当中线作为支撑线,StochRSI线在0.5上方稳定运行,尤其是当该值接近0.8时,可能表明牛市或上涨趋势的延续。同样,当该值始终低于0.5并趋于0.2时,表示下降或下降趋势。我们将通过在Python中进行回测来介绍RSI和StochRSI两种方法。基于均值回归的StochRSI策略最常见的StochRSI策略是基于均值回归的。与RSI一样,StochRSI通常使用80表示超买水平以做空,使用20表示超卖水平以买入。此外,14天的回溯期和平滑期也很常见。出于我们的目的,我们将坚持使用这些标准值。现在开始编码,让我们在Python中导入一些标准包。importnumpyasnpimportpandasasspdimportmatplotlib.pyplotaspltimportyfinanceeasyf接下来,我们将构建一个函数来计算我们的指标。我们称它为calcStochRSI(),它将依赖一些函数来计算我们选择的指标的RSI和随机震荡指标。defcalcRSI(data,P=14):#Calculategainsandlossesdata['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)#Getinitialvaluesdata[['init_avg_gain','init_avg_loss']]=data[['gain','loss']].rolling(P)#Calculatesmoothedavggainsandlossesforallt>Pavg_gain=np.zeros(len(data))avg_loss=np.zeros(len(数据))fori,_rowinenumerate(data.iterrows()):row=_row[1]ifishort_level,-1,df['position'])else:df['position']=np.where(df['StochRSI']>short_level,0,df['position'])df['position']=df['位置'].ffill()returncalcReturns(df)table=pd.read_html('https://en.wikipedia.org/wiki/List_of_S%26P_500_companies')df=table[0]syms=df['Symbol']#Samplesymbols#ticker=np.random.choice(syms.values)ticker="BSX"print(f"TickerSymbol:{ticker}")start='2000-01-01'end='2020-12-31'#GetDatayfyfObj=yf.Ticker(ticker)data=yfObj.history(startstart=start,endend=end)data.drop(['Open','High','Low','Volume','Dividends','StockSplits'],inplace=True,axis=1)#Runtestdf_rev=StochRSIReversionStrategy(data.copy())#Plotresultscolors=plt.rcParams['axes.prop_cycle'].by_key()['color']图,ax=plt.subplots(2,figsize=(12,8))ax[0].plot(df_rev['strat_cum_returns']*100,label='MeanReversion')ax[0].plot(df_rev['cum_returns']*100,label='BuyandHold')ax[0].set_ylabel('回报率(%)')ax[0].set_title('CumulativeReturnsforMeanReversionand'+f'BuyandHoldStrategiesfor{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='OverBought',color=colors[1],linestyle=':')ax[1].axhline(20,label='OverSold',color=colors[2],linestyle=':')ax[1].axhline(50,label='中心线',color='k',linestyle=':')ax[1].set_ylabel('StochasticRSI')ax[1].set_xlabel('Date')ax[1].set_title(f'StochasticRSIfor{ticker}')ax[1].legend(bbox_to_anchor=[1,0.75])plt.tight_layout()plt.show()在我们研究的21年期间,均值回归策略击败了波士顿科学(BSX)买入并持有策略,回报是28倍,而后者是2倍。第二张图显示了StochRSI和一些关键指标。我还添加了RSI以与更不稳定的StochRSI进行比较。这导致交易频繁,如果您的账户较小且交易成本相对较高,会严重影响您的实际收益。我们只是在一种工具上运行它,所以我们最终得到443笔交易,或者每12天一次交易,这看起来并不多。然而,如果我们要使用该指标来管理适当的工具组合并频繁交易,我们可能每天进场和退场多笔交易,交易成本会变得很高。#Gettradesdiff=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}PriceChartandTradesfor'+'StochRSIMeanReversionStrategy')ax.legend()plt.show()查看整体的一些关键指标strategy,让我们看看使用下面的getStratStats函数。defgetStratStats(log_returns:pd.Series,risk_free_rate:float=0.02):stats={}#TotalReturnsstats['tot_returns']=np.exp(log_returns.sum())-1#MeanAnnualReturnsstats['annual_returns']=np.exp(log_returns.mean()*252)-1#AnnualVolatilitystats['annual_volatility']=log_returns*np.sqrt(252)#SortinoRatioannualized_downside=log_returns.loc[log_returns<0].std()*np.sqrt(252)stats['sortino_ratio']=(stats['annual_returns']-risk_free_rate)\/annualized_downside#SharpeRatiostats['sharpe_ratio']=(stats['annual_returns']-risk_free_rate)\/stats['annual_volatility']#MaxDrawdowncum_returns=log_returns。cumsum()-1peak=cum_returns.cummax()drawdown=peak-cum_returnsstats['max_drawdown']=drawdown.max()#MaxDrawdownDurationstrat_dd=drawdown[drawdown==0]strat_ddstrat_dd_diff=strat_dd.index[1:]-strat_dd.index[:-1]strat_dd_days=strat_dd_diff.map(lambdax: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()returnstatsrev_stats=getStratStats(df_rev['strat_log_returns'])bh_stats=getStratStats(df_rev['log_returns'])pd.concat([pd.DataFrame(rev_stats,index=['MeanReversion']),pd.DataFrame(bh_stats,index=['BuyandHold'])])这里我们看到该策略的回报率为28倍,而标的资产的年化波动率大致相同。此外,根据Sortino和夏普比率的风险调整回报率衡量,我们有更好的表现。在2020年COVID-19大流行中,我们确实看到了均值回归策略的潜在问题之一。该策略的总回报显着下降,因为该策略定位为回升,但市场继续低迷,模型保持不变。它恢复了一些,但从未在这个测试点达到COVID之前的高点。正确使用止损可以帮助限制这些巨大的损失并可能增加整体回报。StochRSI和动量策略我们之前提到的另一个基本策略是使用StochRSI作为动量指标。当指标穿过中线时,我们根据其方向买入或做空股票。defStochRSIMomentumStrategy(data,P=14,N=14,centerline=50,shorts=True):'''BuyswhentheStochRSImovesabovethecenterline,sellswhenitmovesbelow'''df=calcStochRSI(data,P)df['position']=np.nandf['position']=np.where(df['StochRSI']>50,1,df['position'])ifshorts: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()returncalcReturns(df)运行我们的回测:#Runtestdf_mom=StochRSIMomentumStrategy(data.copy())#Plotresultscolors=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='BuyandHold')ax[0].set_ylabel('回报率(%)')ax[0].set_title('CumulativeReturnsforMomentumand'+f'BuyandHoldStrategiesfor{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('StochasticRSI')ax[1].set_xlabel('日期')ax[1].set_title(f'StochasticRSIfor{ticker}')ax[1].legend(bbox_to_anchor=[1,0.75])plt.tight_layout()plt.show()在这种情况下,我们的动量策略表现得很糟糕,在我们假设的回撤时间略短的时间段内损失了几乎所有的初始投资。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=['MeanReversion']),pd.DataFrame(bh_stats,index=['BuyandHold'])])这并不意味着StochRSI不适合此类应用。糟糕的回测并不意味着该策略毫无价值。相反,良好的回测并不意味着您拥有应该立即开始交易的东西。我们需要将其与其他指标结合起来以改善结果。