运用 R-平方 评估策略余额曲线的品质

13 十二月 2017, 17:13
Vasiliy Sokolov
0
2 407

内容目录


引言

每个交易策略都需要客观评估其有效性。为此, 使用了范围广泛的统计参数。它们当中的很多参数很容易计算, 并可显示直观的衡量数值。而其它参数则在数值构建和解读上更加困难。尽管有这些多样性, 但少有量化衡量指标可以评估一个非凡但同时又明显的数值 - 交易系统的余额曲线的平滑性 。本文提出了解决这个问题的方案。我们来研究这样一个非凡的衡量指标, 即判定系数 R 的平方 (R^2), 它计算每位交易者所期望的最具吸引力, 平滑, 上升余额曲线的量化估值。

当然, MetaTrader 5 终端已经提供了一套开发完毕的汇总报告, 可展示交易系统的主要统计数据。然而, 其中的参数尚有不足。幸运的是, MetaTrader 5 提供了编写自定义评估参数的能力, 这恰恰是我们要做的。我们不仅要建立判定系数 R^2, 而且还要尝试估算其数值, 将其与其它优化准则进行比较, 然后根据基本的统计评估得出规律性。 


评估交易系统的常用统计方法的评议

每次生成交易报告或交易系统回测结果时, 我们都会看到若干个"魔幻数字", 这些 "魔幻数字" 可以用来分析交易品质的结论。例如, MetaTrader 5 终端中的典型测试报告如下所示:

图例 1. 交易策略的回测结果


它包含一些有趣的统计数据或衡量指标。我们来分析它们当中最流行的, 并客观地考察它们的强处和弱点。

Total Net Profit (净利润总计)。该衡量指标示意在测试或交易期间赚取或损失的总金额。这是最重要的交易参数之一。每位交易者的主要目标是利润最大化。有多种方法可以做到这一点, 但最终的结果总归一点, 那就是净利润。净利润并非总是取决于成交的数量, 实际上它独立于其它参数, 但反过来是不对的。因此, 相对于其它衡量指标, 它是 不变的, 所以可以独立于它们使用。不过, 这个衡量指标也有严重的缺陷。

首先, 净利润直接取决于是否使用资本复利。当使用资本复利时, 利润增长呈现非线性。资金经常爆发性指数增长。在这种情况下, 测试结束时所记录的净利润数字往往达到天文数字, 而现实当中却一无所获。如果按照固定手数进行交易, 资金增量更趋线性, 但在这种情况下, 利润取决于所选择的交易量。例如, 如果测试结果如上表所示, 使用 0.1 手的固定交易量进行测试, 则获得的利润 $15,757 可视为一个显赫的结果。如果交易量是 1.0 手, 那么测试结果就逊色很多。这就是为什么有经验的测试人员更喜欢针对外汇市场将手数固定为 0.1 或 0.01。在这种情况下, 余额的最小变化等于金融工具的一个点, 这令分析这个特性更加客观。

其次, 最终结果取决于测试期限的长短或交易历史的持续时间。例如, 上表所列的净利润可能是在 1 年或 5 年内获取的。而在每种情况下, 相同的数字意味着策略的有效性完全不同。

第三, 毛利在上一日期的时间里是固定的。不过, 那个时候资本也许会大幅回撤, 而一星期前可能不会。换言之, 这个参数深度依赖于所选择的测试或生成报告的起点和终点。 

Profit Factor (盈利因子)。这可以说是专业交易者最受欢迎的统计数据。虽说新入行者只想看总利润, 但专业人士发现知晓所投资资本的转折是必不可少的。如果认为交易亏损也是投资的一种, 那么盈利因子就展现了交易的边际性。例如, 如果只有两笔交易, 第一笔交易损失 $1000, 第二笔交易赚取 $2000, 则此策略的盈利因子为 $2000/1000 = 2.0。这是一个非常良好的数字。此外, 盈利因子既不取决于测试时间跨度也未基于交易量手数。因此, 专业人士非常喜欢。但是, 它也有缺点。

其中之一是盈利因子数值高度依赖于成交数量。如果只有少量成交, 得到的盈利因子等于 2.0 甚至 3.0 单位是完全可能的。另一方面, 如果有大量成交, 那么获得 1.5 单位的盈利因子都是一个巨大的成功。 

Expected Payoff (预期收益)。这是一个非常重要的特征, 指明平均交易回报。如果策略有利可图, 预期收益是正数值; 亏损策略则为负数值。如果预期收益与点差或佣金成本相当, 那么这一策略在真实账户上的盈利能力就要深表怀疑。正常的话, 在策略测试器中的理想执行条件下预期收益可以为正, 余额图形可以是一条平滑的上升曲线。然而, 在实盘交易中, 由于可能出现所谓的重新报价或滑点, 平均成交回报可能比理论计算结果稍差, 这也许会对策略的结果产生严重影响, 并导致实盘亏损。

它也有其缺点。主要的一点也与交易的数量有关。如果只有少量成, 那么获得较大的预期收益并不是问题。另一方面, 当有大量成交时, 预期收益趋向于零。由于这是一个线性衡量指标, 因此不能用于施行资金管理系统的策略。但专业交易者高度重视它, 并将其用于固定手数的线性系统, 将其与成交的数量进行比较。 

Number of Deals (成交数量)。这是一个重要的参数, 它显式或间接地影响大多数其它特性。假设一个交易系统在所有情况下的胜率为 70%。与此同时, 获胜与亏损的绝对值是相等的, 在交易策略中没有其它可能的交易结果。这样的系统似乎非常优秀, 但效率评估只基于最后两笔交易, 发生了什么呢?在 70% 的案例中, 其中之一有利可图, 但两笔成交盈利的可能性仅为 49%。也就是说, 在一半以上的案件中, 两笔交易的总成果将为零。因此, 在一半的情况下, 统计数据将表明该策略无法赚钱。其盈利因子总是等于 1, 预期收益和利润为零, 其它参数也示意效率为零。

