布林线指标(Bollinger Bands,BOLL)是美国股市分析家约翰·布林(John Bollinger)提出的一种技术指标。他根据统计学中的标准差原理,求出股价的标准差及其信赖区间,从而确定股价的波动范围及未来走势。
布林线以移动平均线为基础的,通过股价标准差来反映最近的价格波动,可以认为是股票价格围绕着近期移动均线这个价值中枢在一定范围内变动。布林线通常可由上轨、中轨和下轨三条线组成。由于是利用波动带来显示股价的高低价位范围,经常被称为布林带。
一、指标计算及绘制
BOLL指标由三根线组成,中轨线是价格均线可以看作价值中枢,上下两线是根据统计学原理算出来的价格波动区间,上轨线可以看作压力线,下轨线可以看作支撑线。
计算方法如下:
(1)计算收盘价C的N日移动平均价格TR;
(2)计算N日收盘价的标准差S;
(3)计算上轨线价格:UP=TR+M*S。
(4)计算下轨线价格:DN=TR-M*S
其中N在日K线中为天数,通常取14,也可以看作K线个数,从而将该指标用于小时线或者分钟线;M为标准差倍数,通常取2。
布林线因具备多种功能,使用起来非常方便。主要特点如下:
(1)指示支撑和压力位置。下轨代表支撑线,上轨代表压力线。
(2)指示超买和超卖。突破上轨为超买,突破下轨为超卖。
(3)指示趋势。一直处于中轨上方为上升趋势,反之为下跌趋势。
(4)具备通道作用。三根线构成的带状区为通道。
(5)作为行情突变的标准。通过开口和收窄的趋势变化来判断行情准备突变还是盘整。
BOLL指标的计算代码如下:
#BOLL计算 取N=13,M=2
df['boll']=df.close.rolling(13).mean()
df['delta']=df.close-df.boll
df['beta']=df.delta.rolling(13).std()
df['up']=df['boll']+2*df['beta']
df['down']=df['boll']-2*df['beta']
BOLL指标的绘制代码如下:
#均线+BOLL线
line=Line()
line.add_xaxis( df.index.tolist() ) #X轴数据
line.add_yaxis( 'MA5', #序列名称
df.sma.round(2).tolist(), #Y轴数据
is_smooth=True, #平滑曲线
is_symbol_show=False #不显示折线的小圆圈
)
line.add_yaxis( 'boll',df.boll.round(2).tolist(),is_smooth=True,is_symbol_show=False,linestyle_opts=opts.LineStyleOpts(width=2,type_='dashed') )
line.add_yaxis( 'up',df.up.round(2).tolist(),is_smooth=True,is_symbol_show=False,linestyle_opts=opts.LineStyleOpts(width=2,type_='dashed') )
line.add_yaxis( 'down',df.down.round(2).tolist(),is_smooth=True,is_symbol_show=False,linestyle_opts=opts.LineStyleOpts(width=2,type_='dashed') )
绘制结果如下图所示:
二、应用分析
布林线指标在不同行情中具有不同的用法,例如在震荡行情中作为行情反转的指标,而在趋势行情中则可看作趋势开始或结束的指标。
布林线首先是用来判断趋势类型:
(1)当布林线收窄时,价格从下往上突破中轨后,再反转跌破中轨,布林线开口张大,说明行情进入下跌趋势。
(2)当布林线收窄时,价格从上向下跌破中轨后,再反转突破中轨,布林线开口张大,说明行情进入上涨趋势。
(3)当布林线的带状通道呈水平方向移动时,价格运行在布林线的下轨和上轨之间,说明行情进入震荡趋势。
在不同的趋势中,可以根据布林线的形态进行操作,例如在震荡行情中,价格突破上轨,将形成短期的回调,为短期的卖出信号;相反,价格突破下轨,为短线的买入信号。
由于在不同的周期中表现不一样,例如在分时线或者分钟线中可以作为短线指标,但在日K线或者周线中,可以作为长期趋势指标。因此用户需要在确定所采用的周期的情况下来进行买卖信号测试,以确定信号的有效性。
使用布林线最简单的策略就是根据股价是否穿越布林带的下轨或上轨来买入或卖出股票。即:
(1)股价下降穿越布林线下轨,则买入;
(2)股价上升穿越布林线上柜,则卖出。
计算代码如下:
#标记买入和卖出信号
markb=pd.DataFrame()
marks=pd.DataFrame()
for i in range(len(df)):
if df.loc[i,'close']>df.loc[i,'up'] :
marks.loc[i,'date']=df.loc[i,'date']
marks.loc[i,'close']=df.loc[i,'close']
marks.loc[i,'act']='S'
if df.loc[i,'close']<df.loc[i,'down'] :
markb.loc[i,'date']=df.loc[i,'date']
markb.loc[i,'close']=df.loc[i,'close']
markb.loc[i,'act']='B'
绘制买入和卖出信号其实有很多种方法,这里采用的是Scatter(),将标记作为单独的曲线加入到K线上。其实还可以采用MarkPointItem()来实现,说明参考官方文档。
下面将这个策略叠加到K线上,代码如下:
import akshare as ak
import pandas as pd
from pyecharts.charts import *
from pyecharts import options as opts
df = ak.stock_zh_a_hist(symbol="600036", start_date='20220104',end_date='20220916', adjust="qfq").iloc[:, :6]
df.columns = ['date','open','close','high','low','volume',] #列名改为英文方便下面操作
# 把date作为日期索引
df.index = pd.to_datetime(df.date)
df.index=df.index.strftime('%Y%m%d')
df=df.sort_index()
df['sma']=df.close.rolling(5).mean()
df['lma']=df.close.rolling(10).mean()
df['lma20']=df.close.rolling(20).mean()
df['lma30']=df.close.rolling(30).mean()
df['lma60']=df.close.rolling(60).mean()
df.index=range(len(df)) #修改索引为数字序号
df['ATR1']=df['high']-df['low'] #当日最高价-最低价
df['ATR2']=abs(df['close'].shift(1)-df['high']) #上一日收盘价-当日最高价
df['ATR3']=abs(df['close'].shift(1)-df['low']) #上一日收盘价-当日最低价
df['ATR4']=df['ATR1']
for i in range(len(df)): #取价格波动的最大值
if df.loc[i,'ATR4']<df.loc[i,'ATR2'] :
df.loc[i,'ATR4']=df.loc[i,'ATR2']
if df.loc[i,'ATR4']<df.loc[i,'ATR3'] :
df.loc[i,'ATR4']=df.loc[i,'ATR3']
df['ATR']=df.ATR4.rolling(14).mean() # N=14的ATR值
df['stop']=df['close'].shift(1)-df['ATR']*3 #止损价=(上一日收盘价-3*ATR)
#BOLL计算 取N=13,M=2
df['boll']=df.close.rolling(13).mean()
df['delta']=df.close-df.boll
df['beta']=df.delta.rolling(13).std()
df['up']=df['boll']+2*df['beta']
df['down']=df['boll']-2*df['beta']
#标记买入和卖出信号
markb=pd.DataFrame()
marks=pd.DataFrame()
for i in range(len(df)):
if df.loc[i,'close']>df.loc[i,'up'] :
marks.loc[i,'date']=df.loc[i,'date']
marks.loc[i,'close']=df.loc[i,'close']
marks.loc[i,'act']='S'
if df.loc[i,'close']<df.loc[i,'down'] :
markb.loc[i,'date']=df.loc[i,'date']
markb.loc[i,'close']=df.loc[i,'close']
markb.loc[i,'act']='B'
df.index = pd.to_datetime(df.date)
df.index=df.index.strftime('%Y%m%d')
kline = (
Kline(init_opts=opts.InitOpts(width="1200px",height="600px"))
.add_xaxis(xaxis_data=list(df.index)) #X轴数据
.add_yaxis(
series_name="klines", #序列名称
y_axis=df[["open","close","low","high"]].values.tolist(), #Y轴数据
itemstyle_opts=opts.ItemStyleOpts(color="#ec0000", color0="#00da3c"),
markpoint_opts=opts.MarkPointOpts(
data=[#添加标记符
opts.MarkPointItem(type_='max', name='最大值'),
opts.MarkPointItem(type_='min', name='最小值'), ],
#symbol='circle',
#symbol_size=[100,30]
),
)
.set_global_opts(
title_opts=opts.TitleOpts(title="K线及均线",pos_left='45%'), #标题位置
legend_opts=opts.LegendOpts(pos_right="35%",pos_top="5%"), #图例位置
#legend_opts=opts.LegendOpts(is_show=True, pos_bottom=10, pos_left="center"),
datazoom_opts=[
opts.DataZoomOpts(
is_show=False,
type_="inside", #内部缩放
xaxis_index=[0,1], #可缩放的x轴坐标编号
range_start=0, range_end=100, #初始显示范围
),
opts.DataZoomOpts(
is_show=True, #显示滑块
type_="slider", #滑块缩放
xaxis_index=[0,1], #可缩放的x轴坐标编号
pos_top="85%",
range_start=0, range_end=100, #初始显示范围
),
],
yaxis_opts=opts.AxisOpts(
is_scale=True, #缩放时是否显示0值
splitarea_opts=opts.SplitAreaOpts( #分割显示设置
is_show=True, areastyle_opts=opts.AreaStyleOpts(opacity=1) ),
),
tooltip_opts=opts.TooltipOpts( #提示框配置
trigger="axis", #坐标轴触发提示
axis_pointer_type="cross", #鼠标变为十字准星
background_color="rgba(245, 245, 245, 0.8)", #背景颜色
border_width=1, border_color="#ccc", #提示框配置
textstyle_opts=opts.TextStyleOpts(color="#000"), #文字配置
),
visualmap_opts=opts.VisualMapOpts( #视觉映射配置
is_show=False, dimension=2,
series_index=5, is_piecewise=True,
pieces=[ {"value": 1, "color": "#00da3c"}, {"value": -1, "color": "#ec0000"}, ],
),
axispointer_opts=opts.AxisPointerOpts( #轴指示器配置
is_show=True,
link=[{"xAxisIndex": "all"}],
label=opts.LabelOpts(background_color="#777"), #显示标签设置
),
brush_opts=opts.BrushOpts(
x_axis_index="all", #所有series
brush_link="all", #不同系列选中后联动
out_of_brush={"colorAlpha": 0.1}, #高亮显示程度
brush_type="lineX", #纵向选择
),
)
)
#均线+BOLL线
line=Line()
line.add_xaxis( df.index.tolist() ) #X轴数据
line.add_yaxis( 'MA5', #序列名称
df.sma.round(2).tolist(), #Y轴数据
is_smooth=True, #平滑曲线
is_symbol_show=False #不显示折线的小圆圈
)
line.add_yaxis( 'boll',df.boll.round(2).tolist(),is_smooth=True,is_symbol_show=False,linestyle_opts=opts.LineStyleOpts(width=2,type_='dashed') )
line.add_yaxis( 'up',df.up.round(2).tolist(),is_smooth=True,is_symbol_show=False,linestyle_opts=opts.LineStyleOpts(width=2,type_='dashed') )
line.add_yaxis( 'down',df.down.round(2).tolist(),is_smooth=True,is_symbol_show=False,linestyle_opts=opts.LineStyleOpts(width=2,type_='dashed') )
line.set_series_opts(
label_opts=opts.LabelOpts(is_show=False), #是否显示数据标签
#linestyle_opts=opts.LineStyleOpts(width=1), #线宽
)
line.set_global_opts(
datazoom_opts=[
opts.DataZoomOpts(
is_show=False,
type_="inside", #图内缩放调整
xaxis_index=[0,1], #可缩放的x轴坐标编号
range_start=0, range_end=100, #初始显示范围
),
opts.DataZoomOpts(
is_show=True, #是否显示滑块
type_="slider", #外部滑块缩放调整
xaxis_index=[0,1], #可缩放的x轴坐标编号
pos_top="85%",
range_start=0, range_end=100, #初始显示范围
),
],
legend_opts=opts.LegendOpts(pos_right="20%",pos_top="5%"), #图例位置
tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="cross") #趋势线设置
)
stop=(
Line()
.add_xaxis( df.index.tolist() ) #X轴数据
.add_yaxis( 'STOP', #序列名称
df.stop.round(2).tolist(), #Y轴数据
is_smooth=True, #平滑曲线
)
.set_series_opts(
label_opts=opts.LabelOpts(is_show=False), #是否显示数据标签
linestyle_opts=opts.LineStyleOpts(width=2), #线宽
)
.set_global_opts(
datazoom_opts=[
opts.DataZoomOpts(
is_show=False,
type_="inside", #图内缩放调整
xaxis_index=[0,1], #可缩放的x轴坐标编号
range_start=0, range_end=100, #初始显示范围
),
opts.DataZoomOpts(
is_show=True, #是否显示滑块
type_="slider", #外部滑块缩放调整
xaxis_index=[0,1], #可缩放的x轴坐标编号
pos_top="85%",
range_start=0, range_end=100, #初始显示范围
),
],
legend_opts=opts.LegendOpts(pos_right="20%",pos_top="5%"), #图例位置
tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="cross") #趋势线设置
)
)
#买卖标记点
markb.index = pd.to_datetime(markb.date)
markb.index=markb.index.strftime('%Y%m%d')
mark1 = ( #买入信号
Scatter()
.add_xaxis(markb.index.tolist())
.add_yaxis(series_name='',
y_axis=markb.close.tolist(),
xaxis_index=0,
symbol='triangle',
symbol_size=10,#设置散点的大小
)
.set_series_opts(label_opts = opts.LabelOpts(is_show =False),
itemstyle_opts = opts.ItemStyleOpts(color="red"))
.set_global_opts(legend_opts=opts.LegendOpts(is_show=False))
.set_global_opts(visualmap_opts=opts.VisualMapOpts(is_show=False))
)
marks.index = pd.to_datetime(marks.date)
marks.index=marks.index.strftime('%Y%m%d')
mark2 = ( #卖出信号
Scatter()
.add_xaxis(marks.index.tolist())
.add_yaxis(series_name='',
y_axis=marks.close.tolist(),
xaxis_index=0,
symbol='triangle',
symbol_size=10,#设置散点的大小
symbol_rotate=180,
)
.set_series_opts(label_opts = opts.LabelOpts(is_show =False),
itemstyle_opts = opts.ItemStyleOpts(color="green"))
.set_global_opts(legend_opts=opts.LegendOpts(is_show=False))
.set_global_opts(visualmap_opts=opts.VisualMapOpts(is_show=False))
)
kline.overlap(line)
kline.overlap(stop) #止损数据叠加到K线
kline.overlap(mark1)
kline.overlap(mark2)
#成交量
bar = (
Bar()
.add_xaxis(xaxis_data=df.index.tolist()) #X轴数据
.add_yaxis(
series_name="volume",
y_axis=df["volume"].tolist(), #Y轴数据
#xaxis_index=1,
#yaxis_index=1,
label_opts=opts.LabelOpts(is_show=False),
itemstyle_opts=opts.ItemStyleOpts(
color='#ef232a' #'#14b143'
),
)
.set_global_opts(
xaxis_opts=opts.AxisOpts(
type_="category", #坐标轴类型-离散数据
grid_index=1,
axislabel_opts=opts.LabelOpts(is_show=False),
),
legend_opts=opts.LegendOpts(is_show=False),
)
)
grid_chart = Grid(
init_opts=opts.InitOpts(
width="1200px", #显示图形宽度
height="600px",
animation_opts=opts.AnimationOpts(animation=False), #关闭动画
)
)
grid_chart.add( #加入均线图
kline,
grid_opts=opts.GridOpts(pos_left="10%", pos_right="8%", height="40%"),
)
grid_chart.add( #加入成交量图
bar,
grid_opts=opts.GridOpts(pos_left="10%", pos_right="8%", pos_top="60%", height="20%"),
)
grid_chart.render("BOLL.html")
#df.to_excel('example.xlsx', sheet_name='600036', index=False)
结果如下图所示:
上图中,红色三角形为买入信号,绿色三角形为卖出信号。
从图中可以看出,在不区分行情类型的时候,下跌趋势中会连续出现买入信号,上涨趋势中会连续出现卖出信号。
其它策略如:
(1)布林线三轨向上,股价穿越中轨买入,穿越上轨卖出;
(2)布林线三轨向下,股价穿越下轨买入,穿越中轨卖出;
(3)布林线走平,股价穿越下轨买入,穿越上轨卖出;
(4)三线向上张口买,三线向下张口卖
或者:
(1)价格由下向上穿越下轨线时,建仓。
(2)价格由下向上穿越中间线时,股价可能加速向上,加仓。
(3)价格在中间线与上轨线之间波动时,为多头市场,可做多。
(4)价格在中间线与上轨线间,由上往下跌破中间线,卖出。
(5)价格在中间线与下轨线之间向下波动时,为空头市场,空仓。
根据前面提供的示例,读者可以自己采用不同的买入和卖出策略并将所得结果加入到图中进行分析,这是不是也算是一个简单的策略回归测试😊