回测系统

基础概念

在开始编写策略系统之前,需要了解一些基础概念,搞明白什么是回测系统?怎么进行回测?如何进行回测分析?并且在回测中需要注意的一些要素,真正的做到有效的策略回测。
根据回测的精度可分为Bar回测和Tick回测,所谓的Bar是指一根包含上下引线的柱体,由开盘价、最高价、最低价、收盘价、成交量、成交额、持仓量、时间等组成的分钟、小时、天、周或年的数据,这种方式回测速度快但精度不够细,适用于中低频策略;而Tick由最新价、卖一价、卖一量、买一价、买一量、成交量、成交金额、持仓量、时间等组成的实时数据,这种方式精度细回测慢,适用于高频策略或针对中低频策略进行分段精细测试。
回测了最关键的是数据,但数据清理和检验是一件繁琐但又非常重要的事情,因为数据是策略回测的根源,根源出错了,所有的分析都是白费,所以找一份靠谱的数据源至关重要。
而对于期货品种来说,会涉及到换月问题,这里引入“主力复权合约”这个名词,主力换月通常会根据当天收盘后的成交量或持仓量进行判断是否需要将老的主力合约切到最活跃的合约上去,同时将换月缺口补齐,让数据的连续性更好,这样就形成了主力复权合约;通常历史回测需要加载较长一段时间数据进行测试,并且要考虑换月问题,所以用处理好的主力复权合约数据进行回测是最方便和最科学的方式。
有了历史数据就可以通过程序按时间先后顺序进行遍历,在遍历过程中加载策略逻辑,通过逻辑发出买卖信号并模拟产生成交,同时对每一笔的交易进行统计,如:记录交易次数、盈亏、手续费、最大盈利、最大回撤等分析指标。最终生成一份完整的策略报告用于评测策略的优劣。
策略编写时常常还会用到指标,所谓的指标即用一组数据利用各种数学公式计算得出一组新的数据,通常指标分为趋势类、震荡类、量能类等,如:MA均线、KDJ、MACD、BOLL、RSI、SAR、CCI、VOL等。而这些指标
有了这些概念,可以通过各种方式进行回测,如果数据不大,Excel也能达到回测效果,但是为了能更高效进行回测,需要搭建自己的回测系统,把一些重复的代码抽出来复用,并搭建好回测系统的结构,让策略编写人员的专注力放在策略逻辑上和分析上,而不是侧重于对数据整理、指标封装、信号入库等,本教程回测系统的架构如下:


为了方便今后回测系统与实盘系统代码一致,我们把回测的策略内部架构改成与实盘一致的异步架构,并有以下几个函数组成:InitStrategy、RtnTickData、RtnBarData、RtnRspOrder、RtnRspTrade等。

注意点

  1. 策略编写过程中避免使用未来函数
  2. 报告分析时必须考虑滑点,实盘滑点是避免不了的
  3. 实盘交易时考虑换月问题和下单方式(需封装算法单)
  4. 盘后需要对信号进行检验,避免实盘和回测的运行结果不一致
  5. 定期回顾策略,不断优化、顺应市场

下面我们分别对指标库、回测中心、回测工人进行代码解析,并实战演练一下策略开发,并且对策略进行分析。

指标库

概念介绍

指标的某一时间点的Bar数据是对应这个时间点的一组指标数据,所以内部Bar的序列数量和指标序列数量是一一对应的,由于一个指标一个时间点存在多个值,所以用Dictionary进行分组保存每个时间点的值,最后通过遍历每个时间点,根据Bar数据进行公式计算即可得出一组指标数据,并将数据存放于ValueDict中供用户调用。基类中的GraphDict是用于存放画图用的值,后期介绍图形库的时候会详细介绍,下面看下具体指标基类的代码:

