今天,我们一起来看下,十大经典技术分析交易策略中的“布林带”交易系统在 Hikyuu 中的多种实现方法,以及其在全部A股市场中的普适情况以及可能的改进。
策略描述
一起回顾下什么是“布林带”? 布林带(Bollinger Band)是20世纪80年代由约翰·布林格(John Bollinger)开发的市场趋势技术指标,用于衡量市场波动性和价格动态。
布林带由上、中、下三条线组成。位于中间的中轨线主要用于衡量中期趋势,通常是简单的移动平均线,是上轨线和下轨线的基础。其利用统计原理,求出股价的标准差及其信赖区间,从而确定股价的波动范围及未来走势,利用波带显示股价的安全高低价位。
其公式如下:
- 中轨线=N日的移动平均线
- 上轨线=中轨线+M倍的标准差
- 下轨线=中轨线-M倍的标准差
围绕布林线其实可以有多种不同的交易策略,比如:
- 趋势交易策略:当股价向上突破上轨时,为买入信号,当股价向下突破下界时,则为卖出信号。
- 均值回归策略:当股价向上突破上轨时,为买出信号,当股价向下突破下界时,为卖出信号。
- 中轨线交易策略:布林带线将呈现上升态势时,等待价格回撤并触及布林带中轨线;当价格上涨并触及布林带中轨线时,买入。
今天我们主要以趋势交易策略为例,来看看在 Hikyuu 中如何实现这样的策略,不涉及其他的交易方法。趋势交易的特点是通过捕获较大的趋势获取较大的收益,不追求胜率,而是控制收益和亏损的风险比例。
从绘图开始
量化交易回测,绘图是基本功,主要目的是验证技术指标的正确性,下面让我们看看在 Hikyuu 中如何来绘制上述的布林线(以下示例代码在 Jupyter 中执行):
%matplotlib inline
%time from hikyuu.interactive import *
# 定义查询条件,获取相应的 K 线数据,这里以平安银行为例
start_date = Datetime(20200101)
end_date = None
query = Query(start_date, end_date)
stk = sm['sz000001']
k = stk.get_kdata(query)
# 这里以趋势交易为例,特点是通过捕获较大的趋势获取大额收益
# 所以其 N 日时间窗口应该较长,而轨道宽度应该比较窄
# 这里取 100 日的时间窗口,0.5标准差的轨道宽度
n = 100
band = 0.5
c = CLOSE()
ma = MA(CLOSE, n)
upper = ma + band * STDEV(CLOSE, n)
upper.name = "上轨线"
lower = ma - band * STDEV(CLOSE, n)
lower.name = "下轨线"
# 绘图
c(k).plot(legend_on=True)
ma(k).plot(new=False, legend_on=True)
upper(k).plot(new=False, legend_on=True)
lower(k).plot(new=False, legend_on=True)
gca().set_title(stk.name)
定义信号指示器
实现方法1
使用 crtSG 方法快速创建 SG,并绘制
def bulindai_calculate(self: SignalBase, k: KData):
n = self.get_param("n")
band = self.get_param("band")
top = (MA(CLOSE, n) + band * STDEV(CLOSE, n))(k)
bottom = (MA(CLOSE, n) - band * STDEV(CLOSE, n))(k)
c = k.close
for i in range(top.discard, len(top)):
if c[i] > top[i]:
self._add_buy_signal(k[i].datetime)
elif c[i] < bottom[i]:
self._add_sell_signal(k[i].datetime)
# 创建 SG 实例
my_sg = crtSG(bulindai_calculate, params={
"n": n, "band": band}, name="趋势布林带")
# 绘制 sg
# sg 只有指定了交易对象才会进行实际计算,通常由 SYS 实例执行回测时自动设定
my_sg.to = k
my_sg.plot()
ma(k).plot(new=False, legend_on=True)
upper(k).plot(new=False, legend_on=True)
lower(k).plot(new=False, legend_on=True)
实现方法2
使用常规的类继承方式实现信号指示器,绘图方法与实现方法1同。
# 使用常规的类继承方式实现信号指示器
class SG_BuLin(SignalBase):
def __init__(self, n=100, band=0.5):
super(SG_BuLin, self).__init__("SG_BuLin")
self.set_param("n", int(n))
self.set_param("band", float(band))
def _calculate(self, k):
n = self.get_param("n")
band = self.get_param("band")
c = k.close
ma = MA(c, n)
sd = STDEV(c, n)
top = ma + band * sd
bottom = ma - band * sd
for i in range(len(c)):
if c[i] > top[i]:
self._add_buy_signal(k[i].datetime)
elif c[i] < bottom[i]:
self._add_sell_signal(k[i].datetime)
def _clone(self):
cloned = SG_BuLin(self.get_param("n"), self.get_param("band"))
return cloned
# 创建 SG 实例
my_sg = SG_BuLin(n=n, band=band)
实现方法3
使用内建的 SG_Bool 和 CROSS 指标来实现,代码如下:
buyind = CROSS(CLOSE, upper)
sellind = CROSS(lower, CLOSE)
my_sg = SG_Bool(buyind, sellind)
实现方法4
完全使用 C++ 实现,和前述 python 的实现方法2 类似。这里不再贴代码了,在新版本 2.0.7 中,扩展了原来内建的 SG_Band 使其支持 3 个指标的调用方式,如:my_sg = SG_Band(CLOSE, lower, upper)
实现方法总结
上述实现方法1和2都是使用纯 python 实现方法,其中由于在 python 中使用循环,其执行的性能会比方式3、4要慢大约10倍。所以,建议实现时尽量优先使用内建指标和函数的方式去实现,比如方法3。如果有 C++ 编程经验,可以使用方法4。使用 C++ 实现不一定非要在 hikyuu 的源码中实现,而是可以在 hub 中快捷创建,这个以后介绍。
创建系统实例进行回测
这个策略很简单,没有设置止损、止盈之类的,我在星球的hub中已经实现了趋势布林带的SG和SYS,可以参考自己在 jupyter 中重建系统实例并回测(注:公开的 hikyuu hub 地址: https://gitee.com/fasiondog/hikyuu_hub.git,该默认 hub 中的代码会滞后星球,之前发布的趋势双均线系统策略已经发布在 hikyuu hub 上,可供下载)。欢迎大家加入星球,支持开源项目发展。
回测代码及结果
执行交易记录如下:
看下所有 A 股的绩效
stks = [s for s in blocka if s.valid and s.start_datetime < start_date]
# stks = [s for s in sm if s.valid and s.type == constant.STOCKTYPE_ETF]
print(len(stks))
my_sys = get_part("start.sys.趋势布林带")
总共有盈利的984支,亏损的2744支(就不贴图了)。ETF的测试结果,盈利278支,亏损484。总体结果比之前的趋势双均线要好不少,其对趋势明显的的证券,捕获能力还是很强的,或许自行修改圈定少量的股票,就能够带来盈利。
以上代码已在知识星球中分享,星球用户请自行取用
文章评论