系列文章目录

第一章 神龙摆尾


文章目录

目录

系列文章目录

文章目录

前言

一、所涉结构模型简介

二、策略实现

1.策略源代码

2.初步参数调优-选股参数

3.进一步参数调优-卖出参数

总结


前言

本章围绕一个结构模型,基于backtrader框架,从策略实现、参数调优等方面探讨量化交易实践。

一、所涉结构模型简介

本模型来自网易公开课,是一个波段上涨模型,名曰亢龙有悔,模型基本示意如下(图片截取自公开课视频)

模型要点如下:

二、策略实现

1.策略源代码

import backtrader as btclass StrategyClass(bt.Strategy):'''神龙摆尾'''params = dict(ma = 'S',         ## 'S': SMA、'E':EMA、'W':WMAperiod_b1 = 22,    # 前箱体长度period_b2 = 5,     # 后箱体长度boxR = 8,       # 前箱体检测,60、22、10 三均线最大差值放大boxR倍仍不高于股价breakdown = 0,  # 后箱体接受几次下突破preboxnohigh = False, # 前箱体不能高于涨停价,此参数经测试移除,详见 https://gitee.com/kuge/backtrader-study/commit/69b56932ecb62d4fb35a7e941ff8fda5c5d2eff7needvcd = True,    # 需要量均死叉maxuplimit = -1,    # 后箱体等待期涨停上限,负数表示不限制postboxV = 2,      # 后箱体规则,1:公开课版本; 2: 正课版本,3:自动切换closeup1 = 1.3,    # 目标止盈closeup2 = 1.1,    # 顶部回落止盈closedown = 0.85,  # 止损code = None,name = None,log = True,)def log(self, txt, dt=None, force=False):if force or self.p.log:dt = dt or self.data.datetime.datetime()who = f'{self.p.code} {self.p.name} ' if self.p.code else ''print(f'{dt.isoformat()} {who}{txt}')def notify_order(self, order):if order.status in [bt.Order.Submitted, bt.Order.Accepted]:returnif order.status == order.Completed:if order.isbuy():buytxt = '买入价:%.2f, 量:%s, 持仓:%s' % (order.executed.price, order.executed.size, self.position.size)self.log(buytxt, bt.num2date(order.executed.dt))else:selltxt = '卖出价, %.2f, 量:%s, 持仓:%s' % (order.executed.price, order.executed.size, self.position.size)if 'log' in order.info:selltxt = '%s %s' % (selltxt, order.info.log)self.log(selltxt, bt.num2date(order.executed.dt))elif order.status in [order.Expired, order.Canceled, order.Margin]:if 'log' not in order.info:self.log('%s , %s' % (order.Status[order.status], order))passdef __init__(self):ma = bt.ind.EMA if self.p.ma == 'E' else bt.ind.WMA if self.p.ma == 'W' else bt.ind.SMAvma5 = ma(self.data.volume, period=5)vma10 = ma(self.data.volume, period=10)ma10 = ma(self.data, period=10)ma22 = ma(self.data, period=22)ma60 = ma(self.data, period=60)self.xxx = (ma10, ma22, ma60)self.yyy = bt.Max(abs(ma60 - ma22), abs(ma60 - ma10), abs(ma22 - ma10))self.zzz = bt.Max(abs(ma60 - ma22), abs(ma60 - ma10), abs(ma22 - ma10)) * self.p.boxR# 涨停self.limitup = (self.data.close(-1) * 1.1 - 0.01 <= self.data.close)# 前箱体最高self.preBoxHigh = bt.ind.Highest(self.data.high, period=self.p.period_b1)# 横盘-均线贴近self.preBox = bt.Max(abs(ma60 - ma22), abs(ma60 - ma10), abs(ma22 - ma10)) * self.p.boxR < self.data# 后箱体上沿self.boxHigh = bt.ind.Highest(self.data, period=self.p.period_b2)# 后箱体下沿self.boxLow = bt.ind.Lowest(self.data, period=self.p.period_b2)# 后箱体均线交叉self.m5c10 = bt.ind.CrossOver(vma5, vma10)self.signal = None # [涨停柱后静默期,下突破线,下突破计数,死叉,上破线]self.order = Nonedef onLimitup(self):self.signal = [len(self) + self.p.period_b2,    # 0 静默期self.data.close[0],              # 1 箱体下突破线self.p.breakdown,                # 2 下突破计数False,                          # 3 死叉self.data.close[0],             # 4 上突破线None,                           # 5 买单None,                           # 6 止赢单self.data.close[-1],            # 7 箱体V1下线0,                              # 8 后箱体涨停计数]def checkBreakup(self):curr = len(self)close = self.data.close[0]s = self.signalif self.limitup[0]:s[8] += 1else:if close < s[7]:  # 向下突破self.log('跌穿V1箱体,重新等待涨停')self.signal = Nonereturn Falseif close < s[1]:  # 向下突破s[2] -= 1if s[2] < 0 and self.p.postboxV == 2:self.log('跌穿箱体,重新等待涨停')self.signal = None  # 重新来过return Falseif not s[3]:      # 未见死叉s[3] = self.m5c10 < 0if curr == s[0]:  # 箱体长度达标v = self.p.postboxVif v == 3:v = 1 if s[2] < 0 else 2if v == 2:s[4] = self.boxHigh[0]else:s[4] = s[1]s[1] = s[7]elif curr > s[0] and close > s[4]:   # 箱后突破if s[3] or not self.p.needvcd:   # 已有死叉return Trueelse:self.log('万事俱备,只欠死叉,继续等待')s[4] = closeif self.p.maxuplimit >= 0 and s[8] > self.p.maxuplimit:self.log('后箱体涨停次数超限,取消观察')self.signal = Nonereturn Falsedef next(self):if self.signal:if self.signal[6]:if self.signal[6].status in [bt.Order.Canceled, bt.Order.Completed]:self.signal = Noneelif self.signal[6].status in [bt.Order.Expired, bt.Order.Margin]:print('订单状态异常', self.signal[6].status)elif self.data.close[0] * self.p.closeup2 < self.boxHigh[0]:self.sell(exectype=bt.Order.Market, log=' (高位回落)止盈 ', oco=self.signal[6])elif self.checkBreakup():self.log('向上突破箱体,买入')self.signal[5] = self.buy()self.signal[6] = self.sell(exectype=bt.Order.Limit, price=self.data.close[0] * self.p.closeup1, log=' 止盈 ')self.sell(exectype=bt.Order.Stop, price=self.signal[1], log=' 初始止损 ', oco=self.signal[6])if self.p.closedown > 0:self.sell(exectype=bt.Order.Stop, price=self.data.close[0] * self.p.closedown, log=' 百分比止损', oco=self.signal[6])elif self.limitup[0]:if self.preBox[0]:if self.p.preboxnohigh and self.preBoxHigh[0] > self.data.close[0]:self.log('涨停,但横盘阶段有高过今日涨停价格,不于理会')else:self.log('横盘后涨停,开始观察')self.onLimitup()else:self.log('涨停,但横盘不足')

2.初步参数调优-选股参数

先测选股部分,重点关注机会数量,也顺便初步观察收益

import argparse
import backtrader as bt
from datetime import datetime as dt
from time import time
from multiprocessing import cpu_count, Process
import osfrom os.path import join, exists, dirname
if __name__ == '__main__':import syssys.path.append(dirname(dirname(__file__)))
# 此即上文策略实现
from strategies.netEasyTailwhip import StrategyClass
# 以下涉及完整代码参见 https://gitee.com/kuge/backtrader-study
from utils.localdata import onebyone, download ps = dict(boxR = [7,7.4,7.8,8,8.2,8.6,9],       # 前箱体检测,60、22、10 三均线最大差值放大boxR倍仍不高于股价breakdown = [0,1,2],  # 后箱体接受几次下突破preboxnohigh = [True,False], # 前箱体不能高于涨停价needvcd = [True,False],    # 需要量均死叉maxuplimit = [-1,0,1],    # 后箱体等待期涨停上限,负数表示不限制postboxV = [1,2],      # 后箱体规则,1:公开课版本; 2: 正课版本,3:自动切换
)
ps2 = Nonedef pps(p={}, ks=list(ps.keys()), ps=ps, ps2=ps2):if len(ks) > 0:for x in ps[ks[0]]:yield from pps(dict({f'{ks[0]}': x}, **p), ks[1:], ps, ps2)elif 'afterdown' in p and p['afterdown'] and ps2:yield from pps(p, list(ps2.keys()), ps2, None)else:yield pprint(len([x for x in pps()]))def main(num, pss, StrategyClass=StrategyClass):logfile = join('logs', f's{num:03d}_{dt.now().isoformat()}_{os.getpid()}.log')def log(text, write=False, file=logfile, cache=dict(a=[])):lines = cache['a']lines.append(f'{dt.now().isoformat()} {text}')if write:with open(file, 'a', encoding='utf8') as f:f.write('\n'.join(lines))cache['a'] = ['\n']commparams = dict(log=False, closeup1=1.11, closeup2=1.06, closedown=0.8)import rereplace = re.compile(r'0{4}0+1$')for p in pss:params = dict(commparams, **p)total = [0, 0, 0, 0, 0] # 有交易的证券数,交易总笔数,盈利总笔数,损失总笔数,盈利总和# for daily_data, code, name in [d for d in onebyone(klen=300) if d[1] in ['sh.600378', 'sh.603333']]:for daily_data, code, name in onebyone(klen=300):params['code'] = codeparams['name'] = namecerebro = bt.Cerebro()# 设置启动资金cerebro.broker.setcash(10000000.0)# 设置佣金cerebro.broker.setcommission(commission=0.001)cerebro.addstrategy(StrategyClass, **params)data = bt.feeds.PandasData(dataname=daily_data)cerebro.adddata(data)cerebro.addsizer(bt.sizers.FixedSize, stake=100)  # 每笔交易使用固定交易量,stake代表交易量cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='ta')result = cerebro.run(plot=False)r = result[0].analyzers.ta.get_analysis()if r.total and r.total.total > 0:s = r.total.totall, w = 0, 0if r.total.total - r.total.open > 0:l, w, x = r.lost.total, r.won.total, r.pnl.gross.totalelse:x = cerebro.broker.getvalue() - 10000000log(f'{code} {name} {s} {w} {l} {x:.2f} ')total = [a+b for a,b in zip(total, [1,s,w,l,x])]total[-1] = round(total[-1], 2)log(replace.sub('', f'={total}={p}={dt.now()}'), True)def parse_args():parser = argparse.ArgumentParser(description='神龙摆尾策略参数搜索-多进程')parser.add_argument('-n', default=cpu_count()-1, required=False, type=int, help='进程数')return parser.parse_args()if __name__ == '__main__':args = parse_args()download()params = [x for x in pps()]size = (len(params) // (args.n - 1)) if args.n > 1 else len(params)for i in range(args.n):Process(target=main,kwargs={"num": i, "pss": params[size * i:size * (i + 1)]}).start()print(dt.now().isoformat(), f"主进程ID: {os.getpid()} 进程数量:{args.n} 参数组数量: {len(params)} 每个进程搜索参数组数量:{size}")

测试发现

1、preboxnohigh参数纯属画蛇添足,遂移除。

2、最大机会数量(共4314次交易机会,胜率37%,涉及2145只证券)

2154    4314    1599    2636    3589.04     {'postboxV':1,'maxuplimit':-1,'needvcd':False,'breakdown':0,'boxR':7}
2154    4314    1599    2636    3589.04     {'postboxV':1,'maxuplimit':-1,'needvcd':False,'breakdown':1,'boxR':7}
2154    4314    1599    2636    3589.04     {'postboxV':1,'maxuplimit':-1,'needvcd':False,'breakdown':2,'boxR':7}

3、最高收益(22042.67)

1760    2746    1024    1658    19236.8      {'postboxV':1,'maxuplimit':0,'needvcd':False,'breakdown':0,'boxR':7.4}
1760    2746    1024    1658    19236.8      {'postboxV':1,'maxuplimit':0,'needvcd':False,'breakdown':1,'boxR':7.4}
1760    2746    1024    1658    19236.8      {'postboxV':1,'maxuplimit':0,'needvcd':False,'breakdown':2,'boxR':7.4}
907     1128    422     690     19609.18     {'postboxV':2,'maxuplimit':1,'needvcd':True,'breakdown':2,'boxR':7.4}
865     1063    395     652     20718.98     {'postboxV':2,'maxuplimit':1,'needvcd':True,'breakdown':2,'boxR':8.2}
1118    1500    544     936     20830.49     {'postboxV':2,'maxuplimit':1,'needvcd':False,'breakdown':2,'boxR':7}
874     1076    404     656     21041.59     {'postboxV':2,'maxuplimit':1,'needvcd':True,'breakdown':2,'boxR':8}
934     1167    442     708     22042.67     {'postboxV':2,'maxuplimit':1,'needvcd':True,'breakdown':2,'boxR':7}

3.进一步参数调优-卖出参数

选定 {'postboxV':1,'maxuplimit':-1,'needvcd':False,'breakdown':2,'boxR':7} ,期待通过止盈止损参数提高收益

ps = dict(closeup1 = [x/100 for x in range(105, 118)],    # 目标止盈closeup2 = [x/100 for x in range(103, 110)],    # 顶部回落止盈closedown = [x/100 for x in range(72,90,2)],  # 止损
)

进一步测试发现,如下参数组合可获得显著的性能提升

2149 4340 2705 1550 2925 31900.29 {'closedown':0.72,'closeup2':1.09,'closeup1':1.05}
2149 4340 2705 1550 2925 31900.29 {'closedown':0.74,'closeup2':1.09,'closeup1':1.05}
2149 4340 2705 1550 2925 31900.29 {'closedown':0.76,'closeup2':1.09,'closeup1':1.05}

其中:

  • 胜率 从37%提升到 62.3%,提升了 68%
  • 收益 从22042.67提升到31900.29,提升了 44.7%

另外,三个参数中有两个处于测试范围的边界,下一步可考虑更换范围继续搜索。


总结

本文通过源代码简单演示了如何使用backtrader来实现一个交易策略,并简单介绍了如何通过回测搜索来对策略参数进行调优。

所涉策略仅用于技术探讨,不代表作者认同该模型有效性。

对量化技术感兴趣可加微信交流

量化交易-回测调参-神龙摆尾相关推荐

  1. Pyalgotrade量化交易回测框架

    现在就开始干活了.先要测试一下pyalgotrade回测数据对不对.我找了个参照标准:在聚宽上开通了个账号,按入门教程写了个策略:2016-2018年每个交易日买入100股平安银行(000001),回 ...

  2. python外汇交易回测系统_StarQuant - 综合量化交易回测系统/平台

    Welcome to StarQuant StarQuant(中文名:易数交易系统)是一个轻量的.面向个人( 普通)用户的综合量化交易回测系统,目前主要用于期货期权程序化交易(CTP接口,在实盘测试中 ...

  3. python量化回测框架_股票量化交易回测框架pyalgotrade源码阅读(一)

    PyAlgoTrade是什么呢? 一个股票量化交易的策略回测框架. 而作者的说明如下. To make it easy to backtest stock trading strategies. 简单 ...

  4. 【思考14】量化交易回测中,关于涨跌停的处理方式

    这篇文章免费,可以在知乎看到 有好多人在量化交易的回测过程中,对股票.期货之类的涨跌停比较在意,害怕涨跌停了之后交易不了,本文就尝试梳理下我对涨跌停的看法以及我在回测中的应对方式. 在开始本文对涨跌停 ...

  5. 6款优秀的量化交易回测框架!VNPY位居第一

    一个策略从想法,到测试,在到实盘,然后改进,进入另一个循环,需要很多的时间和精力.这时候选择一款高效.灵活的测试系统就是当务之急了.即使最后你可能需要写自己的系统,但是这些框架的软工架构还是很值得借鉴 ...

  6. 基于强化学习的期权量化交易回测系统1

    概述 量化交易平台很重要的一个环节就是回测系统,可以通过对历史行情的回放,验证量化交易策略的性能表现.量化交易强化学习环境,则是向Agent提供一个交互的环境,Agent(即量化策略)根据市场环境(强 ...

  7. python股票回测_Python量化交易-回测简单的交易策略

    这篇文章主要介绍如何使用Python对一些简单的交易策略进行回测,对这块比较感兴趣的朋友可以看一看. 1.获取证券数据 本文以A股市场为例,先获取A股近10年的数据并保存到数据库. 1.1.安装数据库 ...

  8. 基于强化学习的期权量化交易回测系统5

    我们现在已经可以在主循环中获取行情数据,并且传给了Agent类.接下来Agent类会调用策略类,由于决定采取的行动.在策略类做决策时,需要参考用户仓位Position信息,还有就是权利金.保证金.手续 ...

  9. 5、Python量化交易-回测收益计算

    目录 前言 一.回测的主方法 二.回测实现 1 - 获取回测数据ticks 2 - 运行回测 3 - 为回测数据添加生成方法 4 - sell中添加订单的pnl收益计算 5 - 策略执行中调整买卖ma ...

最新文章

  1. CentOS5.6系统下mysql5安装
  2. leetcode--下一个更大元素II--python
  3. VB.NET 数组的定义 动态使用 多维数组
  4. 树状数组 poj 2352
  5. ROS探索总结(十三)(十四)(十五)——导航与定位框架 move_base(路径规划) amcl(导航与定位)
  6. 推荐一个好用而且免费的XML文件查看工具,高效,易用而且可定制
  7. python全栈开发中级班全程笔记(第二模块、第四章)(常用模块导入)
  8. h5 先加载小图_萌宝学诗|读诗、画诗、唱诗,尽在小图姐姐的《九月九日忆山东兄弟》中!...
  9. python 数据结构 1
  10. hbase 根据主键查询
  11. asp.net中后台javaScrip的使用
  12. Java开发不得不会!java私塾初级模拟银源代码
  13. SiteMesh3简介及使用
  14. Android 简单又炫酷的帧动画(加载动画)
  15. 豆丁网word文档下载方法集锦
  16. 支持向量机(SVM)的原理推导及解释
  17. 毕业论文查重注意事项论文检测查重原理(以PaperPass为例)
  18. mysql median_在MySQL中使用MEDIAN和MAX,MIN和AVG功能
  19. Linux Shel高级技巧(目录)
  20. 李群、李代数只求理解不求数学推导

热门文章

  1. REDIS哨兵【Sentinel】模式+哨兵的核心知识点+redis哨兵主从切换的数据丢失问题+上一章铺垫的【异步复制数据丢失问题】+【集群脑裂】
  2. OpenJudge1758 二叉树
  3. 使用tee命令获取不到输出的解决方案
  4. Sanity Check: SRM, AA test
  5. WriteError Resulting document after update is larger than 16777216
  6. 全新8.6版本SEO快排系统(可源码级搭建)
  7. Python文件处理和异常处理基础知识
  8. CTFHub-SSRF(全部)
  9. Qt/C++ 添加pri文件
  10. APP二维码微信扫描后无法下载的问题 微信扫码(扫一扫二维码)下载不了app怎么解决