请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  数理科学 帖子:3353396 新帖:42

用指数战胜指数,ETF二八轮动对冲模型

Peace发表于:5 月 9 日 18:51回复(1)

最近在忙着做视频,总算是有时间来研究研究策略了。

二八轮动,早已不是新鲜的策略了。聚宽有关于二八轮动的帖子,很经典,附上链接:斗牛蛋卷二八轮动原版策略实现

“二八轮动”就是根据A股市场中大盘股和小盘股走势不同作为信号判断的。(所谓二,就是指数量占20%的大盘股、权重股;所谓的八,就是数量占80%左右的中小盘股,非权重股;其轮动就是指在两者之间不断切换,轮流持有。)

大盘股和小盘股的区分就是根据公司的流通股本的多少,大盘股通常指流通股本大于1亿的上市公司股票,而小盘股则与大盘股相对,通常指流通股本不足3000万的股票。沪深300通常可以近似表示大盘股的整体走势,中证500指数近似表示小盘股的整体走势。

好了,其它的不多说了,二八轮动有不明白的,直接访问上面的链接,也可以克隆教程里的策略代码。接下来,我想说一下我对于二八轮动的想法。

先来看看二八轮动原代码的一段回测结果:

可以看出,二八轮动赚的是牛市的钱。在18年的时候,处于一种失控的状态。

这里我提出两个问题:

1. 如何避免类似于18年的失控下滑?
2. 如何让二八获得风险更低、收益更高的结果?

在解决这两个问题之前,先来说一下如何对二八策略进行代码分析。

一、计算指数市值

如上图,首先要做的是计算沪深300与上证500的市值与市盈率,还可以将其可视化后进行直观观察。

大小盘的轮动意义在于,它们的动量大小是不一样的,如下图,市值更小的上证500在牛市的时候上涨空间更大,但在熊市的时候下跌也更狠。

二、计算大小盘指数的动量

如何计算动量,我这里就不啰嗦了,不懂的看聚宽教程去。

代码如下:

结果可视化如下:

如果想跟踪指数做交易,一般会交易对应的ETF,下面是沪深300与上证500对就的ETF趋势。

三、验证ETF与指数的跟踪误差值

如果交易的ETF品种无法很好的跟踪对应指数,我相信,亏损是必然结果!因此我们有必要验证ETF与对应指数间的相关程度。

计算两个数据之间的相关度,可使用corrcoef函数,如下:

以沪深300指数为例,我们从聚宽ETF数据集里,找到与其最相关的一个ETF出来,如下:

通过上图可以看出,结果正好是510300,即300ETF

对比一下趋势看看:

它们的相关度很高。不过,有趣的是,上证500与之相关程度最高的并不是500ETF:

以上的计算,可以辅助我们解决上面提出的两个问题。

四、加入国债指数

针对于第一个问题,我的第一个想法就是,在市场下行的时候,买入国债避险,一来减少亏损,二来减少闲置资金。

由于聚宽无法交易国债,这里选择了511010-国债ETF

从上图来看,国债指数是一个很好的避险品种。

加入国债指数后,在大小盘指数都出现负动量的时候,买入国债指数。完成策略,回测后的结果如下:

结果显示,整体收益有所提升,但18年的情况更加不妙了。于是我想,应该可以增加动量值,减少频繁交易。接下来便将动量值由0增加到5%,回测结果如下:

回撤值有所减小,但牺牲的是绝对收益率。

五、使用负相关标的做“对冲”

如果想提高收益,我的想法是在市场上行的时候做多,在下行的时候做空。可惜A股不支持做空。那是否存在这样一个标的,与大盘趋势相反,这样即可完成当大盘下跌时,买入该标的,迎来一定的上涨空间?

仍旧从聚宽的ETF集里去寻找:

通过计算,发现标的150051与大盘存在高达百分之80%的负相关程度。于是,把国债指数替换成该标的,即在大小盘指数都出现负动量时,全部资金买入该标的。

回测结果如下:

