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

量化交易吧 /  数理科学 帖子:3353266 新帖:5

成交量脉冲择时模型

外汇工厂发表于:7 月 19 日 16:00回复(1)

基于成交量脉冲的大盘择时策略¶

研究思路¶

成交量脉冲指的是成交量(金额)突然放大的现象。成交量脉冲出现,往往是一股集中的力量大量买入股票造成的,有时候是投资者者的一致预期突然改变,有时是“聪明的钱”的集中活动,有时候是外资大量增持A股。
这种脉冲式的买入,往往蕴涵着A股未来大概率向好。我们就捕捉这种脉冲,开发相应的交易策略。 根据研报定义,我们用脉冲指标比来衡量成交量脉冲的强度。具体定义脉冲比为当日成交额除以过去 5 日(不含当日)的平均成交金额。
脉冲比=当日成交额 / MA(过去 5 个交易日的成交额) 下图是 A 股历史上的总成交金额图,可以肉眼看到,2015 年初和2019年初有明显成交量放量。

get_price('000002.XSHG',start_date = '2009-12-23',end_date = '2019-07-12',fields=['money']).plot()
<matplotlib.axes._subplots.AxesSubplot at 0x7f9e7c805f28>

成交量脉冲与A股涨跌的关系¶

根据统计,成交量出现正的脉冲,当天股市行情上涨的概率较大。总体趋势是脉冲比越大,当天上涨概率越高,总体概率在74.1%。

from jqdata import *
import numpy as np
import pandas as pd

#数据获取
start='2009-12-23'
end='2019-7-12'
n=5
index = '000001.XSHG'
extend_days = get_trade_days(end_date=start,count=n)

if end in extend_days:
    extend_days = extend_days
else:
    extend_days = extend_days[:-1]
trade_days = get_trade_days(start_date=start, end_date=end, count=None)
extended_trade_days=np.concatenate((extend_days,trade_days),axis=0)

def cal_future_ret(close,n):
    future_ret=[]
    for i in range(len(close)-n):
        future_ret.append(close[i+n]/close[i]-1)
    return np.concatenate((future_ret,n*[np.nan]))

def get_data(index):
    df = get_price(index,start_date=extended_trade_days[0],end_date=end,fields=['close','open','money'])
    df['ma_vol']=df['money'].rolling(6).mean()
    df['vol_shock']=df['money']/((df['ma_vol']-df['money']/6)*6/5)
    ret = np.concatenate(([np.nan],np.array(df['close'][1:])/np.array(df['close'][:-1])))-1
    df['当日涨幅'] = ret

    groups = [1.2,1.3,1.4,1.5,1.6,1.7]
    v = pd.cut(df['vol_shock'],bins=[1.2,1.3,1.4,1.5,1.6,1.7,max(df['vol_shock'])],labels=groups)
    v = v.cat.add_categories(0)
    v = v.fillna(0)
    df['vol_shock_group'] = v
    df['未来30日涨幅'] = cal_future_ret(df['close'],30)
    df['未来20日涨幅'] = cal_future_ret(df['close'],20)
    df['未来10日涨幅'] = cal_future_ret(df['close'],10)
    return df

df = get_data(index)

def group_plot(df,y):
    a = df[df['vol_shock_group']!=0]
    b = a.groupby('vol_shock_group').mean()
    plt.xlabel('脉冲量级')
    plt.ylabel(y)
    plt.plot(np.array(b.index),b[y])

sum_dict = {}
for group in groups:
    b = df[df['vol_shock_group']==group]
    freq_pos = len(b[b['当日涨幅']>=0])
    freq_neg = len(b[b['当日涨幅']<0])
    prob_pos = freq_pos/(freq_pos+freq_neg)
    sum_dict[group] = [freq_pos,freq_pos+freq_neg,prob_pos]
b = df[df['vol_shock_group']!=0]
freq_pos = len(b[b['当日涨幅']>=0])
freq_neg = len(b[b['当日涨幅']<0])
prob_pos = freq_pos/(freq_pos+freq_neg)
sum_dict['汇总'] = [freq_pos,freq_pos+freq_neg,prob_pos]
pd.DataFrame(sum_dict,index=['上涨数','样本数','上涨占比'])
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
1.2 1.3 1.4 1.5 1.6 1.7 汇总
上涨数 106.000000 65.000000 44.000000 18.000000 12.000000 21.000 266.000000
样本数 161.000000 82.000000 56.000000 23.000000 13.000000 24.000 359.000000
上涨占比 0.658385 0.792683 0.785714 0.782609 0.923077 0.875 0.740947

