
开发多币种 EA 交易(第 7 部分):根据前向时间段选择组
概述
在之前的文章中,我一直在优化同一时间段 - 从 2018 年到 2022 年 - 的单个交易策略实例。这是一个相当长的时间段 ,其中肯定包括影响价格动态的各种事件。另一方面,它也不会太大,而且一次运行的时间足够短 - 在几十秒之内 。所选时间段的安排是,到当前时刻还有一年多的时间。这样就可以看到在优化参数时未使用的历史部分中,策略实例将如何运行。
标准 MetaTrader 5 测试器可执行单次测试和优化,同时考虑到所谓前向时间段的存在。使用时,测试器将把整个指定测试期分成两部分 - 主要时间段和前向时间段。主要时间段结束时,所有交易关闭,交易账户余额恢复到初始状态。然后,EA 将在前向时间段再次运行,测试器收集的所有统计数据将分别计算主要时间段和前向时间段。
在机器学习领域,术语 "样本内"(In-Sample)和 "样本外"(Out-Of-Sample)(IS 和 OOS)经常用来指代模型训练和测试的数据集。在我们的领域,主要时间段将扮演 IS 的角色,而前向时间段则是OOS。请记住,OOS 是一个比前向时间段更广泛的概念。我们可以在测试器中不指定远期时间而运行 EA 测试,但如果测试时间段在优化所处时间段之外,则仍将在 OOS 上进行测试。
由于我们过去(除了这篇文章结尾部分)很少使用 OOS 测试,因此现在是时候看看在前向时间段工作时是否能够保持可比结果,正如主要时期的 EA 所显示的那样。
已完成 EA 的前向时间段
让我们看看上一篇文章中的 EA 所显示的结果,即一组手动选择的策略和根据最大利润自动选择的最佳策略组。我将以 2023 年全年为前向时间段。2024 年的三个月将暂时保留。我希望 得到的结果是,前向时间段的利润大约是主要时间段的五分之一,,因为前向时间段比主要时间段短五倍。前向时间段的回撤最好与主要时间段的回撤大致相同或更少。
让我们从一组手动选择的策略(BaselineExpert.mq5 EA)开始。蓝线的垂直部分在图中将主要时间段和前向时间段清楚地分开。此时,账户余额再次变为 10000 美元。图表中与前向时间段有关的部分只占整个图表的一小部分。如果我们需要更详细地检查,可以运行一个单独的测试器,只对这里指的前向时间段进行测试。
主要时间段
前向时间段
图 1.BaselineExpert.mq5 主要时间段和前向时间段的 EA 结果
在这种情况下,我们可以清楚地看到,即使不对前向时间段进行单独测试,结果也会大打折扣。但这并不意味着策略的完全崩溃。回撤率从 10%上升到 12%,恢复期超过 6 个月。但这还不是余额曲线趋势的逆转。或者是这样?不管怎么看,结果都需要改进。
现在让我们看看在没有聚类集的情况下选出的最佳组(OptGroupExpert.mq5 EA)。
主要时间段
前向时间段
图 2.OptGroupExpert.mq5 主要时间段和前向时间段的 EA 结果
对于这组策略来说,尽管在前向时间段结束时仍有少量盈利,但结果也大大低于预期。回撤增加了近 1.5 倍。看来,主要时间段的最佳组并不比前向阶段的最佳组更长。
让我们来看看用参数集聚法(OptGroupClusterExpert.mq5 EA)选出的组的结果,尽管我们已经非常怀疑其结果会与前两种情况大致相同。
主要时间段
前向时间段
图 3.OptGroupClusterExpert.mq5 主要时间段和前向时间段的 EA 结果
的确,我的怀疑是有道理的。这里的结果同样不明朗,回撤幅度更大。因此,我们将不介绍从文件中选出的最佳组的结果,因为在一个群集中只剩下一组。它们都是差不多的。
那么,我们是否有任何组的前向时间段业绩符合我们的预期呢?为了回答这个问题,我们将进行一次新的优化,但有一个前向时间段,这样就不需要在前向时间段内在测试器中针对上一次优化中获得的所有参数集组手动启动 EA。
前向时间段优化结果
这样,我们就得到了OptGroupClusterExpert.mq5 EA 的优化结果,其中包含一个前向时间段。使用的参数集文件为 64 个群集各一个参数集文件。对于优化结果的初步分析,普通 Excel 可能就足够了。让我们将主要时间段和前向时间段的优化结果导出为 XML 文件,并保存为 Excel 格式:
图 4.包含主要时间段(IS)和前向时间段(OOS)优化结果的源文件
前向时间段的文件中有 "Back Result"一列,其中包含在主要时间段使用同一组优化参数获得的结果。这很好,但我们希望在旁边看到主要时间段的所有其他特征。因此,我们将通过 Pass 关键列把这两个表的数据合并为一个表。这一列中的相同值对应于 Pass 中的相同输入组合。
合并后,我们将用不同的颜色标出主要时间段和前向时间段的相关数据,暂时隐藏一些列,并按主要时间段的归一化利润降序排序:
图 5.主要时间段和前向时间段的合并结果
很明显,对于主要时间段的最佳结果,前向时间段的结果大多为负值。结果绝不是灾难性的 (每年损失初始余额的 3-5%),但肯定不是好事。
让我们回顾一下是如何获取在 Forward Result 和 Back Result 列中的值的。这是 EA 通过后返回的 OnTester() 函数的结果。我们称这一数值为归一化利润,计算公式如下:
Result = Profit * (10% / EquityDD),
其中,Profit 是 测试期间获得的利润,EquityDD 是测试期间资金的最大相对回撤。
这个计算值的含义是,如果改变开启仓位的大小,使最大相对回撤率达到 10%,在测试期间可获得的估计利润。
如果对前向时间段和主要时间段使用相同的仓位缩放系数,就能正确比较参数结果:coeff = (10% / 10% / 100%) EquityDD)。在前向试验期间,我们很难获得主要时期的这一比率值,因此现在就进行调整。换算公式如下:
ForwardResultCorrected = ForwardResult * (coeff_IS / coeff_OOS)
= ForwardResult * (EquityDD_OOS / EquityDD_IS)
应用调整后,我们得到了以下结果:
图 6.重新计算前向时间段规范化利润后的结果
我们看到,前向时间段结果的绝对值有所增加。这是正确的,原因如下。举例来说,让我们来设想一下主要时间段的第二组参数。根据其回撤率为 1.52% 的事实,我们将把仓位大小扩大 10 / 1.52 = 6.58 倍,以实现 10% 的目标回撤率。那么,如果我们对前向时间段还一无所知,那么我们也必须将仓位规模扩大 6.58 倍。但在这种情况下,如果前向时间段收到的利润为-98,那么计算正常化利润时,应将利润乘以相同的比率 6.58。因此,我们得到的是-635,而不是-240。-240 的值较小,因为远期的回撤率几乎是原来的三倍(4.03% 而不是 1.52%),在计算规范化利润时,比率为 10 / 4.03 = 2.48,即几乎小了三倍。
结果并不令人愉快。现在,让我们试着找点更令人鼓舞的东西吧。首先,让我们看看我们在前向时间段是否取得了正面的结果。让我们按照 Forward Result 校正列对数据进行排序,看看下面的内容:
图 7.按前向时间段结果排序的结果
不过,也有一些组即使在前向时间段也能取得正面结果。这些组在主要时间段的标准利润约为 15000 到 18000 美元。我们可以看到,在这里,主要时间段和前向时间段的回撤差别不大,前向时间段的规范化利润大约是主要时间段规范化利润的五分之一。
那么,有可能选择好的组吗?
哲学问题
其实,这是一个非常棘手的问题。它可以有不同的表述方式。例如:
- 我们是否有权在考虑到前向时间段的情况下进行选择?
- 如果我们希望这样的选择能让我们继续获得类似的结果,那岂不是自欺欺人?
- 我们能在多大程度上相信这样的选择?
- 与不考虑前向时间段目标的选择相比,如果我们在选择组时考虑了前向时间段目标,那么在新的时期,前向时间段目标的结果会明显更好吗?
- 在未来哪个时期可以重复结果?它与前向时间段有可比性吗?
- 如果我们每年都进行类似的遴选程序,这是否意味着下一年总能有一个优秀的组?那六个月呢?那每周呢?
虽然这些问题有共同的基础,但却涉及不同的方面。
为了尝试以某种方式回答这些问题,让我们回顾一下贝叶斯方法。首先,我们提出一系列假设(或假定),然后在获得新数据之前对其可能性进行评估。在运行实验后,我们更新了我们的估计值,以考虑新的数据。在我们的眼中,一些假设的可能性变得更大,而另一些的可能性则变得更小。在这里,我们将概率视为我们对某一事件结果的信心程度。
我们的主要假设如下:选择一个考虑前向时间段的组可以改善前向时间段后的结果。另一种假设:考虑到前向时间段因素选择组不会改善结果。
一种可能的实验是,在考虑到前向时间段的情况下选择若干组,在不考虑前向时间段的情况下选择若干组。然后,我们在前向时间段后的一段时间内对所有选定的组别进行测试。
如果考虑了前向时间的组比没有考虑前向时间的组表现更好,那么这将是支持主要假设的微弱证据。我们对主要假设有效性的信心也会增强。
如果在不考虑前向时间段因素的情况下,所选组的结果大致相同或更好,那么这将是支持备选假设的微弱证据。同时,我们也不能完全否定任何一个假设,因为任何一组策略在前向时间后阶段所显示的结果,除了取决于分组的选择方法外,还取决于许多其他因素。对于所使用的策略来说,这可能只是一个总体不景气的时期,因此,将策略分成若干组的一种或另一种选择方法可能不会产生明显的效果。
我们这里可能无法支持更多。
对 "考虑到前向时间段" 一语的理解可能略有偏差。如果我们在考虑到前向时间段的情况下进行选择,这就意味着以前的前向时间段(OOS)现在不再是 OOS 期,而是 IS 期,尽管我们仍然称其为前向时间段。这意味着,为了评估交易结果,我们需要使用一个新的前向时间段(前向时间段后的时间段,请原谅我的同义反复)。
让我们更详细地描述一下我们想要进行的实验,以获取更多信息。假设我们有 2018 年至 2023 年(含 2023 年)的历史数据。在此基础上,我们希望选择一组能在 2024 年取得良好成果的策略。那么,我们可以从两个方面着手:
- 对 2018-2023 年(IS)进行优化,并根据优化结果选出最佳组。
- 对 2018-2022 年(IS)进行优化,同时对 2023 年前向时间段(OOS)进行验证。选择在两个时期都能提供良好且大致相同结果的最佳组。
在第二种方法中,我们很可能不会选择与第一种方法相同的组。其结果会稍差一些,但它在未参与优化的 OOS 阶段持续了一年。在第一种方法中,由于我们没有在 IS 期之外进行检查,因此不可能对所选组别做出类似的判断。但在第一种方法中,我们对小组进行了较长时间的优化(训练),因为在第二种方法中,我们必须为前向时间段分配 2022 年,而不是用于优化。
哪种方法会更好?让我们试着进行这样一个实验,比较在 2024 年进行交易时以两种方式选择的组别结果。
使用第一种方法选择
要使用第一种方法进行选择,我们首先需要优化 2018-2023 年期间的单一策略副本。此前,我们对截至 2022 年(不包括 2023 年)的时间段进行了优化。优化之后,我们将获得一组参数,并按照前一篇文章的描述,将这些参数分成若干组。然后,我们进行优化,从八组参数中选出好的组。让我们来看看 2018 到 2023 年和 2024 年期间最佳发现组的结果:
图 8.OptGroupClusterExpert.mq5优化选择 2018 到 2023 年主要时间段组的结果
图 9.OptGroupClusterExpert.mq5 选择 2024 年三个月期间组的结果
我们看到,2018 到 2023 年主要时间段发现的最佳组在 2024 年前向时间段的结果总体上是积极的,但它们之间的差异很大。为了进行更彻底的检查,请选择最上面的组,为其分配一个缩放因子值scale_= 10 / 2.04 = 5,并在 2023 年主要时间段和 2024 年前向时间段的测试器中运行。
2023
2024 (3 个月)
图 10. OptGroupClusterExpert.mq5 2023 年和 2024 年最佳组的 EA 结果
基于这些结果,我们无法对使用这组策略参数的 EA 的进一步行为进行特别评估,但至少我们没有看到在 2024 年出现明显的余额曲线下降趋势。让我们记住这些结果,稍后再将它们与使用第二种选择方法得出的结果进行比较。
使用第二种方法选择
2023
2024 (3 个月)
图 11. 2023 年和 2024 年的 OptGroupClusterExpert.mq5 EA 结果
请注意,2023 年的回撤量比主要测试期的计算值高出近两倍:是 1820 美元,而不是 1000 美元。
在考虑到 2023 年为前向时间段的情况下,使用以下算法进行分组选择:
- 在 2018 到 2022 年(主要时间段)和 2023 年(前向时间段)的优化结果合并表中,计算所有参数在主要时间段和前向时间段的数值之比。例如,在交易次数方面:
TradesRatio = OOS_Trades / IS_Trades.
这些比率越接近 1,说明这两个时期的参数值越相同。对于利润参数,引入一个考虑到不同时期长度的比率 - 一年的利润应比五年的少大约 5 倍:
ResultRatio = OOS_ForwardResultCorrected * 5 / IS_BackResult. - 让我们计算一下所有这些关系的偏离统一值的总和。这一数值将用来衡量各组在主要时间段和前向时间段的结果之间的差异:
SumDiff = |1 - ResultRatio| + ... + |1 - TradesRatio|. -
此外,还要考虑到在主要时间段和前向时间段,每个通过的回撤情况可能不同。选择两个时期的最大回撤率,并用它来计算为达到 10%的标准化回撤率而建立的仓位规模的比例系数:
Scale = 10 / MAX(OOS_EquityDD, IS_EquityDD).
-
现在,我们要选择Scale 大于 SumDiff 的集合 。为此,请计算最后一个参数:
Res = Scale / SumDiff.
-
根据上一步Res计算出的数值对所有组进行降序排序。在这种情况下,主要时期和远期的结果比较相似,而且两个时期的回撤幅度都较小的组位居榜首。
- 让我们把最上面的一组作为第一组。要选择下一组,先将与第一组具有相同群集索引的所有组进行排序,然后再次选取排在最前面的一组。让我们重复几次这样的操作,现在把之前选定的组中包含的所有索引进行分类。我们将把由此产生的四个组作为新的 EA。
要测试基于 OptGroupClusterExpert.mq5 EA 的选定组的协作,请创建一个新的 EA 并对其稍作修改。由于 EA 不会用于优化,因此可以删除其中的 OnTesterInit() 和 OnTesterDeinit() 函数。我们还可以删除指定要包含在组中的参数集索引的输入,因为我们将根据执行的选择程序在代码中对其进行硬编码。
在 OnInit() 函数中,创建两个数组: strGroups 表示选定的组,scales 表示组乘数。 strGroups 数组元素是包含参数集索引的字符串,以逗号分隔。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Load strategy parameter sets int totalParams = LoadParams(fileName_, params); // If nothing is loaded, report an error if(totalParams == 0) { PrintFormat(__FUNCTION__" | ERROR: Can't load data from file %s.\n" "Check that it exists in data folder or in common data folder.", fileName_); return(INIT_PARAMETERS_INCORRECT); } // Selected set groups string strGroups[] = {"55,12,3,35,48,54,16,40", "11,54,33,30,62,6,10,23", "50,15,8,34,2,36,4,9", "26,42,25,22,36,51,53,0" }; // Scaling factors for selected set groups double scales[] = {4.16, 3.40, 3.33, 2.76 }; // Set parameters in the money management class CMoney::DepoPart(expectedDrawdown_ / 10.0); CMoney::FixedBalance(fixedBalance_); // Create an EA handling virtual positions expert = new CVirtualAdvisor(magic_, "SimpleVolumes_OptGroupForwardCluster"); CVirtualStrategyGroup *groups[ArraySize(strGroups)]; FOREACH(strGroups, { // Form the string from the parameter set indices separated by commas string strIndexes = strGroups[i]; // Turn the string into the array string indexes[]; StringSplit(strIndexes, ',', indexes); // Create and fill the array of all strategy instances CVirtualStrategy *strategies[]; FOREACH(indexes, { // Remove the cluster number from the parameter set string string param = CSVStringGet(params[StringToInteger(indexes[i])], 0, 11); // Add a strategy with a set of parameters with a given index APPEND(strategies, new CSimpleVolumesStrategy(param)) }); // Add the strategy to the next group of strategies groups[i] = new CVirtualStrategyGroup(strategies, scales[i]); }); // Form and add the group of strategy groups to the EA expert.Add(CVirtualStrategyGroup(groups, scale_)); return(INIT_SUCCEEDED); }
将此代码保存在当前文件夹的 OptGroupForwardClusterExpert.mq5 文件中。
让我们来看看 EA 的测试结果。和上次一样,我们将把 2023 年和 2024 年前三个月这两个时期合并在一起。
2023
2024 (3 个月)
图 12. 2023 年和 2024 年的 OptGroupClusterForwardExpert.mq5 EA 结果
在这里,2023 年的结果显然要好一些:整个期间都呈现出上升趋势,尽管在 3 月至 7 月期间,余额曲线没有出现任何明显的增长。这一时期的回撤情况也有所改善,未超出预期的最大限度。
2024 年的结果也较好,但不是特别惊人。也许,三个月的时间太短,图表看起来不如几年的图表漂亮。
如果我们将这些结果与第一种选择优秀组的方法的结果进行比较,就会发现这些方法都没有明显的优势。结果大致相似,但与第一种方法相比,第二种方法需要我们付出更多努力。不过,由于我们已经清楚地概述了第二种选择方法的行动算法,因此将来可以将其自动化。
结论
正如我们所看到的,所进行的实验并没有增强我们的信心,即最好是多分配一个时期作为前向时间段,并在考虑两个时期的最佳工作的基础上选择组。但这并不意味着不应该使用这种方法。此外,我们只使用了 2024 年的三个月进行比较。这段时间太短了,因为我们已经看到,所使用的交易策略可能会出现持续数月的零左右的余额增长波动。因此,目前还不清楚 2024 年的前三个月是否代表了这一时期的开始,随后将被增长所取代,或者可能根本不会有任何增长。
我们可以尝试将周期后移一年,进行类似的实验。在这种情况下,主要时间段将从 2017 年开始,第二种方法的选择将在 2022 年进行,为了对两种方法进行比较,我们将有 2023 年全年和 2024 年初的时间。
不过,我们将更进一步。没有什么能阻止我们用第一种方法选择部分组,同时用第二种方法选择其他组 ,并将它们合并为一个 EA。但是,我们最多可以在一个 EA 中组合多少个交易策略实例,以使其运行不会占用过多服务器资源,也不会需要异常高的内存?我将在接下来的文章中尝试澄清这个问题。
感谢您的关注,敬请期待!
备注:在准备这篇文章时,我没有对之前创建的文件进行任何修改。我只添加了一个新文件。因此,它是下面唯一的附件。您可以找到上一篇文章中提到的所有其他文件。
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/14549
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.

