序言

BOLL布林带择时策略,是目前热度较高,也较常见的简单择时策略。原理就是,认为股价上涨超过一定的限度,达到超买状态,就会下跌;下跌达到一定的限度,达到超卖状态,就会上涨。这个限度一般是过去M日的平均收盘价格的上下N个标准差的值。

计算公式:

  中间线 = M日均线

  up线 = M日均线 + N*SD (SD为M日收盘价标准差,N为可调参数)

  down线 = M日均线 - N*SD (SD为M日收盘价标准差,N为可调参数)

布林带策略:择时

  当股价突破up线时,达到超买状态,卖出

  当股价跌破down线时,达到超卖状态,买入

一般M=20,N=2。

把买入信号到卖出信号的交易日,记为时段A;卖出信号到买入信号的交易日,记为时段B。那么我们实际需要观察的,就是时段A的收益是否显著大于0,时段B的收益是否显著小于0,这样便可以证明策略是有效的。

为了便于计算,不考虑手续费。编程使用的语言为python,使用的接口为akshare包,akshare包的使用文档如下:

代码研究

导包

1
import numpy as np import pandas as pd import time from datetime import datetime, date from datetime import timedelta import matplotlib.pyplot as plt from scipy import stats import scipy.stats as stats import akshare as ak # import tushare as ts # ts.set_token('你自己的token') # pro = ts.pro_api() # import efinance as ef # import baostock as bs import tqdm import scipy import statsmodels.stats.weightstats as sw #单样本和双样本的z检验都是用该函数

看看沪深300的数据情况

1
df_index=ak.stock_index_pe_lg(symbol='沪深300')[['日期','指数']] df_index 日期 指数 0 2005-04-08 1003.45 1 2005-04-11 995.42 2 2005-04-12 978.70 3 2005-04-13 1000.90 4 2005-04-14 986.98 ... ... ... 4421 2023-06-15 3925.50 4422 2023-06-16 3963.35 4423 2023-06-19 3930.91 4424 2023-06-20 3924.24 4425 2023-06-21 3864.03 4426 rows × 2 columns

取M为20,N为2,开始计算20天移动平均线和移动标准差:

1
N=20 M=2 df_index['mean']=0 df_index['std']=0 # 计算移动平均值 df_index['mean'] = df_index.rolling(20).mean() # 计算移动标准差 df_index['std'] = df_index.rolling(20).std() df_index 日期 指数 mean std 0 2005-04-08 1003.45 NaN NaN 1 2005-04-11 995.42 NaN NaN 2 2005-04-12 978.70 NaN NaN 3 2005-04-13 1000.90 NaN NaN 4 2005-04-14 986.98 NaN NaN ... ... ... ... ... 4421 2023-06-15 3925.50 3856.2015 48.242950 4422 2023-06-16 3963.35 3857.1420 50.199435 4423 2023-06-19 3930.91 3855.2210 46.260957 4424 2023-06-20 3924.24 3855.7735 47.049000 4425 2023-06-21 3864.03 3856.0205 47.080284 4426 rows × 4 columns

计算布林带的上下线

按照上文计算得到超出布林带上下线的交易日,把结果记在chance列,跌破下线的记为1,涨出上线的记为-1

1
df_index['chance']=0 for i in tqdm.trange(len(df_index)): if df_index.iloc[i]['指数']>df_index.iloc[i]['mean']+2*df_index.iloc[i]['std']: df_index.loc[i,'chance']=-1 elif df_index.iloc[i]['指数']<df_index.iloc[i]['mean']-2*df_index.iloc[i]['std']: df_index.loc[i,'chance']=1 else: pass 100%|██████████| 4426/4426 [00:03<00:00, 1385.55it/s]

假设我们在超卖日买入,超买日卖出,可以计算得到我们**每次买入到卖出的时段_A_的收益情况,以及卖出清仓到重新买入的时段_B_**的“市场收益情况”。

时段A和B的收益情况,都记录在这个时段开始的那一天,也就是超买超卖的那天。

1
df_index['收益'] = np.nan df_index['收益率'] = np.nan # 只有所有日期都存在才可以使用(速度原因) df_index['日均收益率'] = np.nan # 只有所有日期都存在才可以使用(速度原因) for i in tqdm.trange(len(df_index)-1): # 判断是否缺失 if df_index['chance'].iloc[i]==0: pass # chance为1的时机 elif df_index['chance'].iloc[i]==1: for j in range(i+1,len(df_index)): if df_index['chance'].iloc[j]==-1: df_index['收益'].iloc[i]=df_index['指数'].iloc[j]-df_index['指数'].iloc[i] df_index['收益率'].iloc[i]=100*(df_index['指数'].iloc[j]-df_index['指数'].iloc[i])/df_index['指数'].iloc[i] df_index['日均收益率'].iloc[i]=100*((df_index['指数'].iloc[j]-df_index['指数'].iloc[i])/df_index['指数'].iloc[i])/(j-i) break else: pass # chance为-1 elif df_index['chance'].iloc[i]==-1: for j in range(i+1,len(df_index)): if df_index['chance'].iloc[j]==1: df_index['收益'].iloc[i]=df_index['指数'].iloc[j]-df_index['指数'].iloc[i] df_index['收益率'].iloc[i]=100*(df_index['指数'].iloc[j]-df_index['指数'].iloc[i])/df_index['指数'].iloc[i] df_index['日均收益率'].iloc[i]=100*((df_index['指数'].iloc[j]-df_index['指数'].iloc[i])/df_index['指数'].iloc[i])/(j-i) break else: pass df_index 日期 指数 mean std chance 收益 收益率 日均收益率 0 2005-04-08 1003.45 NaN NaN 0 NaN NaN NaN 1 2005-04-11 995.42 NaN NaN 0 NaN NaN NaN 2 2005-04-12 978.70 NaN NaN 0 NaN NaN NaN 3 2005-04-13 1000.90 NaN NaN 0 NaN NaN NaN 4 2005-04-14 986.98 NaN NaN 0 NaN NaN NaN ... ... ... ... ... ... ... ... ... 4421 2023-06-15 3925.50 3856.2015 48.242950 0 NaN NaN NaN 4422 2023-06-16 3963.35 3857.1420 50.199435 -1 NaN NaN NaN 4423 2023-06-19 3930.91 3855.2210 46.260957 0 NaN NaN NaN 4424 2023-06-20 3924.24 3855.7735 47.049000 0 NaN NaN NaN 4425 2023-06-21 3864.03 3856.0205 47.080284 0 NaN NaN NaN 4426 rows × 8 columns

