##研究对象:
[【量化课堂】股指期货对冲策略][1]
##研究目的:
1. 寻求一套通用的对冲例程,以期望用于各种资产组合。
2. 学习大神编程经验,提高自己的python语言水平。
##研究过程:
###一、重写原版
1. 发现get_next_month_future函数逻辑有些乱,改写了该函数。
```
# 进入本月第三周即切换到下月合约,而不等第三周的周五本月合约结束
def get_next_month_future(context, symbol):
dt = context.current_dt
month_begin_day = datetime.date(dt.year, dt.month, 1).isoweekday() # 本月1号是星期几(1-7)
third_monday_date = 16 - month_begin_day + 7*(month_begin_day>5) #本月的第三个星期一是几号
# 如果今天没过第三个星期一
if dt.day < third_monday_date:
next_dt = dt #本月合约
else:
next_dt = dt + relativedelta(months=1) #切换至下月合约
year = str(next_dt.year)[2:]
month = ('0' + str(next_dt.month))[-2:]
return (symbol+year+month+'.CCFX')
```
2. 重写compute_hedge_ratio函数
计算资产组合及沪深300指数的日收益率:
原算法:
```
# 取股票在样本时间内的价格
prices = history(g.yb, '1d', 'close', in_position_stocks)
# 取指数在样本时间内的价格
index_prices = list(attribute_history('000300.XSHG', g.yb, '1d', 'close').close)
# 计算股票在样本时间内的日收益率
rets = [(prices.iloc[i+1,:]-prices.iloc[i,:])/prices.iloc[i,:] for i in range(g.yb-1)]
# 计算日收益率平均
mean_rets = [np.mean(x) for x in rets]
# 计算指数的日收益率
index_rets = [(y-x)/x for (x,y) in zip(index_prices[:-1],index_prices[1:])]
```
修改后简洁地表达:
```
prices = history(g.yb, '1d', 'close', in_position_stocks)
index_prices = attribute_history('000300.XSHG', g.yb, '1d', 'close')
# prices 行:日期,列:各只股票 =>pct_change():dataframe, 结构不变,值为日收益率=>[1:] drop first row
# =>mean(axis=1)横向平均,Series=>.values:array
mean_rets = prices.pct_change()[1:].mean(axis=1).values
# pct_change():dataframe, 结构不变,值为日收益率=>[1:] drop first row=>.close:Series =>values:array
index_rets = index_prices.pct_change()[1:].close.values
```
并对beta计算过程进行了注释:
```
# 计算组合和指数的协方差矩阵cov_mat
# Rp | Rm
# Rp Rp.Var | cov(Rp,Rm)
# Rm cov(Rm,Rp) | Rm.Var
cov_mat = np.cov(mean_rets, index_rets)
# 计算组合的系统性风险beta
beta = cov_mat[0,1]/cov_mat[1,1]
'''
# 另一种算法
index_rets = sm.add_constant(index_rets) # 常数用来拟合alpha,系数用来拟合beta
model = regression.linear_model.OLS(mean_rets, index_rets).fit() #线性回归,OLS普通最小二乘法ordinary least square
alpha, beta = model.params[0], model.params[1]
'''
```
本函数返回对冲比例和beta值,其中对冲比例hedge_ratio的表达式`1+beta*g.futures_margin_rate+beta/5`
这个对冲比例hedge_ratio怎么来的?有什么用?请看下面的“研究”
3. 重写initialize函数:
原表达:
```
set_subportfolios([SubPortfolioConfig(cash=context.portfolio.starting_cash*(1/1.3) ,type='stock'),SubPortfolioConfig(cash=context.portfolio.starting_cash*0.3/1.3,type='index_futures')])
```
现表达:
```
# 分仓
stock_cash = np.round(context.portfolio.starting_cash*(1/1.3),0)
future_cash = context.portfolio.starting_cash - stock_cash
set_subportfolios(
[
SubPortfolioConfig(cash=stock_cash, type='stock'),
SubPortfolioConfig(cash=future_cash,type='index_futures')
]
)
```
4. 重写before_trading_start函数
注释掉了如下一条语句,并将之移到compute_signals函数里:
```
g.all_stocks = set_feasible_stocks(get_all_securities(['stock']).index,g.yb,context)
```
理由是:每天计算g.all_stocks计算量极大。实际上只有在compute_signals才能用上g.all_stocks,
而compute_signals函数20个交易日才执行一次(g.tc=20 #调仓频率),所以每天都计算g.all_stocks是无用功。
5. 重写compute_signals函数:
原写法:
```
if g.t%g.tc==0:
# 获取所有股票的财务数据总负债和总资产
q = query(balance.code, balance.total_liability, balance.total_assets).filter(balance.code.in_(g.all_stocks))
data = get_fundamentals(q)
# 计算资产负债比
data['ALR'] = data['total_liability']/data['total_assets']
# 资产负债比从大到小排列
data = data.sort('ALR', ascending=False)
# 输出最靠前的 3%
return list(data.code)[:int(float(len(g.all_stocks))*g.percentile)]
```
修改后:
```
if g.t%g.tc==0:
# 获取可行股票池
all_stocks = set_feasible_stocks(get_all_securities(['stock']).index,g.yb,context)
# 获取所有股票的财务数据总负债和总资产
q = query(
balance.code, balance.total_liability, balance.total_assets,
(balance.total_liability/balance.total_assets).label('ALR')
).filter(
balance.code.in_(all_stocks)
).order_by(
(balance.total_liability/balance.total_assets).desc() #按资产负债率降序
)
data = get_fundamentals(q)
stock_list = data['code'].tolist()
# 输出最靠前的 3%
return stock_list[:int(len(all_stocks)*g.percentile)]
```
修改后的代码试图用一条sql查询语句找出想要的答案。
修改时发现了一个问题,就是“资产负债率”按大到小排列?
一般地,资产负债率越高,表明该企业已经陷入或即将陷入财务困境,这样的股票表现能好么?
为忠实再现原作,先不改,保持降序。策略不是重点,重点是对冲效果。
6. 修改rebalance函数
原作183-186行
```
for stock in g.in_position_stocks:
order_target_value(stock, stock_value/len(g.in_position_stocks), pindex=0)
for stock in g.in_position_stocks:
order_target_value(stock, stock_value/len(g.in_position_stocks), pindex=0)
```
明显重复了,但却不是疏忽。原作想做什么?是要保持等权!
要保持等权,就得削高填低,第一遍贴权的没有钱买,因为超权的还可能没有卖出钱来,
所以第一遍把高的削了,第二遍就有钱把低洼地带填平了。效果是这个效果,但得仔细揣摩才能读懂。
改改,虽然效果一样,语句还多了不少,但浅显些。同时增加了过滤停牌股票的功能:
```
curr_data = get_current_data()
target_stocks = [stock for stock in g.in_position_stocks if not curr_data[stock].paused ] #过滤掉今日停牌的
per_value = stock_value/len(g.in_position_stocks) #每只股票应该达到的权值
over_weight_list = [stock for stock in target_stocks if \
context.subportfolios[0].long_positions[stock].value > per_value] #现持仓中超权的
under_weight_list = [stock for stock in target_stocks if \
stock not in over_weight_list] #剩余的,就是贴权的,应该补权
for stock in over_weight_list: # 超权的先减仓,削高
order_target_value(stock, per_value, pindex=0)
for stock in under_weight_list: # 贴权的再加仓,填低
order_target_value(stock, per_value, pindex=0)
```
为更精准地对冲,从获取沪深300价格改为获取股指期货价格,原因:
(1)股指期货与现货相比,存在升水或贴水
(2)对冲的不限于IF,也可能是IH,IC
```
# 获取沪深300价格
# index_price = attribute_history('000300.XSHG',1, '1d', 'close').close.iloc[0]
# log.info('HS300 index_price: %.2f' % index_price)
# 获取期货指数价格
index_price = attribute_history(current_future, 1, '1d', 'close').close.iloc[0]
log.info('Index futures: %s, Price: %.2f' % (current_future, index_price))
```
rebalance函数中还有几处费解的地方,如何去理解,请看下面的“研究”
另外,对rebalance函数进行了详尽的log,以解释各个变量的含义,并可以借此检验是否达到对冲要求。
一个教学策略,我花了2天的时间来学习,受益匪浅。原作水平很高,尤其是rebalance函数,写的很精彩!
回测的时候发现,起始资金2个亿,有些多了,即使一次买N多只股票,依旧有超出该股全天成交量的。故把起始资金调整为2千万。
资金量小了,股指期货对冲的精度就下降了,所以,回测的结果与原作会有不同。
先改到这里,看看回测效果再说。嗯,回测速度快了很多!
[1]: https://www.joinquant.com/post/3793?tag=algorithm
对冲真的很有效么?改改策略,看看。
修改compute_signals:
1)资产负债率改为升序排列,即选那些“优质”股,收益应该会有显著的提升。
2)过滤掉ST股
收益提高了,但回撤大大增加了。最大回撤 27.826%(2014-10-28,2015-01-05)!好吧,这不是对冲的错,那段时间大盘是急速上涨的,策略所选股票不涨或者下跌,却要同时承受做空股指期货的损失,双重损失啊。
不过可以看出来,对冲的效果没有选“资产负债率”高的原策略那么美了。最大回撤 27.826%(2014-10-28,2015-01-05)不是大盘下跌惹的祸,可以原谅,可股灾V1.0~V3.0收益曲线都是急速下滑,好像对冲效果没有达到理想水平。
2017-01-09
看看不对冲,不带护具地luo奔:策略“ALR"升序,过滤ST,即跟上一个回测完全相同。唯一差别是2亿资金全仓股票,不对冲!
收益更高!曾经达到过400%,可回撤太吓人了。最大回撤 56.092%(2015-06-12,2015-09-15)!股灾1.0,2.0的苦果全部吃下。
2017-01-09
看来对冲是有必要的,问题是如何能更好地对冲,接着继续研究对冲。
2017-01-09
按照修改后的代码,重新测了一些“ALR因子—低资产负债率,过滤ST"的策略对冲效果。
最大回撤 27.584%(2014-10-28,2015-01-05)。这段时间大盘急速上涨,而策略所选票滞涨,但要承受股指期货做空的损失。那是非常怪异的两个月!但这种现象值得研究。
2017-01-10
下文请看:
[小市值20只组合不择时不止损IC对冲——股指期货对冲研究成果应用][1]
[1]: https://www.joinquant.com/post/4462
2017-01-10
假设如下
比如IF1703建空10手空仓 市值为-1
1KW 股票现货市值1KW
等到IF1703换月的时候,现在IF1703合约为-950W 股票市值为1.1KW
如果期货合约要换到IF1704的时候,是要重新调整 期货的市值和股票市值相等呢
还是直接再开10手空仓,把合约从IF1703换到IF1704
2017-02-21
@superbwill , 股指期货换月时, 先平掉旧合约,然后按hedge_ratio计算需要的新合约手数,卖出。以覆盖风险敞口为标准,而不是直接等量换合约。
2017-02-21
@jqz1226 您好,感谢您的回复,如果换月的手都要重新再平衡,那么,股票现货换仓的时候怎么操作?
假设我的多因子策略是每月1号换仓,重新换一批股票,这个时候还需要调整 期货的市值和股票市值相等么,还是只根据期货的市值建立同等市值的现货
2017-02-21
@superbwill , 我的理解是,如果你股票一个月调仓一次,但股指期货可能每天都需要增减仓以尽量覆盖风险敞口,因为股票资产组合的beta系数其实不太稳定。
2017-02-21
发现get_next_month_future函数逻辑有些乱,改写了该函数”。改写函数处理方式,不是最优的。有时第三周是节假日,需要顺延至下一个交易日。 第二个问题是,实盘中,第三周周五是不可行,市场流量不足。
一般方式,通过合约当时成交量或持仓量来判断主、次合约,决定切换到下月合约。
2018-09-06