public class BaseIndicator{private int _count;private int _maxCacheCount = 10;protected List<BarData> barDatas;protected Dictionary<string, List<double>> valueDict;protected Dictionary<string, IndicatorGraph> graphDict;public BaseIndicator(List<BarData> bars){barDatas = new List<BarData>();if (bars != null)barDatas.AddRange(bars);_count = barDatas.Count;valueDict = new Dictionary<string, List<double>>();graphDict = new Dictionary<string, IndicatorGraph>();}protected virtual void Caculate(){if (IsSimpleMode){if (barDatas.Count > MaxCacheCount){barDatas.RemoveRange(0, barDatas.Count - MaxCacheCount);}graphDict.Clear();GC.Collect();}}#region 公共属性/// <summary>/// 指标名称/// </summary>public string Name { get; set; }/// <summary>/// 指标描述/// </summary>public string Description { get; set; }/// <summary>/// 是否在主图上显示/// </summary>public bool IsShowInMain { get; set; }/// <summary>/// 标签/// </summary>public string Tag { get; set; }/// <summary>/// 精简模式/// </summary>public bool IsSimpleMode { get; set; }/// <summary>/// 数据字典/// </summary>public Dictionary<string, List<double>> ValueDict { get { return valueDict;
} }/// <summary>/// 图形字典/// </summary>public Dictionary<string, IndicatorGraph> GraphDict { get { return
graphDict; } }/// <summary>/// 数量/// </summary>public int Count{get { return _count; }set{_count = value;}}/// <summary>/// 缓存Bar数量/// </summary>public int MaxCacheCount{get { return _maxCacheCount; }set{_maxCacheCount = value;}}#endregion#region 公共方法/// <summary>/// 添加参考线/// </summary>/// <param name="name"></param>/// <param name="value"></param>/// <param name="color"></param>/// <param name="lineStyle"></param>public void AddGuidLine(string name, double value, Color color,
EnumLineStyle lineStyle = EnumLineStyle.DotLine){if (!graphDict.ContainsKey(name)){var graph = new IndicatorGraph() { Name = name, LineStyle =
lineStyle };graph.AddValue(value, color);graphDict.Add(name, graph);}}/// <summary>/// 删除参考线/// </summary>/// <param name="name"></param>public void DelGuidLine(string name){graphDict.Remove(name);}/// <summary>/// 根据索引获取Bar数据(0->最新一根,1->前一根,以此推类)/// </summary>/// <param name="index"></param>/// <returns></returns>public BarData GetBarData(int index = 0){if (index < Count)return barDatas[barDatas.Count - index - 1];elsereturn null;}/// <summary>/// 获取最新缓存Bar数据/// </summary>/// <returns></returns>public List<BarData> GetBarDatas(){return barDatas;}/// <summary>/// 绑定数据/// </summary>/// <param name="datas"></param>public void BindData(List<BarData> datas){barDatas.Clear();barDatas.AddRange(datas);Caculate();}/// <summary>/// 添加Bar数据至最后/// </summary>/// <param name="bar"></param>public virtual void AddBarData(BarData bar){barDatas.Add(new BarData(bar));if (IsSimpleMode && barDatas.Count > MaxCacheCount){barDatas.RemoveAt(0);}_count++;}/// <summary>/// 更新Bar数据/// </summary>/// <param name="bar"></param>/// <returns></returns>public virtual void UpdateBarData(BarData bar){barDatas[barDatas.Count - 1] = new BarData(bar);}/// <summary>/// 插入Bar数据/// </summary>/// <param name="index"></param>/// <param name="bar"></param>public void InsertBar(int index, BarData bar){if (index < barDatas.Count){barDatas.Insert(index, new BarData(bar));}else{barDatas.Add(bar);}if (barDatas.Count > MaxCacheCount){barDatas.RemoveAt(0);}_count++;Caculate();}/// <summary>/// 批量添加Bars/// </summary>public void AddBars(List<BarData> bars){barDatas.AddRange(bars);_count += bars.Count;Caculate();}/// <summary>/// 批量插入Bars/// </summary>/// <param name="index"></param>/// <param name="bars"></param>public void InsertBars(int index, List<BarData> bars){if (index < barDatas.Count){if (bars[bars.Count - 1].RealDateTime ==
barDatas[index].RealDateTime){barDatas.RemoveAt(index);//删除第一根实时接收Bar数据_count--;}_count += bars.Count;barDatas.InsertRange(index, bars);Caculate();}}/// <summary>/// 获取最新一组数据/// </summary>/// <returns></returns>public Dictionary<string, double> GetLastValues(){Dictionary<string, double> resultDict = new Dictionary<string,
double>();foreach (var vd in valueDict){if (Count != 0)resultDict.Add(vd.Key, vd.Value[Count - 1]);elseresultDict.Add(vd.Key, JPR.NaN);}return resultDict;}#endregion}

