本文为通过vectorbt(以下简称vbt)实现量化交易系列第一篇文章,通过使用vbt实现RSI策略从而熟悉其代码框架。
关于本文所使用数据的说明
由于vbt官方文档提供的入门案例使用的数据是通过其内置的yfinance包获取,在国内无法直接访问,故本文将原数据替换为更容易获取的国内数据。本文使用的数据是通过tushare获取的A股日线行情数据,具体参数如下图所示:
一、pandas常用函数
(1)drop()
函数语法:
DataFrame.drop(
labels=None,
axis=0,
index=None,columns=None,level=None, inplace=False, errors='raise')
参数说明:
labels: 待删除的行名or列名;
axis: 删除时所参考的轴,0为行,1为列;
index: 待删除的行名
columns: 待删除的列名
level:多级列表时使用,暂时不作说明
inplace: 布尔值,默认为False,若为False,则返回的是一个copy;若为True,返回的是删除相应数据后的数据
(1)set_index()
功能:指定数据表中的某列或指定某个数组列表为DataFrame行索引。
函数语法
DataFrame.set_index(keys, *, drop=True, append=False, inplace=False, verify_integrity=False)
参数说明:
key: 数据表中的某列/列标签列表/数组列表,需要设置为索引的列。
drop: 删除用作新索引的列,默认为True,删除。
append:是否将列附加到现有索引,默认为False,否。
inplace:表示当前操作是否对原数据重新,默认为False,否。
verify_integrity:检查新索引的副本。否则,请将检查推迟到必要时进行。将其设置为False,将提高该方法的性能,默认为False。
保留到小数点后两位
df.apply(lambda x:round(x,2))
二、RSI策略
(1)RSI计算步骤
确定时间周期:通常设定为14天,但也可以根据需要选择6、12、24等其他周期。
计算涨幅和跌幅:
涨幅:每日收盘价高于前一日收盘价的差额。
跌幅:每日收盘价低于前一日收盘价的差额(取正值)。
计算N日内涨幅之和与跌幅之和:将时间周期内每日的涨幅和跌幅分别累加。
计算RS值:用N日内涨幅之和的平均值除以N日内跌幅之和的平均值。
计算RSI值:将RS值代入RSI公式,得到RSI值。
(2)RSI取值范围及意义
RSI的取值范围在0到100之间,通常以30和70作为超卖和超买的标志线,或以20和80作为超卖和超买的标志线。具体解读如下:
当RSI值超过70或80时,市场处于超买状态,可能会有下跌的趋势。
当RSI值低于30或20时,市场处于超卖状态,可能会有上涨的趋势。
当RSI值在50附近时,代表多空力量均衡。
RSI策略的vectorbt实现方法
第一步:RSI.run()
只需要传入股票每日收盘价,窗口默认为14天。
rsi=vbt.RSI.run(price,window=14)
print('rsi:',rsi.rsi)
特别提示: 查看rsi的值请使用rsi.rsi
官方文档详见下图:
第二步:设置买入和卖出条件
rsi值下穿30线时买入,上穿70时卖出。下图为rsi_above和rsi_below函数参数。
#下穿rsi=30线时买入
entries=rsi.rsi_crossed_below(30)
print(entries)
#上穿rsi=70时卖出
exits=rsi.rsi_crossed_above(70)
print(exits)
函数名 | 说明 |
---|---|
rsi_above() | 开区间上穿特定值时,返回True,反之返回False |
rsi_closed_above() | 闭区间上穿特定值时,返回True,反之返回False |
rsi_crossed_above() | 刚好上穿特定值时,返回True,反之返回False |
rsi_below() | 开区间下穿特定值时,返回True,反之返回False |
rsi_closed_below() | 闭区间下穿特定值时,返回True,反之返回False |
rsi_crossed_below() | 刚好下穿特定值时,返回True,反之返回False |
第三步:模拟投资【vbt.Portfolio.from_signals()】
该函数通过买入和卖出信号来模拟投资。代码如下:
pf=vbt.Portfolio.from_signals(price,entries=entries,exits=exits,init_cash=10000)
该函数的参数详情如下:
Portfolio.from_signals(close,entries=None,exits=None,short_entries=None,short_exits=None,signal_func_nb=no_signal_func_nb,signal_args=(),size=None,size_type=None,price=None,fees=None,fixed_fees=None,slippage=None,min_size=None,max_size=None,size_granularity=None,reject_prob=None,lock_cash=None,allow_partial=None,raise_reject=None,log=None,accumulate=None,upon_long_conflict=None,upon_short_conflict=None,upon_dir_conflict=None,upon_opposite_entry=None,direction=None,val_price=None,open=None,high=None,low=None,sl_stop=None,sl_trail=None,tp_stop=None,stop_entry_price=None,stop_exit_price=None,upon_stop_exit=None,upon_stop_update=None,adjust_sl_func_nb=no_adjust_sl_func_nb,adjust_sl_args=(),adjust_tp_func_nb=no_adjust_tp_func_nb,adjust_tp_args=(),use_stops=None,init_cash=None,cash_sharing=None,call_seq=None,ffill_val_price=None,update_value=None,max_orders=None,max_logs=None,seed=None,group_by=None,broadcast_named_args=None,broadcast_kwargs=None,template_mapping=None,wrapper_kwargs=None,freq=None,attach_call_seq=None,**kwargs
)
第四步:查看回测结果
print(pf.stats().apply(lambda x:round(x,2)))
运行结果:
Start 20230804.00
End 20241220.00
Period 335.00
Start Value 10000.00
End Value 10041.23
Total Return [%] 0.41
Benchmark Return [%] -5.53
Max Gross Exposure [%] 100.00
Total Fees Paid 0.00
Max Drawdown [%] 21.89
Max Drawdown Duration 313.00
Total Trades 5.00
Total Closed Trades 5.00
Total Open Trades 0.00
Open Trade PnL 0.00
Win Rate [%] 60.00
Best Trade [%] 13.20
Worst Trade [%] -14.94
Avg Winning Trade [%] 7.12
Avg Losing Trade [%] -9.31
Avg Winning Trade Duration 25.67
Avg Losing Trade Duration 77.00
Profit Factor 1.02
Expectancy 8.25
dtype: float64
第五步:画图
pf.plot().show()
运行结果:
完整代码
import vectorbt as vbt
import pandas as pdpd=pd.read_csv("stock_data/000001.SZ.csv",names=['index','trade_date','open','close','low','high','vol','x','macd','dif','dea'])
#删除index列
pd=pd.drop(columns='index')
#pd.index=pd['trade_date']
#设置trade_date列为行索引
pd=pd.set_index('trade_date')
price=pd['close']#每日收盘价
#设计RSI策略
'''
窗口:14天
超卖:30
超买:70
'''
rsi=vbt.RSI.run(price,window=14)
print('rsi:',rsi.rsi)
#下穿rsi=30线时买入
entries=rsi.rsi_crossed_below(30)
print(entries)
#上穿rsi=70时卖出
exits=rsi.rsi_crossed_above(70)
print(exits)
#回测
pf=vbt.Portfolio.from_signals(price,entries=entries,exits=exits,init_cash=10000)
#
print(pf.stats().apply(lambda x:round(x,2)))
print(pf.total_return)
pf.plot().show()
参考文献
pandas drop()函数详解
tushare官网