这就是为什么成交的数量必须 足够 大的原因。但 足够 说明什么?通常可接受的说法, 任何样本均应包含至少 37 次测量数据。这是统计学中的一个神奇数字, 它标志着参数代表性的下限。当地, 这些成交的数目还不足以评估一个交易系统。至少需要 100-10 笔成交才能得到可靠结果。而且, 对于众多专业交易者来说这也是不够的。他们设计的系统至少要进行 500-1000 笔成交, 之后再用这些结果考察运行的系统用于实盘交易的可能性。


当测试交易系统时常用统计参数的行为

交易系统的统计数据当中, 主要参数已进行了讨论。我们来看看它们在实践中的表现。与此同时, 我们将重点讨论它们的缺点, 看看提议的 R^2 统计量的形式如何有助于解决它们。为此, 我们将使用 "通用智能交易系统: 使用挂单" 一文中所描述的现成 CImpulse 2.0 EA。选择它是因为其简单性和可优化性, 不像来自标准 MetaTrader 5 软件包的智能系统专, 它对于本文的目的极端重要。此外, 还需要一些底层代码结构, 而这些已为 CStrategy 交易引擎编写, 所以无需再次完成相同的工作。判定系数的所有源代码都是这样编写的, 它们可以很容易地在 CStrategy 之外使用 — 例如, 在第三方函数库或过程智能系统中使用。

Total Net Profit (净利润总计)。如前所述, 净 (或总计) 利润是交易者想要得到的最终结果。利润越大越好。然而, 基于最终利润评估策略并不能保证一直成功。我们来研究一下 2015.01.15 至 2017.10.10 期间 CImpulse 2.0 策略在 EURUSD 货币对上的测试结果:

图例 2. CImpulse 策略, EURUSD, 1H, 2015.01.15 - 2017.10.01, PeriodMA: 120, StopPercent: 0.67


在这个段测试期间内, 这个策略看来展示出总利润的稳定增长。交易一份合约, 数额为正 11,894 美元。这是一个很好的结果。但我们来看看不同的情景是什么样的, 这次最终的利润接近于第一种情况:

图例 3. CImpulse 策略, EURUSD, 1H, 2015.01.15 - 2017.10.01, PeriodMA: 110, StopPercent: 0.24


尽管这两种情况利润几乎相同, 但它们看起来像完全不同的交易系统。第二种情况的最终利润似乎也是随机的。如果测试在 2015 年中结束, 利润将接近于零。 

这是另一个不成功的策略, 最终的结果也与第一种情况非常接近:

图例 4. CImpulse, EURUSD, 1H, 2015.01.15 - 2017.10.01, PeriodMA: 45, StopPercent: 0.44


从图表中可以清晰看出, 主要获利来自 2015 年上半年。随之而来的是一段长久的停滞。这样一种策略对于实盘交易不是一个可行的选项。 

Profit Factor (盈利因子)。盈利因子衡量指标对最终结果的依赖性要小得多。该数值取决于每笔成交, 并显示所有资金赢余与所有资金亏损的比率。可以看出, 在图例 2 中, 盈利因子相当高, 在图例 4 中, 它是较低的; 而在图例 3 中. 它几乎处于盈利和不盈利体系之间的边界。但尽管如此, 盈利因子并非普遍特征, 它也能被欺骗。我们来看看其它例子, 其中盈利因子指示不那么明显:

 

图例 5. CImpulse, EURUSD, 1H, 2015.01.15 - 2017.10.01, PeriodMA: 60, StopPercent: 0.82


图例 5 显示以最大盈利因子数值之一运行策略测试的结果。余额图看起来相当看好, 但是获得的统计数据是误导性的, 因为交易数量稀少, 盈利因子数值被夸大了。

我们以两种方式来验证这段陈述。第一种方式: 找出盈利因子对交易数量的依赖。这是通过在策略测试器中使用范围广泛的参数来优化 CImpulse 策略完成的:

图例 6. 使用范围广泛的参数来优化 CImpulse

保存优化结果:


图例 7. 导出优化结果


现在我们可以建立一个盈利因子值对交易数量的依赖图表。例如, 在 Excel 中, 可以通过选择相应的列, 并在 "图表" 选项卡中按下绘制散点图的按钮来完成此操作。


图例 8. 盈利因子对交易数量的依赖


图表清楚地表明, 若是运行结果盈利因子数值高则总是交易数量寥寥。相反, 在大量交易中, 盈利因子几乎等于一。

第二种方式 判断在这种情况下的盈利因子数值取决于交易的数量, 且与策略品质执行 Out Of Sample (OOS 样本外) 测试无关。顺便说一下, 这是判断所得结果稳健性的最可靠方法之一。稳健性在评估中是统计方法稳定性的一个衡量指标。OOS 不仅能有效地测试盈利因子, 还有其它指标。对于我们的目的, 将选择相同的参数, 但时间间隔将有所不同 — 从 2012.01.01 到 2015.01.01:

图例 9. 样本外测试策略


正如所见, 这个策略的行为是上下颠倒的。它产生的是亏损而非盈利。这是一个合乎逻辑的结果, 因为交易数量如此之少, 获得的结果几乎总是随机的。这意味着在一个时间间隔内的随机盈利要通过另一个时间间隔中的亏损来补偿, 这在图例 9 中得到很好地描绘。

Expected Payoff (预期收益)。我们不会详细讨论这个参数, 因为它的缺点与盈利因子的缺陷类似。此处是预期收益对交易数量的依赖图表:

图例 10. 预期收益对交易数量的依赖


可以看出, 交易越多, 预期收益就越小。对于可盈利和非赢利的策略, 都始终遵守这种依赖关系。因此, 预期收益不能作为交易策略优化的唯一准则。


交易系统的测试准测需求

在考察了交易系统的统计评估中的主要准则之后, 得出的结论是每个准则的适用性均有限。它们当中的每一个都可以通过一个例子来反演, 其衡量结果良好, 而策略本身则不然。