案例介绍——MA指标

MA是最简单的一个指标,又称“简单移动平均线”,通常有一个长度参数,用于计算这个长度的数据的平均值。

构造函数中需传入barDatas数据集合,isShowInMain和tag都是后期画图用参数,其余参数可以根据策略不同定义不同参数。
SetParameters用于调整参数并重新计算指标值。
GenerateSMA计算指标值,并将结果存放于valueDict中,主要逻辑全部在该函数内实现。
UpdateBarData和AddBarData在新数据过来时进行更新指标。
GetData、GetValues、GetValue、GetLast等都是取值函数。

public class MA : BaseIndicator{private EnumBarStruct barStruct;private int _length;private AverageS averageS;public MA(List<BarData> barDatas, EnumBarStruct objBarStruct =
EnumBarStruct.Close,int length = 10, bool isSimpleMode = true, bool isShowInMain = true,
string tag = "1"): base(barDatas){Tag = tag;_length = length;barStruct = objBarStruct;IsSimpleMode = isSimpleMode;string paramTag = string.Format("({0},{1})", barStruct.ToString(),
_length);Name = string.Format("MA{0}", paramTag);Description = "移动平均";valueDict.Add("MA", new List<double>());if (!IsSimpleMode){graphDict.Add("MA", new IndicatorGraph() { Name = "MA", Tag =
paramTag, LineStyle = EnumLineStyle.SolidLine });}averageS = new AverageS();IsShowInMain = isShowInMain;Caculate();}public void SetParameters(EnumBarStruct objBarStruct, int length){if (objBarStruct != barStruct || length != _length){string paramTag = string.Format("({0},{1})",
objBarStruct.ToString(), length);Name = string.Format("MA{0}", paramTag);if (!IsSimpleMode){graphDict["MA"].Tag = paramTag;}barStruct = objBarStruct;_length = length;Caculate();}}protected override void Caculate(){valueDict["MA"].Clear();if (!IsSimpleMode){graphDict["MA"].Clear();}averageS.SetParameters(_length);if (barDatas != null && Count != 0){for (int i = 0; i < Count; i++){GenerateSMA(i);}}base.Caculate();}private void GenerateSMA(int i){BarData curData = null;if (i >= barDatas.Count){curData = GetBarData(0);}else{curData = barDatas[i];}double value = averageS.AddValue(GetData(curData));valueDict["MA"].Add(value);if (!IsSimpleMode){graphDict["MA"].AddValue(value, Color.Yellow);}}public override void UpdateBarData(BarData bar){base.UpdateBarData(bar);valueDict["MA"].RemoveAt(Count - 1);if (!IsSimpleMode){graphDict["MA"].RemoveLast();}averageS.RemoveLast();GenerateSMA(Count - 1);}public override void AddBarData(BarData bar){base.AddBarData(bar);GenerateSMA(Count - 1);}private double GetData(BarData bar){double data;switch (barStruct){case EnumBarStruct.Open:data = bar.Open;break;case EnumBarStruct.High:data = bar.High;break;case EnumBarStruct.Low:data = bar.Low;break;case EnumBarStruct.Close:data = bar.Close;break;case EnumBarStruct.Volume:data = bar.Volume;break;case EnumBarStruct.OpenInterest:data = bar.OpenInterest;break;case EnumBarStruct.Amount:data = bar.Amount;break;default:data = 0;break;}return data;}public List<double> GetValues(){return valueDict["MA"];}public double GetValue(int index){if (index >= 0 && index < Count)return valueDict["MA"][index];elsereturn JPR.NaN;}public double GetLast(){if (Count != 0)return valueDict["MA"][Count - 1];elsereturn JPR.NaN;}public int Length{get { return _length; }}public int DataType{get { return barStruct.GetHashCode(); }}}