下图为上证综指的成交量脉冲比与平均收益的关系。可以看出,大致趋势是脉冲比越大,未来一段时间的预期收益会更高。

plt.figure(1,figsize=(12,6))
plt.title('当日涨幅')
group_plot(df,'当日涨幅')
plt.figure(2,figsize=(12,6))
plt.title('未来30日涨幅')
group_plot(df,'未来30日涨幅')
plt.figure(3,figsize=(12,6))
plt.title('未来20日涨幅')
group_plot(df,'未来20日涨幅')
plt.figure(4,figsize=(12,6))
plt.title('未来10日涨幅')
group_plot(df,'未来10日涨幅')

基于脉冲比指标构建择时策略¶

策略逻辑:成交量的脉冲出现,往往是一股集中的力量大量买入股票造成的,有时候是投资者的一致预期突然改变,有时是“聪明的钱”的集中活动,有时候是外资大量增持A股。
这种脉冲式的买入,往往蕴涵着A股未来大概率向好。我们就捕捉这种脉冲,开发相应的交易策略。
策略思路:当成交量出现脉冲时,计算脉冲比。根据脉冲比,决定是持有指数还是空仓。
参数 1:脉冲比阈值(例如:1.3)
参数 2:持有期限(例如:30 天)
如果持有期内,出现新的大于阈值的脉冲,则重现计算持有期限。
交易标的为上证指数和深证成指,基准用各自的价格指数。

上证综指择时效果¶

根据后面的敏感性测试,我们可以得出最优参数1为1.5,最优参数2为30。
策略满仓天数为1130,空仓天数为1196。各项指标均大幅优于基准指数,且完全避开了2015年下半年的大回撤。年化收益达到10.96%,夏普比率达到0.88。

#上证综指择时
threshold = 1.5
holding = 30



def back_test(df,threshold,holding,prt=False):
    count=holding
    position=[]
    for i in range(len(df)):
        vol_shock = df['vol_shock'][i]
        if vol_shock>threshold:
            position.append(1)
            count = 1
        else:
            if count<holding:
                count +=1
                position.append(1)
            else:
                position.append(0)
    df['position']=position
    if prt:
        position = np.array(position)
        print('满仓天数:',len(position[position==1]))
        print('空仓天数:',len(position[position==0]))
    index_ret=np.concatenate(([np.nan],np.array(df['close'][1:])/np.array(df['close'][:-1])-1))
    ret=[0]
    for i in range(len(df)-1):
        ret.append(index_ret[i+1]*position[i])
    ret = np.array(ret)
    df['ret']=ret
    cum_ret=[]
    for i in range(len(ret)):
        if i==0:
            cum_ret.append(1+ret[i])
        else:
            cum_ret.append(cum_ret[-1]*(1+ret[i]))
    df['cum_ret']=cum_ret
    return df

def summary(df):
    #输出各项指标
    cum_ret = df['cum_ret']
    ret = df['ret']
    annual_ret = cum_ret[-1]**(240/(len(ret)-5))-1
    cum_ret_rate = cum_ret[-1]-1
    max_nv = np.maximum.accumulate(cum_ret)
    mdd = -np.min(cum_ret/max_nv-1)
    
    print('年化收益率: {:.2%}'.format(annual_ret))
    print('累计收益率: {:.2%}'.format(cum_ret_rate))
    print('最大回撤: {:.2%}'.format(mdd))
    print('夏普比率:{:.2}'.format(ret.mean()/ret.std()*np.sqrt(240)))

    #作图
    plt.figure(1,figsize=(20,10))
    plt.title('净值曲线',fontsize=18)
    plt.plot(df.index,cum_ret)
    plt.plot(df.index,df['close']/df['close'][0])
    plt.legend(['策略净值','基准净值'],fontsize=15)
    plt.figure(2,figsize=(20,10))
    plt.title('相对优势',fontsize=18)
    plt.plot(df.index,cum_ret-df['close']/df['close'][0])
    plt.show()
    