没有判断交易系统稳健性的理想标准。但制定一个强力统计标准必备属性是可能的。

  • 独立于测试周期的持续时间。交易策略的许多参数取决于测试周期的长度。例如, 一个盈利策略的测试周期越长, 最终利润 就越大。这取决于持续时间和 恢复因子。它是计算总利润与最大回撤之比。由于利润取决于期限, 恢复因子也随着测试期限的增加而增长。相对于期限的不变性 (独立性), 必须要比较不同策略在不同测试期限的有效性;
  • 独立于测试终点。例如, 如果一个策略只是通过亏损扛单才能 "保持平稳", 终点也许对最终的余额产生重大影响。如果测试在 "逾期逗留" 之时已经完成, 则浮亏 (净值) 变为余额, 并在账户上产生可观的回撤。应保护统计数据不受此类欺诈的影响, 并为交易系统运作提供客观的概述。
  • 简单的释义。交易系统的所有参数都是 量化的, 即每个统计量都是由一个特定的数字表征的。这个数字必须直观。所获数值的解释越简单, 参数越易于理解。还希望参数在一定的边界范围内, 因为海量分析以及潜在的无限量数字通常很复杂。
  • 少量成交所代表的结果。这可以说是衡量一个优良指标的最难要求。所有统计方法均取决于测量的次数。次数越多, 得到的统计就越稳定。当然, 少量样本完全不可能解决这个问题。然而, 可以减轻由于缺乏数据而造成的影响。为此目的, 我们开发两种类型的函数来评估 R-平方: 一种实现将基于可用成交的数量来构建这个标准。另一种是使用策略浮盈 (净值) 计算标准。

在直接描述判定系数 R^2 之前, 我们来详细地考察其组成部分。这将有助于理解这个参数的目的, 以及它所基于的原则。


线性回归

线性回归 是一个变量 y 与另一个自变量 x 的线性依赖性, 表达为公式 y = ax+b。在此公式里, а 是乘数, b 是截距系数。实际上, 也许有若干个自变量, 这种模型被称为多元线性回归模型。不过, 我们只会考虑最简单的情况。 

线性依赖性可直观地表现为简单图形的形式。取 2017.06.21 到 2017.09.21 的 EURUSD 日线图表。选择这一片段并非偶然: 在此期间, 该货币对能观察到温和上升趋势。这是它在 MetaTrader 中的模样:

图例 11. 自 2012 年 6 月 21 日到 2012 年 8 月 21 日, 日线时间帧的 EURUSD 动态价格


保存这些价格数据, 并用它们绘制图表, 例如在 Excel 中。

图例 12. 在 Excel 中, EURUSD 汇率 (收盘价) 的图表


此处, Y 轴对应于价格, X 是测量的序数 (序数代替日期)。在结果图形上, 肉眼可见上升趋势, 但是我们需要对这一趋势进行量化解读。最简单的方法是绘制一条直线, 其可最准确地符合检查的趋势。它被称作 线性回归。例如, 这条线可以如此绘制:

图例 13. 描述上升趋势的线性回归, 手工绘制


如果图形相当平滑, 有可能绘制这样一条直线, 即图形点距其的偏离距离最小。相反, 对于振幅较大的图形来说, 不可能选择一条能准确描述其变化的直线。这是因为线性回归只有两个系数。事实上, 几何学课程教导我们, 两点足以画出一条线。由此, 将一条直线拟合成一条 "曲线" 图形并非易事。这是一个有价值的属性, 将有助于进一步提升。

但如何正确绘制直线?可以使用数学方法来最优地计算线性回归系数, 在此方式中, 所有可用点离该线的累计距离最小。在下面的图表里会进行解释。假设有 5 个任意点, 且两条直线经过它们。从这两条线当中, 必需选择离这些点的累计距离最小的一条:


图例 14. 选择最合适的线性回归


很明显, 在两个线性回归变量中, 红线更好地描述了给定的数据: 点 #2 和 #6 比之黑线明显更接近红线。剩下的点离黑线和红线近似等距。在数学上, 能够计算出最符合这种规律性描述的直线坐标。我们无需手工计算这些系数, 而是利用已有的 AlgLib 数学库替代。

相关性

一旦计算出线性回归, 就必需计算这条线和它所计算的数据之间的 "相关性"。相关性 是两个或更多个随机变量的统计关系。在这种情况下, 变量的随机性意味着这些变量的测量不必相互依赖。测量的相关性从 -1.0 到 +1.0。接近零的数值表示检验的变量没有相互关系。数值 +1.0 表示直接依赖性, -1.0 表示反向依赖性。相关性由若干个不同的公式计算。此处, 将使用 皮尔逊相关性系数


在公式中的 dxdy 对应于随机变量 xy 计算得出的 方差方差 是衡量特质变化的指标。用最一般的术语来说, 它可以被描述为数据与线性回归之间距离的平方和。

数据与其线性回归的相关系数体现出该直线描述这些数据的能力何等优秀。如果数据点位于离线很远的地方, 则方差很高, 相关性较低, 反之亦然。相关性很容易解释: 零值意味着回归和数据之间没有相互关系; 一个接近 1 的数值表现出强烈的直接依赖性。

MetaTrader 中的报告有一个特殊的统计指标。它被称为 LR Correlation (LR 相关性), 它表示余额曲线和线性回归之间的相关性。如果余额曲线平滑, 则直线的近似值也会很出色。在这种情况下, LR 相关性系数将接近于 1.0, 或者至少在 0.5 以上。如果余额曲线不稳定, 则上升与下降交替, 相关性系数趋于零。 

LR 相关性是一个有趣的参数。但在统计学中, 通常不直接通过相关性系数来比较数据和描述的回归。其原因将在下一节讨论。


判定系数 R^2

判定系数 R^2 的计算方法类似于 LR 相关性的计算方法。但最终的数值是平方。它的取值范围从 0.0 到 +1.0。这个数字表示 自总样本中已解释数值的份额。线性回归用作解释性模型。严格地说, 解释模型不一定是线性回归, 也可以使用其它模型。不过, R^2 值不需要进一步的线性回归处理。在更复杂的模型中, 近似值通常更好, 为了更充分的评估, 必须通过特殊的 "惩罚" 来额外减少 R^2 值。

