量化投资第四次作业——ETF策略
- *一点说明:该作业是寒假在家写的,所以完全敷衍草草了事,内容也是答辩…
我采用的是$ETF$股指期现套利策略。运行demo结果如下:
我发现代码并没有成功触发交易,其原因是期货价格始终没有达到触发交易的上下界,于是我考虑对上下界进行修改。通过查阅相关资料,我得出了上下界的确定函数:
$$
wtlxx=\frac{S_t(e^{(r-q)(T-t)}-2\beta-\sigma)}{1+2\alpha} \
wtlsx=\frac{S_t(e^{(r-q)(T-t)}+2\beta+\sigma)}{1-2\alpha}
$$
其中变量和参数的解释如下:
$S_t$表示现货股票在当前$t$时刻的价格。
$r$为无风险收益率。我粗略地选取央行一年期存款基准利率 $1.8%$。
$q$为股息收益率。由于我国股息分红很少,因此此处假设$q=0$
$\alpha$为期货交易成本。包含交易手续费,根据中国金融交易所发布调整交易手续费通知,因此这里取调整后的费率 $0.23%$。
$\beta$为现货$ETF$的交易成本。现如今由于$ETF$没有印花税等税收,故不考虑税率,只考虑佣金,假设管理手续费大概为成交金额的$0.2%$,托管费$0.1%$,合在一起约为$0.3%$。
$\sigma$为跟踪误差,一般来说跟踪误差不是一个固定值,但由于计算有些麻烦且对结果影响不大,故采用固定值。参考《上证 50 股指期货期现套利策略研究》中的统计结果,取 $\sigma=0.1496%$。
相关代码如下:
1 2 3 4 5 6 7 8 9 10 11
| a=instruments(context.contract).de_listed_date.date() b=context.now.date() delta=a-b r = 0.018/180 q = 0 beta = 0.003 sigma = 0.001496 alpha = 0.0023 wtlxx = (contract_price[0]*(math.exp((r-q)*delta.days)-2*beta-sigma))/(2*alpha+1) wtlsx = (contract_price[0]*(math.exp((r-q)*delta.days)+2*beta+sigma))/(-2*alpha+1)
|
由此确定新的上下界,从而进行套利。
在确定了新的上下界之后,我还添加了止损信号。在进行套利交易时,对于投资者来说,是常常伴有一定的风险的。而止损信号的目的就是把控交易风险,减少投资者的损失。具体来说,在交易活动中,收益一旦出现负收益,那么就存在一定的亏损,为了不让投资者的损失继续扩大恶化,投资者就要划定一个信号,一旦损失触碰到这个信号,投资者就要平仓停止交易,把损失控制在可控可接受的范围之内。当价格达到止损点时,自动进行平仓止损。
相关代码如下:
1 2 3 4 5 6 7 8 9
| elif (contract_price[0] < wtlsx or (1000*sz50Etf_price[0]-contract_price[0])/contract_price[0]>0.12) and context.count == 1: youngquant.api.buy_close(context.contract,contract_xdss1) order_target_value('510050.XSHG', 0) context.count = 0 elif (contract_price[0] > wtlsx or (1000*sz50Etf_price[0]-contract_price[0])/contract_price[0]>0.12) and context.count == -1: youngquant.api.sell_close(context.contract,contract_xdss2) youngquant.api.order_shares('510050.XSHG',sz50Etf_xdgs) context.count = 0
|
进行上述操作后,运行代码得到如下结果:
可以看出,在优化之后策略的年化收益率有了显著的提升,最大回撤也有了一定的降低,可见优化起到了一定的作用。
完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
| import youngquant from youngquant.api import * from youngquant import exec_strategy import yqdata.services as yq import datetime import warnings import math warnings.filterwarnings('ignore') config = { "base": { "start_date": "2020-05-01", "end_date": "2020-07-31", "frequency": "1d", "benchmark": "000300.XSHG", "plot": True, "accounts": { "stock": 10000000, "FUTURE": 10000000, }, "init_positions":'510050.XSHG:10000000' }, "extra": { "log_level": "error", }, "mod": { "sys_analyser": { "enabled": True, "plot": True }, "mongodb": { "enabled": True, } } }
def update_contract(context, bar_dict): if instruments(context.contract).de_listed_date.strftime("%Y-%m-%d") == context.now.date().strftime("%Y-%m-%d"): year = context.now.strftime("%Y%m%d")[2:4] a = context.now.strftime("%Y%m%d")[4:6] if a[0]=='0': month='0'+str(int(a[1])+1) elif a[0]=='1' and a[1]=='2': month='01' else: month='1'+str(int(a[1])+1) context.contract = '%s%s%s'%(context.symbol,year,month)
def initialize(context): context.count = 0 context.symbol = 'IH' context.contract = 'IH2002' scheduler.run_daily(update_contract, time_rule='before_trading') def handle_data(context, bar_dict): contract_price = youngquant.api.history_bars(context.contract,1,'1d','close') sz50Etf_price = youngquant.api.history_bars('510050.XSHG',1,'1d','close') a=instruments(context.contract).de_listed_date.date() b=context.now.date() delta=a-b r = 0.018/180 q = 0 beta = 0.003 sigma = 0.001496 alpha = 0.0023 wtlxx = (contract_price[0]*(math.exp((r-q)*delta.days)-2*beta-sigma))/(2*alpha+1) wtlsx = (contract_price[0]*(math.exp((r-q)*delta.days)+2*beta+sigma))/(-2*alpha+1)
if contract_price[0] > wtlsx and context.count == 0: global contract_xdss1 contract_xdss1=int(10000000/(contract_price[0]*instruments(context.contract).contract_multiplier)) youngquant.api.sell_open(context.contract,contract_xdss1) contract_bzj=contract_price[0]*instruments(context.contract).contract_multiplier*contract_xdss1*instruments(context.contract).margin_rate sz50Etf_xdss=int((10000000-contract_bzj)/(sz50Etf_price[0]*100)) order_lots('510050.XSHG', sz50Etf_xdss) context.count = 1 elif contract_price[0] < wtlxx and context.count == 0: global sz50Etf_xdgs sz50Etf_xdgs=int(10000000/(sz50Etf_price[0])) sz50Etf_value=sz50Etf_xdgs*sz50Etf_price[0] youngquant.api.order_shares('510050.XSHG',-sz50Etf_xdgs) global contract_xdss2 contract_xdss2=int(sz50Etf_value/(contract_price[0]*instruments(context.contract).contract_multiplier)) youngquant.api.buy_open(context.contract,contract_xdss2) context.count = -1 elif (contract_price[0] < wtlsx or (1000*sz50Etf_price[0]-contract_price[0])/contract_price[0]>0.12) and context.count == 1: youngquant.api.buy_close(context.contract,contract_xdss1) order_target_value('510050.XSHG', 0) context.count = 0 elif (contract_price[0] > wtlsx or (1000*sz50Etf_price[0]-contract_price[0])/contract_price[0]>0.12) and context.count == -1: youngquant.api.sell_close(context.contract,contract_xdss2) youngquant.api.order_shares('510050.XSHG',sz50Etf_xdgs) context.count = 0 else: pass exec_strategy(initialize=initialize, handle_data=handle_data, config=config)
|