从上图看出,整体的年化收益达到28.18%,在18年的时候,由于对冲交易,几乎平稳度过。

这里要强调一下关于对冲的认知。

金融学上,对冲(hedge)指特意减低另一项投资的风险的投资。它是一种在减低商业风险的同时仍然能在投资中获利的手法。

一般对冲是同时进行两笔行情相关、方向相反、数量相当、盈亏相抵的交易。行情相关是指影响两种商品价格行情的市场供求关系存在同一性,供求关系若发生变化,同时会影响两种商品的价格,且价格变化的方向大体一致。

方向相反指两笔交易的买卖方向相反,这样无论价格向什么方向变化,总是一盈一亏。当然要做到盈亏相抵,两笔交易的数量大小须根据各自价格变动的幅度来确定,大体做到数量相当。

因此,我这里的“对冲”与严格意义上的对冲来讲,是有区别的,所以读者要做一下区别对待。

历史总归是历史,未来大盘如何走,负相关品种是否有效,这都不好说,只有提升自己的能力,才是真格。

注:本研究如果对您有用,希望点个赞或转发一下,都是对大树的鼓励。研究中有任何问题,欢迎各位前来讨论。

import numpy as np
import pandas as pd
import datetime
import seaborn as sns
from jqdata import *
sns.set(font='serif')
plt.rcParams['axes.unicode_minus'] = False 
trade_date = get_trade_days(end_date=datetime.datetime.now(), count=50)
index_codes = ['000300.XSHG', '000905.XSHG']  # 沪深300,上证500
start_date = '2013-03-15'
end_date = trade_date[-2]
'''计算指数的平均市场与市盈率'''
def func(index):
    q = query(valuation.code, 
              valuation.market_cap,
              valuation.pe_ratio).filter(valuation.code.in_(get_index_stocks(index)))
    df = get_fundamentals(q, end_date)
    return {'market_cap':df['market_cap'].mean(),'pe_ratio':df['pe_ratio'].mean()}
dict_index = {get_security_info(index).display_name: func(index) for index in index_codes}
pd.DataFrame(dict_index).T
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
market_cap pe_ratio
沪深300 1238.213244 41.364299
中证500 172.281432 43.200443
'''指数市值分布情况'''
df_index = pd.DataFrame(dict_index).T
df_index['market_cap'].plot(kind='bar')
plt.show()
'''指数市盈率分布情况'''
df_index = pd.DataFrame(dict_index).T
df_index['pe_ratio'].plot(kind='bar')
plt.show()
'''指数的历史价格趋势'''
dict_close = {get_security_info(code).display_name:\
              get_price(code, start_date, end_date)['close']\
              for code in index_codes}
pd.DataFrame(dict_close).plot()
plt.show()
'''指数20日前的价格与昨日价格获取'''
price = {get_security_info(index).display_name:\
         get_price(index, end_date=trade_date[-2], count=50)['close'].values[[-20, -1]]\
         for index in index_codes}
price
{'沪深300': array([3698.49, 3997.58]), '中证500': array([5298.5 , 5727.17])}
'''计算前20日动量'''
ratio = {key: {'ratio':(value[1] - value[0]) / value[0]} for key, value in price.items()}
ratio
{'沪深300': {'ratio': 0.08086813807797241},
 '中证500': {'ratio': 0.08090402944229501}}
'''动量可视化对比'''
df_ratio = pd.DataFrame(ratio).T
df_ratio['ratio'].plot(kind='bar')
plt.show()
'''相关ETF的价格趋势'''
etf_list = ['510300.XSHG', '510500.XSHG']
dict_close = {get_security_info(code).display_name:\
              get_price(code, start_date, end_date)['close']\
              for code in etf_list}