我们来仔细观看一下解释模型的表现。为此, 我们将进行一个小实验: 使用专门的编程语言 R-项目, 并生成一个随机漫游, 计算所需的系数。随机漫游是一个过程, 其特征与真实的金融工具非常相似。为了获得随机漫游, 连续添加若干个按照正常规律分布的随机数就足够了。

R 语言的源代码详述了正在完成的事情: 

x <- rnorm(1000)            # 生成 1000 随机数, 根据正常规律分布
                            # 它们的方差等于一, 期望值为零
rwalk <- cumsum(x)          # 累加这些数字, 得到一个经典的随机漫游图形
plot(rwalk, type="l", col="darkgreen")       # 以线性图的形式显示数据
rws <- lm(rwalk~c(1:1000))  # 绘制线性模型 y=a*x+b, 其中 x 是测量的数量, y 是生成的漫游矢量的值
title("随机漫游的线性回归")
abline(rws)                 # 在图表上显示所得到的线性回归

rnorm 函数每次都返回不同的数据, 所以如果您想重复这个实验, 图形将会有不同的模样。 

所提交代码的结果:

图例 15. 随机漫游和其线性回归

结果图表与任意金融工具的图表类似。其线性回归经过计算并输出为图表中的黑线。乍看之下, 随机漫游动态的描述相当平庸。但是我们需要量化评估线性回归的品质。为此目的, 使用 "summary" 函数, 输出回归模型的汇总统计:

summary(rws)
Call:
lm(formula = rwalk ~ c(1:1000))

Residuals:
    Min      1Q  Median      3Q     Max 
-16.082  -6.888  -1.593   4.174  30.787 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -8.187185   0.585102  -13.99   <2e-16 ***
c(1:1000)    0.038404   0.001013   37.92   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 9.244 on 998 degrees of freedom
Multiple R-squared:  0.5903,    Adjusted R-squared:  0.5899 
F-statistic:  1438 on 1 and 998 DF,  p-value: < 2.2e-16

在此, 最有趣的一个数字是 — R-平方。该衡量指标值为 0.5903。因此, 线性回归描述了所有数值的 59.03%, 其余 41% 没有解释。 

这是一个非常敏感的指标, 可以很好地反映平滑、平坦的数据线。为了说明这一点, 我们继续这个实验: 为随机数据引入一个稳定增长的分量。为此, 将平均值或预期值更改为最初生成的数据方差的 1/20:

x_trend1 <- x+(sd(x)/20.0)      # 找到 x 的标准方差, 将其除以 20.0, 并将得到的数值加到每个 x 值中
                                # x 的每个这样的修改值将被存储在新的数值向量 x_trend1 当中
rwalk_t1 <- cumsum(x_trend1)    # 累加这些数字, 得到一个偏移的随机漫游图形     
plot(rwalk_t1, type="l", col="darkgreen")        # 将数据显示为线性图
title("随机漫游 #2 的线性回归")
rws_t1 <- lm(rwalk_t1~c(1:1000))# 绘制线性模型 y=a*x+b, 其中 x 是测量的数量, y 是生成的漫游矢量的值
abline(rws_t1)                  # 在图表上显示所得到的线性回归 



结果图形现在更接近于一条直线:


图例 16. 具有正向期望值的随机漫游, 等于其方差的 1/20

其统计数据如下:

summary(rws_t1)

Call:
lm(formula = rwalk_t1 ~ c(1:1000))

Residuals:
    Min      1Q  Median      3Q     Max 
-16.082  -6.888  -1.593   4.174  30.787 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -8.187185   0.585102  -13.99   <2e-16 ***
c(1:1000)    0.087854   0.001013   86.75   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 9.244 on 998 degrees of freedom
Multiple R-squared:  0.8829,    Adjusted R-squared:  0.8828 
F-statistic:  7526 on 1 and 998 DF,  p-value: < 2.2e-16

显然, R-平方明显更高, 其值为 0.8829。但让我们多走一些里程, 将图表的判定分量加倍, 升至初始数据标准方差的 1/10。处理这些的代码与之前类似, 但是除以 10.0 而非 20.0。新图形现在几乎彻底酷似一条直线:

图例 17. 具有正向期望值的随机漫游, 等于其方差的 1/10

计算其统计数据:

Call:
lm(formula = rwalk_t1 ~ c(1:1000))

Residuals:
    Min      1Q  Median      3Q     Max 
-16.082  -6.888  -1.593   4.174  30.787 
4
Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -8.187185   0.585102  -13.99   <2e-16 ***
c(1:1000)    0.137303   0.001013  135.59   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 9.244 on 998 degrees of freedom
Multiple R-squared:  0.9485,    Adjusted R-squared:  0.9485 
F-statistic: 1.838e+04 on 1 and 998 DF,  p-value: < 2.2e-16

R-平方变得更高, 数额达至 0.9485。这幅图形与所期望的可盈利交易策略的余额动态非常相像。我们再次多走一些里程。将预期值增加到标准方差的 1/5:

图例 18. 具有正向期望值的随机漫游, 等于其方差的 1/5


它有以下统计数据:

Call:
lm(formula = rwalk_t1 ~ c(1:1000))

Residuals:
    Min      1Q  Median      3Q     Max 
-16.082  -6.888  -1.593   4.174  30.787 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -8.187185   0.585102  -13.99   <2e-16 ***
c(1:1000)    0.236202   0.001013  233.25   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 9.244 on 998 degrees of freedom
Multiple R-squared:  0.982,     Adjusted R-squared:  0.982 
F-statistic: 5.44e+04 on 1 and 998 DF,  p-value: < 2.2e-16

很显然, R-平方现在几乎等于一。图表清晰地表明, 绿线形成的随机数据几乎完全位于平滑的直线上。

反正弦定理及其对线性回归评估的贡献

有一个数学证明, 一个随机过程最终会远离它的原点。它被命名为第一和第二反正弦定理。不会详细讨论它们, 只会定义这些定理的必然结果。