df = back_test(df,threshold,holding,prt=True)
summary(df)
满仓天数: 1130
空仓天数: 1196
年化收益率: 10.96%
累计收益率: 173.44%
最大回撤: 26.22%
夏普比率:0.88

上证综指择时的敏感性分析¶

由于本策略受到两个参数的影响,不同的参数可能会带来截然不同的效果。所以,通过敏感性分析找出最优参数是很有必要的。
敏感性分析的参数为触发阈值和持有时间。触发阈值的范围为1.2-1.7(步长0.1),而持有期的范围为10-30(步长10)。优化目标为夏普比率。
通过敏感性分析,我们得到最优阈值为1.5和30,此时夏普比率为0.88。

#上证综指择时敏感性分析
threshold = [1.2,1.3,1.4,1.5,1.6,1.7]
holding = [10,20,30]
sharpe=[]
for i in threshold:
    for j in holding:
        df = back_test(df,i,j)
        ret = df['ret']
        sharpe.append(ret.mean()/ret.std()*np.sqrt(240))
sharpe = np.array(sharpe).reshape((6,3))
sharpe_df = pd.DataFrame(sharpe,index=threshold,columns=holding)
sharpe_df
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
10 20 30
1.2 0.111442 -0.039098 0.033015
1.3 0.275688 0.144543 0.012289
1.4 0.255339 0.082304 -0.029744
1.5 0.676901 0.712248 0.879564
1.6 0.754705 0.688486 0.872068
1.7 0.528588 0.691488 0.811117

深证成指择时效果¶

根据后面的敏感性测试,我们可以得出最优参数1为1.6,最优参数2为10。
策略满仓天数为490,空仓天数为1836。各项指标同样优于基准指数,但由于2015年回撤的影响,不如上证综指的择时效果。年化收益达到10.96%,夏普比率达到0.56。

df2 = get_data('399001.XSHE')
df2 = back_test(df2,1.6,10,prt=True)
summary(df2)
满仓天数: 490
空仓天数: 1836
年化收益率: 6.22%
累计收益率: 79.32%
最大回撤: 33.11%
夏普比率:0.56

深证成指择时的敏感性分析¶

与上文类似,我们对深证成指择时策略的两个参数进行敏感性分析,试图达到最优夏普比率,从而找出最优参数。经过敏感性分析,我们得到最优阈值为1.6,最优持有期为10。

#深证成指择时敏感性测试
threshold = [1.2,1.3,1.4,1.5,1.6,1.7]
holding = [10,20,30]
sharpe=[]
for i in threshold:
    for j in holding:
        df2 = back_test(df2,i,j)
        ret = df2['ret']
        sharpe.append(ret.mean()/ret.std()*np.sqrt(240))
sharpe = np.array(sharpe).reshape((6,3))
sharpe_df = pd.DataFrame(sharpe,index=threshold,columns=holding)
sharpe_df
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
10 20 30
1.2 0.025333 0.078836 -0.013877
1.3 0.020305 0.059453 -0.081795
1.4 0.180617 0.131466 -0.066911
1.5 0.456017 0.019728 -0.062650
1.6 0.559520 0.333438 0.237095
1.7 0.452277 0.422337 0.269803

研究结论¶

成交量脉冲大盘择时基于放量上涨的思想:成交量突然上升往往是由于集中的大量买入造成的。而这种大量的买入往往意味着A股未来大概率上升。基于这种思想,我们参考研报,复现了基于成交量脉冲的大盘择时策略。
策略在两种上证综指和深证成指上的回测效果都不错,可以大幅战胜指数,但策略对于上证综指的择时效果更胜一筹。总的来说,这个策略原理简单,容易实现,择时效果也十分不错,收益稳定且大幅跑赢市场,但对于参数的选择仍需注意,不同的参数可能会带来截然不同的结果。另外,策略的收益主要来自2015年的大牛市,在震荡市中收益较小。根据我们的研究结果,我们建议在上证综指出现1.5以上的脉冲比时,买入持有30天。

全部回复

0/140

量化课程

    移动端课程