pd.DataFrame(dict_close).plot()
plt.show()
'''数据相关程度计算'''
a=[0.6557,0.0357,0.8491,0.9340,0.6787];
b=[0.7315,0.1100,0.8884,0.9995,0.6959];
print('计算一\n',mean(multiply((a-mean(a)),(b-mean(b))))/(std(b)*std(a)))
print('计算二\n', corrcoef(a,b))
"""
corrcoef得到相关系数矩阵(向量的相似程度)
第一个1是a与a的相关系数,左边第一个0.9976是a与b相关系数,第二个0.9976是b与a相关系数,第二个1是b与b的相关系数
"""
计算一
 0.997622244273944
计算二
 [[1.         0.99762224]
 [0.99762224 1.        ]]
'\ncorrcoef得到相关系数矩阵(向量的相似程度)\n第一个1是a与a的相关系数,左边第一个0.9976是a与b相关系数,第二个0.9976是b与a相关系数,第二个1是b与b的相关系数\n'
'''计算指数之间的相关程度'''
stock_base = '000300.XSHG'
index_list = get_all_securities(types=['fund'], date=end_date).index
func = lambda index_a, index_b: np.corrcoef(get_price(index_a, start_date, end_date)['close'].values, 
                                            get_price(index_b, start_date, end_date)['close'].values
                                           )[0, 1]
df_corr = pd.DataFrame({index : {'corrcoef': func(stock_base, index)}\
                        for index in index_list}).T.dropna()
df_corr = df_corr.sort_values(by=['corrcoef'], ascending=False).head()
df_corr
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
corrcoef
510300.XSHG 0.994107
159919.XSHE 0.992958
150009.XSHE 0.991077
160706.XSHE 0.990493
160807.XSHE 0.990209
'''正当关指数与ETF趋势对比'''
compare_codes = (stock_base, df_corr.index[0])
df_test = pd.DataFrame()
for index in compare_codes:
    df_test[get_security_info(index).display_name] = get_price(index, start_date, end_date)['close']
df_test.plot(subplots=True)
plt.show()
'''找出与指数相关程度最高的ETF'''
def func(code):
    etfs = get_all_securities(types=['fund'], date=end_date).index
    func = lambda index_a, index_b: np.corrcoef(get_price(index_a, start_date, end_date)['close'].values, 
                                                get_price(index_b, start_date, end_date)['close'].values
                                               )[0, 1]
    df_corr = pd.DataFrame({etf : {'corrcoef': func(code, etf)}\
                            for etf in etfs}).T.dropna()
    df_corr = df_corr.sort_values(by=['corrcoef'], ascending=False).head()
    return df_corr.index[0], df_corr.values[0]
eft_list = map(func, index_codes)
result = list(eft_list)
result
[('510300.XSHG', array([0.99410662])), ('162711.XSHE', array([0.99720205]))]
'''找出与ETF负相关程度最高的ETF'''
def func(code):
    etfs = get_all_securities(types=['fund'], date=end_date).index
    func = lambda index_a, index_b: np.corrcoef(get_price(index_a, start_date, end_date)['close'].values, 
                                                get_price(index_b, start_date, end_date)['close'].values
                                               )[0, 1]
    df_corr = pd.DataFrame({etf : {'corrcoef': func(code, etf)}\
                            for etf in etfs}).T.dropna()
    df_corr = df_corr.sort_values(by=['corrcoef'], ascending=True).head()
    return df_corr.index[0], df_corr.values[0]
eft_list = map(func, etf_list)
result = list(eft_list)
result
[('150051.XSHE', array([-0.80048751])), ('161815.XSHE', array([-0.69941094]))]
'''大盘与国债指数趋势对比'''
compare_codes = (stock_base, '511010.XSHG')
df_test = pd.DataFrame()
for index in compare_codes:
    df_test[get_security_info(index).display_name] = get_price(index, start_date, end_date)['close']
df_test.plot(subplots=True)
plt.show()
'''负相关ETF趋势对比'''
compare_codes = (stock_base, result[0][0])
df_test = pd.DataFrame()
for index in compare_codes:
    df_test[get_security_info(index).display_name] = get_price(index, start_date, end_date)['close']
df_test.plot(subplots=True)
plt.show()
 

全部回复

0/140

量化课程

    移动端课程