基于它们, 随机过程中的趋势是不可避免的。换句话说, 在这种过程中, 随机趋势比初始点附近的随机波动更多。这是一个非常重要的属性, 对统计数据的评估做出了重大贡献。这对线性回归系数 (LR 相关性) 尤为明显。通过线性回归可以更好地描述趋势。这是由于事实上, 趋势在一个方向里包含更多的走势, 看起来线条很平滑。

如果在随机过程中有更多的趋势很平坦, 那么 LR相关性通常也会高估其数值。为了观察这个非平凡的效果, 我们尝试产生 10000 个独立随机漫游, 方差为 1.0, 且期望值为零。我们为每个这样的图表计算 LR 相关性, 然后绘制这些数值的分布。为此目的, 用 R 语言编写一个简单的测试脚本:

sample_r2 <- function(samples = 100, nois = 1000)
{
   lags <- c(1:nois)
   r2 <- 0  # R^2 rating
   lr <- 0  # Line Correlation rating
   for(i in 1:samples)
   {
      white_nois <- rnorm(nois)
      walk <- cumsum(white_nois)
      model <-  lm(walk~lags)
      summary_model <- summary(model)
      r2[i] <- summary_model$r.squared*sign(walk[nois])
      lr[i] <- (summary_model$r.squared^0.5)*sign(walk[nois])
   }
   m <- cbind(r2, lr)
}

脚本计算 LR 相关性和 R^2。它们之间的区别将在稍后看到。脚本中已添加了一小部分。得到的相关性系数将乘以合成图性的最终符号。如果最终结果小于零, 则相关性为负; 否则为正。这样做是为了方便快捷地将负面结果与正面结果分开, 而无需借助其它统计数据。这是 LR 相关性在 MetaTrader 5 中的工作原理, R^2 将使用相同的原则。

因此, 我们绘制 10000 个独立样本的 LR相关性分布图, 每个样本由 1000 个测量值组成:

ms <- sample_r2(10000, nois=1000)
hist(ms[,2], breaks = 30, col="darkgreen", density = 30, main = "Distribution of LR-Correlation")

结果图性清晰地表明: 定义的正确性:

图例 19. 10000 个随机漫游的 LR 相关性分布

从实验中可以看出, LR-相关性数值在 +/- 0.75-0.95 的范围内被高估了。这意味着 LR-相关性往往在不恰当的位置错误地给出一个很高的正面估值。 

现在我们来考察 R^2 在同一个样本上的行为:

图例 20. 10000 次随机漫游的 R^2 分布

虽然其分布均匀, 但 R^2 数值不是太高。令人惊讶的是, 一个简单的数学动作 (提高到二次幂) 彻底抹平了分布的不良尖端效应。这就是为什么 LR-相关性不能直接进行分析的原因 — 额外的数学变换是必要的。此外, 请注意, R^2 将所分析的虚拟策略余额的显著部分挪移到零点附近, 而 LR-相关性为它们给出了稳定的平均估值。这是一个正面的属性。 

收集策略净值

现在已经研究了这个理论, 剩下的事情就是在 MetaTrader 终端里实现 R-平方。当然, 我们可以用简单的方法去计算历史中的成交。不过, 还会引入进一步的改进。如前所述, 任何统计参数都必须抗衡稀少的的成交数量。不幸的是, 如果账户内只有少量成交, 则 R-平方就可能不合理地夸大其数值, 就像任何其它的统计量一样。为了避免这种情况, 计算可基于净值 — 浮动利润。其背后的思路是, 如果 EA 每年只有 20 笔成交, 评估它的效率是非常困难的。其结果很可能是随机的。但如果按照特定的周期性 (例如, 每小时一次) 测量 EA 的余额, 那么就会有相当数量的点来绘制统计数据。在这种情况下, 将会有超过 6000 个测量值。

此外, 这样的测量会抵消那些不能修正其浮亏的系统, 从而隐藏它。净值存在回撤, 但并非余额。基于余额计算的统计数据不会警告有关发生的问题。然而, 参考浮动盈/亏计算的衡量指标反映了账户的客观情况。

策略的净值将以非传统的方式收集。这是因为收集这些数值需要考虑两个要点:

  • 统计数据收集的频率
  • 事件的检测, 接收那个需要检查的净值。

例如, 智能交易系统只能在 H1 时间帧上通过定时器工作。它在 "仅限开盘价" 模式下进行了测试。所以, 此 EA 收集数据时, 每小时不能超过一次, 且只有 OnTimer 事件触发时才能执行这些数据的筛选。最有效的解决方案就是使用 CStrategy 引擎的强大功能。事实上, CStrategy 将所有事件收集到单独的事件处理程序中, 并自动监视必要的时间帧。因此, 优化方案是编写一个特殊的代理策略, 它计算所有需要的统计数据。它将由 CManagerList 策略管理器创建。该类只会将其代理添加到策略列表中, 它将监视帐户中的变化。

这个代理的源代码提供如下:

//+------------------------------------------------------------------+
//|                                                UsingTrailing.mqh |
//|                                   版权所有 2017, Vasiliy Sokolov. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "版权所有 2017, Vasiliy Sokolov."
#property link      "https://www.mql5.com"
#include "TimeSeries.mqh"
#include "Strategy.mqh"
//+------------------------------------------------------------------+
//| 集成到策略的投资组合中作为智能系统                                 |
//| 并记录投资组合的净值                                              |
//+------------------------------------------------------------------+
class CEquityListener : public CStrategy
{
private:
   //-- 记录频率
   CTimeSeries       m_equity_list;
   double            m_prev_equity;
public:
                     CEquityListener(void);
   virtual void      OnEvent(const MarketEvent& event);
   void              GetEquityArray(double &array[]);
};
//+------------------------------------------------------------------+
//| 设置默认频率                                                      |
//+------------------------------------------------------------------+
CEquityListener::CEquityListener(void) : m_prev_equity(EMPTY_VALUE)
{
}
//+------------------------------------------------------------------+
//| 收集投资组合净值, 监视所有可能的                                   |
//| 事件                                                             |
//+------------------------------------------------------------------+
void CEquityListener::OnEvent(const MarketEvent &event)
{
   if(!IsTrackEvents(event))
      return;
   double equity = AccountInfoDouble(ACCOUNT_EQUITY);
   if(equity != m_prev_equity)
   {
      m_equity_list.Add(TimeCurrent(), equity);
      m_prev_equity = equity;
   }
}
//+------------------------------------------------------------------+
//| 以双精度类型数组形式返回净值                                       |
//+------------------------------------------------------------------+
void CEquityListener::GetEquityArray(double &array[])
{
   m_equity_list.ToDoubleArray(0, array);
}

