交易回测系列一:技术信号回测
本页是一篇关于使用 DolphinDB 进行交易回测的文章,以移动平均线(MA)为例实现技术信号回测,并提供背景说明。
Source: https://dolphindb.cn/blogs/284
What this page covers
- 文章信息与背景:标题、来源、发布日期与系列目标。
- 回测场景设定与数据字段要求。
- MA 信号的判定逻辑与函数实现。
- 不止损回测的交易规则、实现与函数说明。
- 止损回测:止损点判断与回测实现(含 segmentby 与部分应用)。
- 统计指标计算方法(累计盈亏、最大回撤等)。
- 运行实例:数据集、两种回测结果与图表说明。
技能认证特训营第二期报名推广
页面顶部包含限时报名推广信息与链接。
- 提供“技能认证特训营第二期”的报名入口链接。
交易回测系列一:技术信号回测(文章信息与背景)
本部分给出文章标题、来源与发布日期,并说明系列目标与 MA 指标在回测中的背景。
- 文章署名/来源为 DolphinDB。
- 文章发布日期为 2021-05-14。
- 系列目标是介绍如何使用 DolphinDB 进行交易回测。
- 示例以移动平均线(MA)实现技术信号回测。
- MA 指标被描述为一种趋势指标。
回测场景与数据字段要求
回测考虑不止损与止损两种情况,并列出数据表所需字段。
- 回测包含“不止损回测”和“止损回测”两种情况。
- 数据表需要字段:sym(股票代码)。
- 数据表需要字段:date(日期)。
- 数据表需要字段:close(收盘价格)。
定义 MA 信号
本部分给出 MA 交易信号的判定逻辑,并提供 maSignal 函数实现方式。
- 短期均线大于长期均线时,认为出现 MA 交易信号。
- maSignal 将信号定义为 mavg(x, shortHorizon) > mavg(x, longHorizon)。
- maSignal 将前 min(x.size(), longHorizon-1) 个信号置为 NULL。
不止损回测:交易规则、实现与函数说明
本部分定义不止损回测交易规则,给出 backtest 函数输出与盈亏计算,并解释若干内置函数及数据对齐方式。
- 当 prevSignal=false 且 signal=true 时,买入多头头寸(long position)。
- 当 prevSignal=true 且 signal=false 时,卖出空头头寸(short position)。
- 不触发买卖规则时,保持与前一天相同的头寸。
- backtest 返回字段:sym,date,close,signal,position,以及 pnl。
- pnl=position*(close - prevClose),并仅保留 isValid(position) 的记录。
相关内置函数(文中说明)
- _iif(condition, trueResult, falseResult) 用于条件分支选择。
- _int() 返回 int 类型的 NULL 值。
- _prev(x) 将向量元素整体右移一个位置。
- _ffill(x) 用 NULL 前的非 NULL 值填充 NULL。
- _isValid() 用于检查元素是否为 NULL(NULL 返回 0,否则返回 1)。
- 使用 prev() 对齐前一天收盘价 prevClose 与前一天信号 prevSignal。
- position=1 表示买入,position=-1 表示卖出,position=NULL 表示保持不变。
止损回测:止损点判断与回测实现
本部分介绍 stoploss 用于判断止损点,并给出 backtest_stoploss 的实现思路(含 segmentby 与部分应用)。
- stoploss(ret, threshold) 返回布尔向量 indicator,用于判断是否需要止损。
- stoploss 计算 cumret=cumprod(1+ret)。
- stoploss 计算 drawDown=1 - cumret/cumret.cummax()。
- 当 drawDown >= threshold 时认为应当止损。
- firstCutIndex=at(drawDown>=threshold).first()+1。
- firstCutIndex 加 1 的原因:到收盘时才知道是否需要止损/止盈。
- indicator 初始为 false;若 firstCutIndex 有效则从该位置起设为 true。
- backtest_stoploss 同时计算 pnl 与 ret=(close-prevClose)/prevClose,并生成 stoplossInd。
- stoplossInd = segmentby(stoploss{,thresholdDrawDown}, ret, position)(按 sym context)。
- 返回中将 pnl * stoplossInd 作为 pnl,并保留 nostoplossPnl 字段。
- segmentby(func, funcArgs, segment) 对连续相同 segment 分组并应用 func。
- stoploss{, thresholdDrawDown} 表示对 stoploss 的第二个参数进行固定(部分应用)。
- position 的连续阶段可用于分组:连续 1、连续 -1、或连续 NULL。
文中提及的函数(用于说明)
- _cumprod 用于计算累计乘积。
- _cummax 用于计算累计最大值。
- _at(x) 用于找出满足布尔条件的元素位置。
- _first 返回第一个元素。
- _take(x, k) 返回包含 k 个 x 的向量。
- 给出 segmentby 示例输入与输出以解释其分段计算方式。
统计信息计算
本部分提供 calcPerformance 函数,用于计算多项回测统计指标,并以字典形式返回。
- calcPerformance(pnl) 返回字典 dict(STRING, DOUBLE)。
- 计算累计盈亏:cumpnl=pnl.sum()。
- 计算平均盈亏:avgpnl=pnl.avg()。
- 计算样本天数:days=pnl.size()。
- 计算标准差:std=pnl.std()。
- 计算最大回撤:maxDrawdown=(pnl.cumsum().cummax()-pnl.cumsum()).max()。
运行实例:数据集、两种回测结果与图表
本部分使用美国股市 1998-2016 日频数据演示回测,分别给出不止损与止损(2.5%阈值)示例代码与部分结果,并对累计盈亏图作说明。
- 示例数据为美国股市 1998 年到 2016 年的日频交易信息。
- 示例数据集包含 3474 万条记录。
- 示例中使用 maSignal(close, 50, 100) 生成 signal。
- 给出不止损回测的部分结果表(包含多只股票的指标示例)。
- 止损示例预设阈值为 2.5%(backtest_stoploss(t, 0.025))。
- 给出止损回测的部分结果表(包含多只股票的指标示例)。
- 有对“不止损回测累计盈亏图”的文字说明(数值表述为低置信度)。
- 有对“止损回测累计盈亏图”的文字说明(提升表述为低置信度)。
性能与目的说明(含参数免责声明)
本部分描述 DolphinDB 的定位与回测实现/执行方面的主张,并明确示例参数仅用于演示。
- DolphinDB database 被描述为通用的分布式时序数据库。
- 文中提到其内置高效的多范式编程语言,并强调开发效率。
- 文中声明:不考虑止损时,MA 信号计算为 3 行代码。
- 文中声明:不考虑止损时,回测为 3 行代码。
- 目的之一是从技术上帮助金融工程师用 DolphinDB 快速实现交易回测。
- 示例参数仅用于演示,并非实践中的最佳参数。
Facts Index
| Entity | Attribute | Value | Confidence |
|---|---|---|---|
| 交易回测系列一:技术信号回测 | 发布日期 | 2021-05-14 | high |
| DolphinDB | 署名/来源 | DolphinDB | high |
| 本文/系列文章 | 目的 | 介绍如何使用 DolphinDB 进行交易回测,并以移动平均线(MA)为例实现技术信号回测 | high |
| 移动平均线指标(MA) | 指标类型 | 趋势指标 | high |
| 移动平均线指标(MA) | 作用/价值描述 | 除指示趋势外,可用于避免股价下跌错失清仓、减少收益损失并及时止损,也可避免股价上涨错失买入时机从而获得更高收益 | low |
| 回测过程 | 考虑的两种情况 | 不止损回测与止损回测 | high |
| 数据表 | 需要包含字段 | sym(股票代码)、date(日期)、close(收盘价格) | high |
| MA 信号 | 判定规则 | 短期均线大于长期均线时认为是 MA 交易信号 | high |
| maSignal(x, shortHorizon, longHorizon) | 实现方式 | signal = mavg(x, shortHorizon) > mavg(x, longHorizon),并将前 min(x.size(), longHorizon-1) 的信号置为 NULL | high |
| 不止损回测交易算法 | 买入规则 | 若 prevSignal=false 且 signal=true,则买入多头头寸(long position) | high |
| 不止损回测交易算法 | 卖出规则 | 若 prevSignal=true 且 signal=false,则卖出空头头寸(short position) | high |
| 不止损回测交易算法 | 持仓保持规则 | 若不符合买入或卖出规则,则保持与前一天相同的头寸 | high |
| backtest(t) | 输出字段/盈亏计算 | 返回 sym,date,close,signal,position,以及 pnl=position*(close - prevClose)(仅保留 isValid(position) 的记录) | high |
| backtest(t) | 头寸计算含义 | position=1 表示买入,position=-1 表示卖出,position=NULL 表示保持不变 | high |
| _iif(condition, trueResult, falseResult) | 函数说明 | 满足 condition 返回 trueResult,否则返回 falseResult;相当于 if...else 且语法更简洁 | high |
| _int() | 函数说明 | 返回 int 类型的 NULL 值 | high |
| _prev(x) | 函数说明 | 把向量中所有元素向右移动一个位置 | high |
| _ffill(x) | 函数说明 | 使用 NULL 值前的非 NULL 元素填充向量中的 NULL 值 | high |
| _isValid() | 函数说明 | 检查元素是否为 NULL;为 NULL 返回 0,否则返回 1 | high |
| backtest(t) | 数据对齐方法 | 使用 prev() 将前一天收盘价 prevClose 与前一天信号 prevSignal 与当天数据对齐以便计算 | high |
| stoploss(ret, threshold) | 输入/输出 | 返回布尔类型向量 indicator,用于判断是否需要止损 | high |
| stoploss(ret, threshold) | 回撤与阈值触发规则 | 计算 cumret=cumprod(1+ret),drawDown=1 - cumret/cumret.cummax();当 drawDown >= threshold 时认为应当止损,并将 firstCutIndex=at(drawDown>=threshold).first()+1 | high |
| firstCutIndex 计算 | 加 1 的原因说明 | 由于到股市收盘时才知道是否需要止损或止盈,所以 firstCutIndex 要加 1 | high |
| stoploss(ret, threshold) | indicator 赋值规则 | indicator 初始全为 false;若 firstCutIndex 有效且小于 ret.size(),则 indicator[firstCutIndex:] 设为 true | high |
| _cumprod | 函数说明 | 计算累计乘积 | high |
| _cummax | 函数说明 | 计算累计最大值 | high |
| _at(x) | 函数说明 | x 为布尔表达式,找出符合条件 x 的元素的位置 | high |
| _first | 函数说明 | 返回第一个元素 | high |
| _take(x, k) | 函数说明 | 返回包含 k 个 x 的向量 | high |
| backtest_stoploss(t, thresholdDrawDown) | 计算内容 | 在计算 pnl=position*(close-prevClose) 的同时计算 ret=(close-prevClose)/prevClose,并生成 stoplossInd | high |
| backtest_stoploss(t, thresholdDrawDown) | 止损信号生成方法 | stoplossInd = segmentby(stoploss{,thresholdDrawDown}, ret, position)(按 sym context) | high |
| backtest_stoploss(t, thresholdDrawDown) | 止损前后盈亏对比输出 | 返回 pnl * stoplossInd 作为 pnl,并保留 pnl 作为 nostoplossPnl(字段名 nostoplossPnl) | high |
| _segmentby(func, funcArgs, segment) | 函数说明 | 将 funcArgs 按 segment 的连续相同元素分组,并对每个组应用 func | high |
| segmentby 示例 | 输入与输出 | x=1 2 3 0 3 2 1 4 5,y=1 1 1 -1 -1 -1 1 1 1,segmentby(cumsum,x,y) 输出 1 3 6 0 3 5 1 5 10 | high |
| stoploss{, thresholdDrawDown} | 表达式含义 | 定义部分应用,用于固定 stoploss 的第二个参数 thresholdDrawDown | high |
| backtest_stoploss 函数说明 | 分段依据 | 按 position 的阶段分组:连续多个 1 表示持续买入、连续多个 -1 表示持续卖出、连续多个 NULL 表示持续不变 | high |
| calcPerformance(pnl) | 返回类型 | 返回字典(dict(STRING, DOUBLE)) | high |
| calcPerformance(pnl) | 计算指标 | cumpnl=pnl.sum(), avgpnl=pnl.avg(), days=pnl.size(), std=pnl.std(), maxDrawdown=(pnl.cumsum().cummax()-pnl.cumsum()).max() | high |
| 运行实例数据集 | 数据范围与频率 | 使用美国股市从 1998 年到 2016 年股票的每日交易信息 | high |
| 运行实例数据集 | 记录数 | 共包含 3474 万条记录 | high |
| MA 信号示例计算 | 参数设置 | maSignal(close, 50, 100) 作为 signal | high |
| 止损回测示例 | 阈值设置 | 预设阈值设为 2.5%(positions = backtest_stoploss(t, 0.025)) | high |
| 不止损回测示例结果表(节选) | 股票 A 的指标 | cumpnl=48.75, avgpnl=0.0108, days=4,513., std=1.5895, maxDrawdown=106.55 | high |
| 不止损回测示例结果表(节选) | 股票 AA 的指标 | cumpnl=7.9625, avgpnl=0.0017, days=4,624., std=1.131, maxDrawdown=119.75 | high |
| 不止损回测累计盈亏图(AI 说明) | 最终累计盈亏(约) | 最终累计盈亏约为 100,000 | low |
| 止损回测示例结果表(节选) | 股票 A 的指标(止损) | cumpnl=58.2775, avgpnl=0.0129, days=4,513., std=1.5731, maxDrawdown=102.125 | high |
| 止损回测示例结果表(节选) | 股票 AA 的指标(止损) | cumpnl=20.47, avgpnl=0.0044, days=4,624., std=1.1126, maxDrawdown=110.8125 | high |
| 止损回测累计盈亏图(AI 说明) | 峰值累计收益(接近) | 峰值接近 200,000,并称加入止损后最终累计收益显著提升 | low |
| DolphinDB database | 产品定位描述 | 通用的分布式时序数据库,内置高效的多范式编程语言,开发效率高 | low |
| 回测实现代码量(不考虑止损) | MA 信号计算代码行数 | 3 行代码计算 MA 信号 | medium |
| 回测实现代码量(不考虑止损) | 回测代码行数 | 3 行代码进行回测 | medium |
| 不止损回测执行性能(文中声明) | 执行耗时 | 对美国股市 18 年全部股票按日回测,不止损回测执行耗时仅 4 秒多 | low |
| 止损回测执行性能(文中声明) | 执行耗时 | 止损回测仅 7 秒多 | low |
| 本文 | 目标读者/目的说明 | 从技术上帮助金融工程师使用 DolphinDB 快速实现交易回测 | high |
| 文中示例参数 | 免责声明/适用性说明 | 长短线时间、止损阈值、数据过滤方法等参数仅用于演示,并非实践中的最佳参数 | high |
| 技能认证特训营第二期 | 报名链接 | https://www.qingsuyun.com/h5/e/217471/5/ | high |