这里我把时段A和时段B的收益情况,只记录在chance为1或-1的那行;chance为1(或-1)的行,收益情况记录的是chance为1(或-1)的那天起,到chance为-1(或1)的那天结束,这个时间段的收益情况。

只观察超买超卖交易日的行:

1
df_index_chance=df_index[(df_index['日均收益率'] >-100)] df_index_chance 日期 指数 mean std chance 收益 收益率 日均收益率 34 2005-06-02 818.38 875.5640 26.839650 1 75.63 9.241428 0.236960 57 2005-07-05 849.68 888.6625 18.958400 1 44.33 5.217258 0.326079 58 2005-07-06 842.56 885.5020 21.103750 1 51.45 6.106390 0.407093 60 2005-07-08 829.49 878.8550 24.668002 1 64.52 7.778273 0.598329 73 2005-07-27 894.01 850.7795 17.129490 -1 10.20 1.140927 0.025930 ... ... ... ... ... ... ... ... ... 4372 2023-04-03 4090.57 4006.6705 38.913015 -1 -127.90 -3.126704 -0.208447 4373 2023-04-04 4103.11 4009.3835 43.615842 -1 -140.44 -3.422770 -0.244484 4387 2023-04-25 3962.67 4074.9405 55.479681 1 0.68 0.017160 0.000490 4405 2023-05-24 3859.09 3978.2250 45.957186 1 104.26 2.701673 0.158922 4406 2023-05-25 3850.50 3971.6180 54.071264 1 112.85 2.930788 0.183174 443 rows × 8 columns

这样,我们得到了从2005年开始至今的所有超买超卖的交易日,以及按照策略买卖的收益情况。现在我们需要验证的,就是买入到卖出的时段A和卖出到买入的时段B的日均收益率,是否显著不等于0。

统计检验

以2020年开始的数据为例,研究2020年开始的数据收益情况。

1
A=[] B=[] df_index_time=df_index_chance[df_index_chance['日期']>=datetime.strptime('2020-01-01', "%Y-%m-%d").date()] # df_index_time=df_index_chance[df_index_chance['日期']>='2020-01-01'] for i in tqdm.trange(len(df_index_time)-1): if df_index_time.iloc[i]['chance']==1: A.append(df_index_time.iloc[i]['日均收益率']) elif df_index_time.iloc[i]['chance']==-1: B.append(df_index_time.iloc[i]['日均收益率'])

1
print(len(A),len(B)) 45 42

使用Z检验检验数列A和B是否显著不为0。

1
# z,p=sw.ztest(A,B,value=0,alternative="larger")#{“two-sided”, larger, smaller} z,p=sw.ztest(A,value=0,alternative="larger")#{“two-sided”, larger, smaller} print("z值={},p值={}".format(t,p)) z值=3.870538128185361,p值=5.4297684955175646e-05

数列A显著大于0。

1
# z,p=sw.ztest(A,B,value=0,alternative="larger")#{“two-sided”, larger, smaller} z,p=sw.ztest(B,value=0,alternative="smaller")#{“two-sided”, larger, smaller} print("z值={},p值={}".format(t,p)) z值=-3.045772737880438,p值=0.0011604159144329247

数列B显著小于0。

这可以说明,布林带择时策略有效的获取了收益(不一定是超额收益),规避了风险。

研究结论

结果发现,BOLL布林带策略,在本文的研究中证明,超买超卖的择时策略是有效的,超卖时买入,超买时卖出,是一个明智的选择,其收益率也显著大于0;且市场在超买到超卖这段时间的收益也是显著小于0的。

当然,本文仅以2020年后的数据为实证研究对象,只能作为研究案例来说明观点,不具有普适性。

实际上,超买超卖的交易日并不常出现,并且本文是与0收益对比,而不是与沪深300几年间的整体收益对比,没有考虑到市场本来的趋势情况,而是相当于假设沪深300指数是“围绕原点回归波动的”,这其实并不符合实际。

本文依然只是自己闲来无聊研究玩玩,不构成任何投资建议,也不建议任何人拿结论去股市“试水”,否则后果自负。