代理本身包含两个方法: 重新定义的 OnEvent 和一个返回净值的方法。此处, 主要兴趣在于 CStrategy 中首次出现的 CTimeSeries 类。这是一个简单的表格, 数据以 日期, 值, 列号 格式添加。所有存储的数值按时间排序。所需的日期是通过二叉树搜索访问, 这大大加快了收集的工作。OnEvent 方法检查当前事件是否为新开一根柱线, 如是, 则简单地保存新的净值。 

R^2 对长时间没有交易的情况作出反应。在这种情况下, 不变的净值将被记录下来。净值图形成所谓的 "阶梯"。为了防止这种情况, 该方法会比较当前数值与前值。如果数值匹配, 则跳过该记录。因此, 只有净值的变化才会进入清单。

我们把这个类集成到 CStrategy 引擎中。集成将在 CStrategyList 级别按上述执行。该模块适用于自定义统计的计算。可以有若干个自定义统计信息。所以, 枚举当中列举了所有可能的统计类型:

//+------------------------------------------------------------------+
//| 确定优化后计算的自定义标准的                                       |
//| 类型。                                                           |
//+------------------------------------------------------------------+
enum ENUM_CUSTOM_TYPE
  {
   CUSTOM_NONE,                     // 自定义标准未计算
   CUSTOM_R2_BALANCE,               // R^2 基于策略余额
   CUSTOM_R2_EQUITY,                // R^2 基于策略净值
  };

上面的枚举表明, 自定义优化标准有三种类型: 基于交易结果的 R-平方, 基于净值数据的 R-平方, 和无计算统计。

添加配置自定义计算类型的能力。为此, 请为 CStrategyList 类提供附加的 SetCustomOptimaze *方法:

//+------------------------------------------------------------------+
//| 设置 R^2 作为优化标准。系数为                                     |
//| 计算出的交易结果。                                                |
//+------------------------------------------------------------------+  
void CStrategyList::SetCustomOptimizeR2Balance(ENUM_CORR_TYPE corr_type)
{
   m_custom_type = CUSTOM_R2_BALANCE;
   m_corr_type = corr_type;
}
//+------------------------------------------------------------------+
//| 设置 R^2 作为优化标准。系数为                                     |
//| 基于记录的净值计算。                                              |
//+------------------------------------------------------------------+  
void CStrategyList::SetCustomOptimizeR2Equity(ENUM_CORR_TYPE corr_type)
{
   m_custom_type = CUSTOM_R2_EQUITY;
   m_corr_type = corr_type;
}

每种方法都将其内部变量 ENUM_CUSTOM_TYPE 的值设置为 m_custom_type, 并将第二个参数设置为相关性类型 ENUM_CORR_TYPE:

//+------------------------------------------------------------------+
//| 相关性类型                                                        |
//+------------------------------------------------------------------+
enum ENUM_CORR_TYPE
  {
   CORR_PEARSON,     // 皮尔逊相关性
   CORR_SPEARMAN     // 斯皮尔曼的排位顺序相关性
  };

这些额外的参数必须单独提及。事实上, R^2 不是别的什么, 而是图形与其线性模型之间的相关性。然而, 相关性类型本身可能会有所不同。使用 AlgLib 数学库。它支持两种计算相关性的方法: 皮尔逊 和 斯皮尔曼。皮尔逊的公式是经典的, 非常适合均匀, 正态分布的数据。斯皮尔曼的排位顺序相关性更能抗衡行情当中经常观察到的价格尖刺。所以, 我们的计算将允许使用每个变体来计算 R^2。

现在所有的数据都准备就绪, 继续计算 R^2。它被移动到单独的函数中:

//+------------------------------------------------------------------+
//| 返回基于策略余额的 R^2 估值                                       |
//+------------------------------------------------------------------+
double CustomR2Balance(ENUM_CORR_TYPE corr_type = CORR_PEARSON);
//+------------------------------------------------------------------+
//| 返回基于策略净值的 R^2 估值                                        |
//| 传递 "权益" 数组作为净值                                           |
//+------------------------------------------------------------------+
double CustomR2Equity(double& equity[], ENUM_CORR_TYPE corr_type = CORR_PEARSON);

它们位于名为 RSquare.mqh 的单独文件中。计算以函数的形式进行排列, 以便用户能够方便快捷地将这种计算模式纳入他们的项目中。在这种情况下, 不需要使用 CStrategy。例如, 若要计算智能系统中的 R^2, 只需重新定义 OnTester 系统函数即可:

double OnTester()
{
   return CustomR2Balance();
}

当需要计算策略净值时, 不采用 CStrategy 的用户必须自行完成这些。

在 CStrategyList 中需要做的最后一件事是定义 OnTester 方法:

//+------------------------------------------------------------------+
//| 增加对净值的监控                                                  |
//+------------------------------------------------------------------+
double CStrategyList::OnTester(void)
{
   switch(m_custom_type)
   {
      case CUSTOM_NONE:
         return 0.0;
      case CUSTOM_R2_BALANCE:
         return CustomR2Balance(m_corr_type);
      case CUSTOM_R2_EQUITY:
      {
         double equity[];
         m_equity_exp.GetEquityArray(equity);
         return CustomR2Equity(equity, m_corr_type);
      }
   }
   return 0.0;
}

现在研究函数 CustomR2EquityCustomR2Balance 的实现。

利用 AlgLib 计算判定系数 R^2