开源分布式量化交易系统——回测系统(一)相关推荐

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

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

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

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

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

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

  4. 开源分布式量化交易系统——开篇

    目录 前言 量化概念 准备工作 架构设计 后端模块 交易中心 行情中心 算法工人 前端模块 策略编辑 策略回测 策略仿真 系统部署 策略分享 回顾与后记 前言 本人是一名计算机专业毕业的普通程序员,机 ...

  5. 开源分布式量化交易系统——架构设计

    准备工作 搭建一套量化系统并非一件容易的事,如果你是一位初出茅庐的程序猿,在下文中如遇到不懂的知识点请自行查阅相关资料,本文也会推荐一些文章和书籍,个人认为作为一名合格的程序猿,最基本的自学能力和探究 ...

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

    获取50ETF指数行情数据 50ETF期权的标的物是50ETF指数,我们可以使用akshare来获取该指数的日行情数据,如下所示: class Sh50etfIndexDataSource(objec ...

  7. python外汇交易回测系统_易经量化交易系统之回测系统1

    我们在这里向大家介绍如何从零开始,实现一个适合于A股市场的回测系统.在这里我们以A股日K线数据为例,实际上可以比较方便的扩展为分级的数据源. Tick数据类 我们首先定义一个Tick数据的基类,这个类 ...

  8. 目前市面上量化交易软件、平台、框架的特点, 重点推荐VNPY底层仿真回测系统

    随着这几年量化交易在国内的快速发展,各种API接口.量化平台.量化交易框架匆匆推出,呈现出一片百花齐放,欣欣向荣的景象,但是由于目前国内还处于私募基金发展的初期,大部分平台也都匆匆上马,既有自己的特点 ...

  9. 使用java开发一个股票交易及量化投资回测分析系统

    经过近两年的研究和学习,我使用java开发(也使用了部分python,数据源:聚宽)出了一个股票交易及量化投资回测分析系统,将于近期陆续推出系列文章,向大家介绍一下整个开发过程,当整个系列文章都完成后 ...

最新文章

  1. PostgreSQL专题
  2. [zz]grep 命令的使用
  3. wifi管理系统_如何有效选择一款移动考勤管理系统
  4. html漂亮的表格模板+背景_咨询amp;金融主题响应式网站着陆页模板
  5. 【剑指offer】连续子数组的最大和(未完待续。。。)
  6. H2介绍 – Java嵌入式数据库
  7. 2021qq服务器维护到什么时候,2021QQ扩列下架了吗怎么没了?QQ暂停服务到什么时候...
  8. 万特电能表接线仿真系统 软件_电工技能——分享一款超实用的电工仿真教学接线Flash动画软件...
  9. 计算机word设置斜框线,Word绘制多线斜线表头技巧-word技巧-电脑技巧收藏家
  10. 南京大学计算机学院刘向阳,刘向阳  南京大学计算机科学与技术系教授_光通信名人录_光邻网...
  11. 怎样把英文pdf翻译成中文?
  12. colorbox去除close关闭按钮,附上colorbox的基本使用方法
  13. 腾讯云php小程序,使用微信小程序和腾讯云实现直播功能
  14. matlab 理想变压器,Simscape Electrical
  15. AI2022:如何在 Illustrator 中创建色板?
  16. 专访盖国强李轶楠丨通过数据库服务能力评估背后的故事
  17. RuntimeError: cuda runtime error (8) : invalid device function at /pytorch/torch/lib/THC/generic/THC
  18. 天源财富:意法半导体推出下一代MEMS加速度计 可用于高性能汽车
  19. 内温的整体优先效应实验_第四章 知觉10.ppt
  20. 表示转折时yet与but的区别是什么

热门文章

  1. 怎么删除win11系统休眠文件?
  2. Caused by: com.google.common.util.concurrent.ExecutionError:
  3. 当添加对MEF插件项目的引用时,为什么会出现警告图标?
  4. FPGA 学习之路(一)EDA软件设计流程
  5. 网络验证的破解-用SPI修改网络封包
  6. cp dd 用法与区别
  7. OEA ORM中的分页支持
  8. Linux LNMP源码架构部署 | Nginx服务 | Mysql服务 | php服务 | 论坛源码编译安装 | 超详细
  9. CCNA静态路由实验
  10. 笨方法学python笔记(4)