本文将探索新的策略回测程序,主要是为了尝试不同的技术指标在backtrader平台上的应用,为后续复杂策略的实现做准备。
本文将实现的策略是,当股票放量突破布林线中轨时进行买入,当股票收盘价低于短期均线时卖出。
买入条件中,放量突破布林线中轨具体指的是,当日股票开盘价在布林线中轨下方,收盘价在布林线中轨上方,当日成交量为10日以来的最高量。卖出条件中,短期均线选取为5日线。回测初始资金100000元,单笔操作单位1000股,佣金千分之一,回测时间自2018年1月1日至2020年3月20日。
策略核心代码还是位于策略类的init方法中:
def __init__(self):
self.inds = dict()
for i, d in enumerate(self.datas):
self.inds[d] = dict()
# 布林线中轨
boll_mid = bt.ind.BBands(d.close).mid
# 买入条件
self.inds[d]['buy_con'] = bt.And( \
# 突破中轨
d.open < boll_mid, d.close > boll_mid, \
# 放量
d.volume == bt.ind.Highest(d.volume, period = self.p.p_period_volume, plot = False))
# 卖出条件
self.inds[d]['sell_con'] = d.close < bt.ind.SMA(d.close, period = self.p.p_sell_ma)
这里需要注意的是,技术指标在backtrader里是lines对象,而非数值,所以在使用与或操作时,不能使用python自带的and和or操作符,而只能调用backtrader的And和Or函数。对技术指标做比较时可以使用大于号、小于号等符号,这是因为backtrader对这些符号进行了重写。
回测000001后的最终资产为101107.35元:
可以看到,该策略并非每笔交易都会盈利,但是盈利额度较大,亏损额度较小。由于我们选取的交易单位是1000股,而000001的股价只是10元左右,相当于我们只动用了不到20%的资金,因此总盈利额显得较小,如果提高交易手数,盈利总额也会随之提升。
同时回测000001、000002后的最终资产为100082.86元:
同时回测000001、000002、000004后的最终资产为100247.27元:
回测603999后的最终资产为98871.44元:
可以看到该策略也有总体亏损的情况,直觉上判断,该策略可能比较适合大盘股,对小盘股而言,放量的条件比较容易达到,策略缺乏稳定性。
我们进一步尝试将放量突破的标准选为20日以来的最高值时,回测000001的最终资产为100613.27元,可以看到3笔盈利交易,1笔亏损交易。
当我们把回测起始时间改为2008年1月1日后,回测0000001的最终资产为103137.30元,可以看到也存在大量的亏损交易,但是亏损值都较小。
友情提示:本系列学习笔记只做数据分析,记录个人学习过程,不作为交易依据,盈亏自负。
放量突破布林线中轨买入策略代码:
from __future__ import (absolute_import, division, print_function, unicode_literals)
import datetime # 用于datetime对象操作
import os.path # 用于管理路径
import sys # 用于在argvTo[0]中找到脚本名称
import backtrader as bt # 引入backtrader框架
import pandas as pd
stk_num = 1 # 回测股票数目
# 创建策略
class BollStrategy(bt.Strategy):
# 可配置策略参数
params = dict(
p_period_volume = 10, # 前n日最大交易量
p_sell_ma = 5, # 跌破该均线卖出
p_oneplot = False, # 是否打印到同一张图
pstake = 1000, # 单笔交易股票数
)
def __init__(self):
self.inds = dict()
for i, d in enumerate(self.datas):
self.inds[d] = dict()
# 布林线中轨
boll_mid = bt.ind.BBands(d.close).mid
# 买入条件
self.inds[d]['buy_con'] = bt.And( \
# 突破中轨
d.open < boll_mid, d.close > boll_mid, \
# 放量
d.volume == bt.ind.Highest(d.volume, period = self.p.p_period_volume, plot = False))
# 卖出条件
self.inds[d]['sell_con'] = d.close < bt.ind.SMA(d.close, period = self.p.p_sell_ma)
# 跳过第一只股票data,第一只股票data作为主图数据
if i > 0:
if self.p.p_oneplot:
d.plotinfo.plotmaster = self.datas[0]
def next(self):
for i, d in enumerate(self.datas):
dt, dn = self.datetime.date(), d._name # 获取时间及股票代码
pos = self.getposition(d).size
if not pos: # 不在场内,则可以买入
if self.inds[d]['buy_con']: # 如果金叉
self.buy(data = d, size = self.p.pstake) # 买买买
elif self.inds[d]['sell_con']: # 在场内,且死叉
self.close(data = d) # 卖卖卖
def notify_trade(self, trade):
dt = self.data.datetime.date()
if trade.isclosed:
print('{} {} Closed: PnL Gross {}, Net {}'.format(
dt, trade.data._name, round(trade.pnl, 2), round(trade.pnlcomm, 2)
))
cerebro = bt.Cerebro() # 创建cerebro
# 读入股票代码
stk_code_file = '../TQDat/data/tq_stock_code.csv'
stk_pools = pd.read_csv(stk_code_file, encoding = 'gbk')
if stk_num > stk_pools.shape[0]:
print('股票数目不能大于%d' % stk_pools.shape[0])
exit()
for i in range(stk_num):
stk_code = stk_pools['code'][stk_pools.index[i]]
stk_code = '%06d' % stk_code
# 读入数据
datapath = '../TQDat/day/stk/' + stk_code + '.csv'
# 创建价格数据
data = bt.feeds.GenericCSVData(
dataname = datapath,
fromdate = datetime.datetime(2018, 1, 1),
todate = datetime.datetime(2020, 3, 31),
nullvalue = 0.0,
dtformat = ('%Y-%m-%d'),
datetime = 0,
open = 1,
high = 2,
low = 3,
close = 4,
volume = 5,
openinterest = -1
)
# 在Cerebro中添加股票数据
cerebro.adddata(data, name = stk_code)
# 设置启动资金
cerebro.broker.setcash(100000.0)
# 设置佣金为千分之一
cerebro.broker.setcommission(commission=0.001)
cerebro.addstrategy(BollStrategy, p_oneplot = False) # 添加策略
cerebro.run() # 遍历所有数据
# 打印最后结果
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.plot() # 绘图
博客内容只用于交流学习,不构成投资建议,盈亏自负!
个人博客:http://coderx.com.cn/(优先更新)
项目最新代码:https://gitee.com/sl/quant_from_scratch
欢迎大家转发、留言。有微信群用于学习交流,感兴趣的读者请扫码加微信!
如果认为博客对您有帮助,可以扫码进行捐赠,感谢!
微信二维码 | 微信捐赠二维码 |
---|---|