判定系数 R^2 是利用 AlgLib 实现的 — 一个跨平台的数值分析库。它有助于计算各种统计标准, 从简单到最高级的。

此处是计算系数的步骤。

  • 获取净值的数值并将其转换为矩阵 M[x, y], 其中 x 是测量的次数, y 是净值。
  • 对于获得的矩阵, 计算线性回归方程的 a 和 b 系数。
  • 为每个 X 生成线性回归值并将其放入数组中。
  • 用两个相关性公式之一找出线性回归与净值的相关性系数。
  • 计算 R^2 和其符号。
  • 将 R^2 的常规化数值返回给调用函数。

这些步骤由 CustomR2Equity 函数执行。其源代码如下所示: 

//+------------------------------------------------------------------+
//| 返回基于策略净值的 R^2 估值                                        |
//| 传递 "权益" 数组作为净值                                           |
//+------------------------------------------------------------------+
double CustomR2Equity(double& equity[], ENUM_CORR_TYPE corr_type = CORR_PEARSON)
{
   int total = ArraySize(equity);
   if(total == 0)
      return 0.0;
   //-- 填充矩阵: Y - 净值, X - 数值的序号
   CMatrixDouble xy(total, 2);
   for(int i = 0; i < total; i++)
   {
      xy[i].Set(0, i);
      xy[i].Set(1, equity[i]);
   }
   //-- 找出线性模型 y=a*x+b 的系数 a 和 b;
   int retcode = 0;
   double a, b;
   CLinReg::LRLine(xy, total, retcode, a, b);
   //-- 为每个 X 生成线性回归值;
   double estimate[];
   ArrayResize(estimate, total);
   for(int x = 0; x < total; x++)
      estimate[x] = x*a+b;
   //-- 找出数值与其线性回归的相关性系数
   double corr = 0.0;
   if(corr_type == CORR_PEARSON)
      corr = CAlglib::PearsonCorr2(equity, estimate);
   else
      corr = CAlglib::SpearmanCorr2(equity, estimate);
   //-- 计算 R^2 和其符号
   double r2 = MathPow(corr, 2.0);
   int sign = 1;
   if(equity[0] > equity[total-1])
      sign = -1;
   r2 *= sign;
   //-- 将 R^2 估值常规化到百分位 (小数点后 2 位) 之内, 并返回
   return NormalizeDouble(r2,2);
}

此代码引用三种统计方法: CAlgLib::LRLine, CAlglib::PearsonCorr2 和 CAlglib::SpearmanCorr2。主要是 CAlgLib::LRLine, 它直接计算线性回归系数。

现在我们来描述计算 R^2 的第二个函数: CustomR2Balance。顾名思义, 这个函数根据成交次数计算数值。迭代历史中的所有成交并形成一个双精度类型的数组, 其中包含余额的动态, 所有工作均依赖此数组。

//+------------------------------------------------------------------+
//| 返回基于策略余额的 R^2 估值                                       |
//+------------------------------------------------------------------+
double CustomR2Balance(ENUM_CORR_TYPE corr_type = CORR_PEARSON)
{
   HistorySelect(0, TimeCurrent());
   double deals_equity[];
   double sum_profit = 0.0;
   int current = 0;
   int total = HistoryDealsTotal();
   for(int i = 0; i < total; i++)
   {
      ulong ticket = HistoryDealGetTicket(i);
      double profit = HistoryDealGetDouble(ticket, DEAL_PROFIT);
      if(profit == 0.0)
         continue;
      if(ArraySize(deals_equity) <= current)
         ArrayResize(deals_equity, current+16);
      sum_profit += profit;
      deals_equity[current] = sum_profit;
      current++;
   }
   ArrayResize(deals_equity, current);
   return CustomR2Equity(deals_equity, corr_type);
}

一旦数组形成, 它就被传递给前面提到的 CustomR2Equity 函数。实际上, CustomR2Equity 函数是通用的。它计算 equity[] 数组中包含的所有 R^2 数值, 无所谓余额动态还是浮盈的数值。

最后一步是对 CImpulse EA 的代码进行小幅修改, 即覆盖 OnTester 系统事件:

//+------------------------------------------------------------------+
//| 测试器事件                                                        |
//+------------------------------------------------------------------+
double OnTester()
{
   Manager.SetCustomOptimizeR2Balance(CORR_PEARSON);
   return Manager.OnTester();
}

该函数设置自定义参数的类型, 然后返回其数值。

现在我们可以看到计算的系数在行动。一旦 CImpulse 策略开始回测, 参数将出现在报告中: 

 

图例 21. R^2 作为自定义优化准则的数值


在实际当中使用 R-平方参数

现在, R-平方是作为自定义优化标准内置的, 是时候在实际当中尝试一下了。这是通过 EURUSD 货币对在 М15 时间帧上优化 CImpulse 来完成的。将收到的优化结果保存到 Excel 文件中, 然后使用获得的统计信息与根据不同选择标准的几次运行结果进行比较。 

下面提供了优化参数的完整列表:

  • 品名: EURUSD
  • 时间帧: H1
  • 期限: 2015.01.03 - 2017.10.10

EA 参数的范围列在表中:

参数 开始 步进 停止符号 步数
PeriodMA 15 5 200 38
StopPercent 0.1 0.05 1.0 19

优化之后, 获得了由 722 个变体组成的优化云:


图例 22. CImpulse 的优化云, 品名 - EURUSD, 时间帧 - H1


选择利润最大的运行, 并显示其余额图:

图例 23. 根据最大利润的标准选择策略的余额图


现在根据 R-平方参数找到最佳运行结果。为此, 请比较 XML 文件中的优化运行结果。如果在计算机上安装了微软的 Excel, 该文件将自动打开。这项工作将涉及排序和过滤器。选择表格标题并按下相同名称的按钮 (主页 -> 排序 & 过滤器 -> 过滤器)。这允许自定义列的显示。根据自定义优化标准对运行结果进行排序:


图例 24. 微软 Excel 中的优化运行结果, 按 R-平方排序


表中的第一行将含有整个样本的最佳 R-平方值。在上图中, 它被标记为绿色。策略测试器中的这组参数给出了如下的余额图:

图例 25. 根据最大 R-平方值的标准选出的策略余额图


这两个余额图之间的量化差异肉眼可见。在 2015 年 12 月, 按照 "利润最大化" 进行测试的同时, 另一个最大 R^2 的变种继续稳步增长。

通常 R^2 取决于成交次数, 若样本较少时, 一般可能高估其值。在这个层面, R-平方与盈利因子相关。在某些策略类型中, 盈利因子的高值与 R^2 的高值并肩前进。然而, 并非总是如此。举例来说, 从样本中选择一个反例, 证明 R^2 和盈利因子之间的差异。下图展示的是盈利因子值最高的策略之一, 等于 2.98:

图例 26. 盈利因子等于 2.98 的策略的测试运行结果

如图形所示, 即使策略呈现平稳增长态势, 但策略余额曲线的品质仍然低于最大 R-平方的品质。

运用时的优点和限制

每个统计量的衡量指标都有其优缺点。R-平方在这方面也不例外。下表列出了它们的缺陷, 以及能够减轻其影响的解决方案:

缺陷 解决方案
取决于成交次数。成交量较少时会高估数值。 基于策略净值计算 R^2 值, 部分解决了这个问题。
与策略有效性的现有衡量指标相关联, 尤其是策略的盈利因子和净利润。 相关性不是 100%。取决于策略的特征, R-平方可能根本不与任何其它衡量指标相关, 或相关微弱。
需要复杂的数学计算。 算法是使用 AlgLib 函数库实现的, 该函数库承担了所有的复杂性。
专门用于估算线性处理, 或系统以固定手数进行交易。 不可用在采用了复利系统 (资金管理) 的交易系统。

我们来详述将 R^2 应用于非线性系统 (例如, 采用动态手数的交易策略) 的问题。

每位交易者的主要目标是利润最大化。一个必要条件是使用各种复利系统。复利系统是将线性过程转化为非线性过程 (例如, 转化为指数过程)。但是这样的转换令大多数统计参数变得毫无意义。例如, 对于复利系统来说, "最终利润" 参数是毫无意义的, 因为哪怕是测试时间间隔的轻微变化, 或者将策略参数改变百分之一, 最终结果都可能变化几十甚至几百倍。

该策略的其它参数也会失去其意义, 如盈利因子, 预期收益, 最大利润/亏损等。在这个意义上, R-平方也不例外。为余额曲线平滑度所创建的线性估值, 在评估非线性过程时变得无能为力。所以, 任何策略都应该以线性形式进行测试, 且仅在测试之后才应将复利系统添加到所选择的选项中。评估非线性系统, 最好使用特殊的统计衡量指标 (例如, GHPR), 或以年度百分比计算收益率。

结束语

  • 用于评估交易系统的标准统计参数具有已知的缺点, 必须予以考虑。
  • 在 MetaTrader 5 的标准衡量指标中, 只有 LR 相关性被设计用来评估策略余额曲线的平滑度。然而, 其数值往往被高估。
  • R-平方是计算策略余额曲线和浮盈曲线平滑度的少数指标之一。同时, R-平方不存在 LR 相关性的缺点。
  • 利用 AlgLib 数学库计算 R-平方。算法本身有很多修改, 在相应的例子中有详细的描述。
  • 自定义优化标准可以内置到智能交易系统当中, 以便所有智能系统都可以自动计算该衡量指标, 而无需它们参与。如何执行此操作的说明已在 R-平方集成到 CStrategy 交易引擎的示例中提供了。
  • 可以使用类似的集成方法计算自定义统计数据所需的附加数据。对于 R-平方, 这些数据是策略 (净值) 的浮盈数据。浮盈的动态记录由 CStrategy 交易引擎执行。
  • 判定系数允许选择含有余额/净值增长的策略。在这种情况下, 选择基于其它参数的过程可能会错过这样的变体。
  • 与任何其它统计衡量指标一样, R-平方也有其缺点, 在使用此值时必须考虑到这一点。

因此, 可以肯定的是, 判定系数 R-平方是对现有的 MetaTrader 5 测试衡量指标的重要补充。它能够评估策略余额曲线的平滑度, 其本身就是一个非凡的指标。R-平方易于使用: 其值被限制在 -1.0 至 +1.0 的范围内, 标志策略余额中的负面趋势 (接近 -1.0 的数值), 没有趋势 (接近 0.0 的数值) 和正面趋势 (数值趋于 +1.0)。感谢所有这些属性, 可靠性和简单性, 能够将 R-平方推荐用于构建可盈利的交易系统。



本文译自 MetaQuotes Software Corp. 撰写的俄文原文
原文地址: https://www.mql5.com/ru/articles/2358

附加的文件 |
UnExpert.zip (114.84 KB)
交易中不同类型移动平均线的比较 交易中不同类型移动平均线的比较

已经研究过 7 种移动平均线 (MA), 并已开发了与它们协同工作的交易策略。在单一交易策略中测试和比较各种移动平均线的工作已经完成了, 结果展示了所有给定移动平均线应用的可比较性能特征。

三角套利 三角套利

本文讨论流行的交易方法 - 三角套利。在此我们尽可能详细地分析该主题, 研究策略的正、负两方面, 并开发即用的智能交易系统代码。

利用卡尔曼 (Kalman) 滤波器预测价格方向 利用卡尔曼 (Kalman) 滤波器预测价格方向

为了成功交易, 我们几乎总是需要指标来把主要价格走势与噪音波动剥离。在本文中, 我们考察最有前途的数字滤波器之一, 卡尔曼滤波器。本文将介绍如何绘制和使用滤波器。

将入场信息解析到指标 将入场信息解析到指标

交易者的生活中会出现不同的状况。经常地, 成功交易的历史令我们能够复现策略, 而查看亏损历史, 让我们尝试开发和改进新的策略。在这两种情况下, 我们要将交易与已知指标进行比较。本文推荐了一批拿交易与数个指标进行比较的方法。