请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  量化策略 帖子:3352689 新帖:40

连续前行优化 (第四部分): 优化管理器(自动优化器)

吃瓜群众小王发表于:6 月 11 日 16:00回复(1)

概述

这是“连续前行优化”系列中的新篇章。 在此,我会演述所创建的程序,可实现编程的自动优化。 之前的文章论述了终端侧和函数库侧的程序实现细节,这些程序可与生成的优化报告一起运作。 您可以在以下链接查看这些文章:

  1. 连续前行优化 (第一部分): 操控优化报告
  2. 连续前行优化 (第二部分): 创建优化报告机器人的机理
  3. 连续前行优化 (第三部分): 将机器人适配为自动优化器

本文演示了所创建产品的概况,可作为指南。 已创建的自动优化器功能可以扩展。 因此,我们要进一步讨论的算法可以轻松地替换为您自己的优化算法,从而令您可以实现任何期望的想法。 另外,深入探讨会揭示所创建程序的内部结构。 本文主要目的在于阐述运用得到的应用程序进行操控的机制及其能力。 事实是,我的同事有时发现很难快速理解它。 然而,自动优化器的设置和运用非常容易 — 我会进一步展示这一点。 因此,本文可视为应用程序使用指南,其中涵盖了所有可能的陷阱和设置细节。


自动优化器操作说明

To proceed with the analysis of the created program we first need to define the purpose of this project. 我们决定在交易中运用科学的方法,并着手创建清晰的程序化交易算法(无论我们与何种类型的机器人打交道,基于指标亦或是应用模糊逻辑和神经网络 — 所有这些都是执行特定任务的编程算法)。 因此,选择优化结果的方式也应形式化。 换言之,如果在交易过程中拒绝采用随机性,那么准备交易的过程也应该是自动化的。 否则,我们可以随机地选择自己喜欢的结果,这比系统交易更接近直觉。 这一思路是鼓励我创建此应用程序的第一个动机。 下一个则是能够利用优化来测试算法 — 运用下图所示的连续前行优化。   


连续前行优化在给定的时间区间内,轮流在历史(黄色)和前向验证(绿色)优化过程之间交替。 假设您拥有 10 年的历史数据。 我们确定优化区间应等于 1 年的间隔,而前向验证间隔则由 1 个季度(或 3 个月)组成。 作为结果,我们的间隔时间等于 1.25 年(1 年+ 1 个季度),这包含了一个优化通关测试 + 一个前向验证测试。 在图例中,每行代表该时间间隔。 

接下来,我们重复相同类型的过程:

  1. 在选定的历史周期(1 年)内进行优化。
  2. 在优化间隔的结果中选择最佳参数,利用固定方法。
  3. 移到前向验证时间段,并运行测试,然后返回到第一步 — 现在是处于偏移的时间段上。 重复该过程,直至达到当前时间段。
  4. 收集到每一季度的测试结果后,我们对算法的可行性进行了最终评估。
  5. 可以启用最后一个优化通关测试(未经前向验证测试)进行交易。

由此,我们得到了一种方法,可通过优化来测试算法的可持续性。 不过,在这种情况下,我们必须在每次前向验证周期失效后重新优化算法。 换言之,我们为算法重新优化设置了某段时间间隔,固化了选择参数的方法,并首先在历史记录上执行此过程,后期每当前向验证测试周期失效时,便要重复一次。 

第一点,这种优化技术令我们拥有清晰定义的优化逻辑,从而令我们可以摆脱人工干预,得到满意的结果。

第二点,运用类似技术对新资产或新算法执行优化,我们可以得到该算法的压力测试的全貌。 当参数选择方法和优化参数在所有前向验证优化中都得以固定不变时,我们应进行连续的压力测试,从而提示我们市场是否不再适合我们的策略。

第三点,我们在相当短的时间内得到了很多优化片段,这提高了所执行测试的可靠性。 例如,将上述划分为 1 年优化和 1 个季度前向验证,那么 2 年间隔能提供 4 次压力测试和 1 个最终优化。


优化启动设置

现在我们已经讨论了应用程序执行的过程,我们来研究一下它的用法。 该系列文章,是我先前有关优化过程管理图形界面文章的逻辑延续:

  1. 优化管理(第一部分)
  2. 优化管理(第二部分)

文章介绍了如何在终端中以受控过程运行优化或测试。 本系列中采用相同的方法。 不过,基本区别之一是控制过程不是作为终端的附加实现的,而是作为独立程序实现的。 这种方法可供计算机上安装的所有终端使用。 在先前的系列文章中,我们创建了一个扩展,可以从工作终端启动。 该扩展程序可供计算机上启动的所有终端设备使用。 当前应用程序还可以访问计算机上安装的所有终端,但是每次只能操控一个终端,且它可以启动任何所需的终端。

为确保自动优化器成功运行,请确保在启动应用程序之前关闭选定的终端。 

该应用程序的运行方式如下:

  1. 在下一次启动过程中,无论是优化还是测试,应用程序都会启动终端,并将整个测试过程委托给终端。 测试或优化完成后,终端将关闭。
  2. 经过测试或优化后,机器人会生成包含优化结果的报告(有关更多详细信息,请阅读本系列中的先前文章)。
  3. 自动优化器知道报告放在何处,因此它可以读取和处理报告。 一旦处理完成后,要么启动新的优化和测试阶段,亦或完成优化并将结果显示在“结果”选项卡中。 

稍后将研究优化机制的实现,而本章节之目的是以最少的技术细节来讲述程序操作过程。 不要忘记,您可将自己的算法添加到负责优化的代码部分当中。 自动优化器是启动优化过程的控制程序,但这些过程的逻辑可能不同。 此处是完结版应用程序得主选项卡屏幕截图。


如果您细看屏幕截图,位于绿色区域的第一个名为 “Select Optimiser” 的组合框,可提供程序中已实现的优化类型的选择。 

应用程序图形界面切分为 2 个主栏。 主栏之一是设置(Settings)。 当我们需要启动或停止优化时,会于此处开始操作。 该栏在第二部分中有过详细讲述。 另一个板面 “Result” 则是显示优化结果的位置。

首先,在启动自动优化器时,我们需要选择要使用的终端。 终端选择原理与先前启动优化的图形界面相同。 换言之,自动优化器将找到此计算机上安装的所有终端(只有那些采用标准安装过程的终端,直接复制/导入的不在此列)。 

“设置(Settings)”栏分为 4 个部分。 每个屏幕部分的边框都可以拖拽。 当算法有很多输入参数时,这尤其方便。

  • 屏幕的第一部分提供了来自 MetaTrader 优化器的参数列表。 每次更改所选终端时,都会更新其下“智能交易系统”的列表。 该列表包含位于所选终端相应目录下的所有智能交易系统。 指定 EA 时要考虑嵌套目录。 
  • 屏幕的第二部分包含所选算法的参数列表。 参数列表是在相应的 “MQL5/Profiles/Tester/{Expertname}.set” 文件里找到的。 如果所选 EA 没有对应的文件,则在第一个屏幕区域中选择 EA 之后,先要启动该终端的测试器。 然后,采用默认设置为请求文件创建设置。 之后,关闭终端。 每次从 “Available experts” 列表中选择其它智能交易系统时,该列表都会变化。 若您希望刷新已加载的参数列表,只需单击 “Update (*.set) file”,之后系统启动终端,更新列表,再关闭它。 在此之前,将删除现有文件,并在同一目录下创建一个新文件。
  • 屏幕的第三板面包含非常重要的参数:未加载数据数组的过滤和排序条件列表。 本系列的第一篇文章里详细讨论了排序过程。 如第一篇文章中所述,可以使用 “>=”,“<=”,“<||>” 等来指定过滤条件。 
  • 屏幕的第四个板面包含一系列前向验证和历史时段通关优化。 间隔的逻辑如上所述。 在此轮的第一篇文章中,已涵盖了通关优化之间的排序和交互的实现。 请注意,在启动测试而非优化时(参数 Optimisation model = Disabled),该字段应含有一个历史时间间隔,或两个间隔(历史和前向)。 鉴于每次启动自动优化器期间都要输入数据实在太无聊(依据我的经历验证),故此实现了一种将数据保存在文件里的机制。 单击“保存/加载(Save/Load,)”后,将检查参数列表中之前输入的数据的可用性。 如果列表已填写,则将接收到的参数保存到文件当中。 如果列表为空,选择一个文件,将从中加载含有通关优化数据的列表。 内部文件结构将在今后的文章里讲述。 像该程序生成的所有其他文件一样,它是 xml 格式。 请注意,日期输入格式为 “DD.MM.YYYY”,而数据的显示格式为 “MM.DD.YYYY”。 这是因为日期会自动转换为字符串。 这对我来说不是很关键,这就是为什么我决定不干涉此行为。      

同样,由于设置文件的文本格式,我们无法区分算法参数的格式。 例如,所有枚举参数都作为整数显示。 这就是为什么决定在屏幕的第二个板面中以字符串形式显示参数列表的原因。 如果这不利于您直接在自动优化器中配置优化步骤,和其他机器人参数,您可以在终端里执行所有必需的设置。 然后,在更改测试器里的板面之后(或在关闭终端之后),设置将被写入期望的文件。 您所要做的就是在自动优化器中选择所需的算法 — 它会立即随您的设置一同加载。

从自动优化器启动优化或测试时,需要设置以下字段:

  1. 选择 EA。
  2. 检查要优化的参数,并选择范围(就像在终端中一样)。
  3. 选择至少一个排序标准:这将影响机器人所生成数据的排序,且在前向验证间隔中会启用最佳结果(在测试运行中可省略)。
  4. 选择前向验证优化日期(如果运行测试,则选择测试日期)。   
  5. 若您运行优化,选择所需的优化器(“Select Optimiser::”下拉列表)。 
  6. 指定 "Asset name" — 将依据此品种名称执行请求的操作。 发生错误时,终端将无法运行测试或优化。
  7. 您可以使用 “Directory prefix”,为您要保存的优化通关测试结果名称的附加规范。 优化结束后,将在一个特殊的程序内部目录中创建一个包含优化结果的文件夹。 文件夹名称设置如下:“{目录前缀} {选定的优化器} {智能交易系统名称} {资产名称}”。 这些是显示在“优化:”列表中的名称,它们可从中其内上载,以便查看和进一步分析。
  8. “Rewrite” 或 “Append” 参数的下拉列表也是可选的。 如果它在保存参数的文件里找到同名的结果,它会设置自动优化器应执行的操作。 如果f "Rewrite" 被选中,所有文件都将被新内容覆盖。 如果 "Append" 被选中,匹配的优化日期将被覆盖。 如果在当前优化间隔列表和先前保存的间隔列表中找到相同的间隔,则原保存的结果将被新的结果覆盖。 如果范围是新的,它们只会在现有范围之外添加。

一旦完成设置后,单击 “Start/Stop” 来启动该过程。 再次单击此按钮可中断优化过程。 在优化过程中,其状态将显示在自动优化器窗口底部的进度栏和文本标签中。 优化结束后,优化结果将上传到 “Result” 板面,且进度栏将重置为初始状态。 不过,如果单击 “Start / Stop” 运行测试,则终端不会自动关闭。 这样做是出于方便起见,它允许用户查检所有必需的数据。 一旦您研究过所需数据,请手动关闭终端,以便继续自动优化器的操作。 请不要忘记终端应始终关闭,因为应用程序必须能够独立掌管终端。

另外,您应该在启动优化之前配置优化器本身。 这并非强制性需求,但是优化管理器的结构允许创建自定义优化器,以及为每个优化器设置独立的参数。 单击优化器选择器组合框旁边的 “GUI” 按钮可以打开设置。 已实现的优化器的设置如下:

  1. Test on ticks — 指示历史和前向验证测试中的数据测试方法。 优化方法在 “Settings” 窗口的第一个板面中指定,而测试方法在优化器设置中指明。 如果启用该选项,将依据即时报价执行测试。 如果禁用,则以 OHLC 1 分钟模式执行测试。 
  2. Replace real dates to set — 是否用所传递的日期替换实际的优化开始日期和结束日期。 优化的开始和结束时间采用 1 分钟时间帧保存。 有时,如果没有交易或恰逢假期,则实际开始日期和结束日期可能与指定的日期会有出入。 如果启用此选项,则优化器将设置更贴合的数据,并知晓结果属于哪个确定的间隔。 不过,若您要查看实际交易日期,请不要勾选该选项。
  3. Use different shift for tick test — 我们在第二篇文章中讨论过可在结果中添加滑点和移位。 如果基于即时报价测试,则可以禁用滑点或完全减除。 此选项正是为此情况而添加。 它仅可在 “Test on ticks” 模式下激活。 若要使用该选项,请指定负责代表佣金和滑点的算法参数,并为其设置新值。 为机器人指定负责滑点的参数,并将其设置为 0,您可在即时报价测试模式下从结果中剔除滑点。 指定参数及其数值之后,可通过 “Add” 参数将此参数添加到表中,以便保存该参数。

没必要保存输入的参数值(这里没有 “Save” 按钮),因为它们会自动保存。 在启动优化之前,请关闭此窗口,从而防止在优化过程中意外更改参数。 


操控优化结果

启动优化后,请勿干预该过程。 还有,直至优化停止前,请勿从图表中删除 EA。 否则,优化器将把这种状况视为出错,因为日期不匹配。 一旦该过程完成,并关闭终端后,优化器会将优化报告加载到 Results 板面,您可以在其中评估完成的工作。 其结构显示在以下板面中:

 

结果版面也分为多个部分,并用数字标记,从而可简化说明。 第一部分出具优化通关测试,分为两个版面(“Selected pass” 和 “Optimisations”)。 第一个容器版面称为 “Selected pass”,其中包含选定的优化通关递次。 这些通关递次分为两个版面(“Forward” 和 “History”)。 我们看看优化参数如何在这些版面之间分布。 例如,指定以下日期:

  1. 01.01.2012 - 07.12.2012 - History
  2. 10.12.2012 - 07.03.2012 - Forward

优化将在历史间隔内执行。 然后,利用过滤和排序筛选最佳参数(请参阅上一章中的说明)。 选择之后执行两项测试:一项针对历史记录,另一项针对前向验证时间间隔。 进而,将最佳参数的测试结果添加到 “Selected pass” 选卡中,在 “History” 和 “Forward” 选卡之间进行切分。 优化通关过程将添加到 “Optimisations” 选卡中,您可以在其中查看任何历史间隔的整个优化列表。 更详尽地研究 “optimizations” 选卡。


表格结构(“Optimisations”选卡的第一部分)类似于 “Forward” 和 “History” 选卡的结构。 但是此表显示了在请求间隔内的所有优化通关递次。 可以从 “Optimisation dates” 组合框中选择所需的时间间隔。 选择新的时间间隔后,将更新含有优化通关递次的整个表格。 该选卡的第二部分类似于 “Settings” 窗口的第三部分,并且这些选卡中的所有更改都已同步。

“Optimisations” 选卡和 “Selected pass” 选卡都包含 “Save to (*.csv)” 按钮。 单击 “Selected pass” 按钮时,将创建一个 *.csv 文件,其中包含历史和前向验证递次的列表。 单击 “Optimisations” 按钮时,所有已执行优化的相关信息将下载到相应的 *.csv 文件之中。 按钮 “Sort” 和 “Filter” 仅在 “Optimisations” 选卡中可用。 它们的目的是根据 “Optimisations” 选卡第二部分中指定的设置,对生成的优化递次进行过滤和排序。 实际上,由于自动优化器采用相同的机制,因此不需要此选项。 不过,该选项允许利用任何期望的自定义过滤。 

两个表都是交互式的。 单击表格行会根据所选的优化通关递次更新 “Result” 板面的第二部分和第三部分。 双击则将在终端中启动测试。 在这种情况下,测试结束后将不会关闭终端。 因此,在研究结果之后,您应手动将其关闭。 在开始测试之前,可以配置一些测试器参数:“Results” 选卡的第二部分。

  

测试开始和结束日期会根据所选时间间隔自动更新。 然而,可以通过双击所需的行来更改它们。 您还可以选择执行延迟,和测试类型(即时报价,OHLC 等)。 "No delay" 和 "Every tick" 作为默认设置。 

该板面的第三部分显示了所选优化通关递次的交易统计信息(每日盈亏和最大盈亏/回撤),以及所选通关递次的机器人参数(Bot params 选卡)。 “Max PL/DD” 选卡不显示最终盈亏额,而仅仅显示总利润(所有盈利成交之和),和总亏损(所有亏损成交之和)。 在表格当中显示交易完成时登记的利润和最大亏损,并带有优化和测试结果。 “Daily PL” 选卡显示每日平均利润和每日平均亏损,类似于终端中提供的报告。

另一种运用自动优化器的最简单算法

我们研究一个运用自动优化器的机器人示例。 在本系列的第三篇文章中,我们曾研究过算法模板。 现在,我们修改模板令其适合 C 语言风格的算法。 首先,我们将研究算法本身。 该算法是 2 条移动平均值。 通过固定的止损或止盈来平仓。 描述 EA 逻辑的函数实现已从下面的代码中删除了,因为这并非示例的本意。

//+------------------------------------------------------------------+
//|                                                     SimpleMA.mq5 |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade/Trade.mqh>
#define TESTER_ONLY

input int ma_fast = 10; // MA fast
input int ma_slow = 50; // MA slow
input int _sl_ = 20; // SL
input int _tp_ = 60; // TP
input double _lot_ = 1; // Lot size

int ma_fast_handle,ma_slow_handle;
const double tick_size = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
CTrade trade;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

#ifdef TESTER_ONLY
   if(MQLInfoInteger(MQL_TESTER)==0 &&
      MQLInfoInteger(MQL_OPTIMIZATION)==0)
     {
      Print("This expert was created for demonstration! It is not enabled for real trading !");
      ExpertRemove();
      return(INIT_FAILED);
     }
#endif

   ma_fast_handle = iMA(_Symbol,PERIOD_CURRENT,ma_fast,0,MODE_EMA,PRICE_CLOSE);
   ma_slow_handle = iMA(_Symbol,PERIOD_CURRENT,ma_slow,0,MODE_EMA,PRICE_CLOSE);

   if(ma_fast_handle == INVALID_HANDLE ||
      ma_slow_handle == INVALID_HANDLE)
     {
      ExpertRemove();
      return(INIT_FAILED);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(ma_fast_handle != INVALID_HANDLE)
      IndicatorRelease(ma_fast_handle);
   if(ma_slow_handle != INVALID_HANDLE)
      IndicatorRelease(ma_slow_handle);
  }

enum Direction
  {
   Direction_Long,
   Direction_Short,
   Direction_None
  };

//+------------------------------------------------------------------+
//| Calculate stop                                                   |
//+------------------------------------------------------------------+
double get_sl(const double price, const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Calculate take                                                   |
//+------------------------------------------------------------------+
double get_tp(const double price, const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Open position according to direction                             |
//+------------------------------------------------------------------+
void open_position(const double price,const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Get direction                                                    |
//+------------------------------------------------------------------+
Direction get_direction()
  {
   ...
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(!PositionSelect(_Symbol))
     {
      Direction direction = get_direction();
      if(direction != Direction_None)
         open_position(iClose(_Symbol,PERIOD_CURRENT,0),direction);
     }
  }
//+------------------------------------------------------------------+

此 EA 代码部分只是基本的。 需要分析我们所进行的修改,从而令 EA 在自动优化器中可用。 

请注意,该任务不需要高效的 EA,因此该机器人很可能会亏损。 EA 有一条限制指令,以避免其在真实交易中意外启动。 若要删除限制(令其能够在交易中启动),请注释掉 TESTER_ONLY 定义。 

我们在 OnOnit 中所做的就是实例化 移动平均指标。 相应地,应在 OnDeinit 中将指标删除。 代码中声明的 Direction 枚举用于确定方向。 开仓由 CTrade 类在 open_position 函数中执行。 整个逻辑在 OnTick 回调中用四行代码描述。 现在,我们将所需功能的连接添加到机器人。

//+------------------------------------------------------------------+
//|                                                     SimpleMA.mq5 |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade/Trade.mqh>
#include <History manager/AutoLoader.mqh> // Include CAutoUploader
#define TESTER_ONLY

input int ma_fast = 10; // MA fast
input int ma_slow = 50; // MA slow
input int _sl_ = 20; // SL
input int _tp_ = 60; // TP
input double _lot_ = 1; // Lot size

// Comission and price shift (Article 2) 
input double _comission_ = 0; // Comission
input int _shift_ = 0; // Shift

int ma_fast_handle,ma_slow_handle;
const double tick_size = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
CTrade trade;
CAutoUploader * auto_optimiser; // Pointer to CAutoUploader class (Article 3)
CCCM _comission_manager_; // Comission manager (Article 2)

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

#ifdef TESTER_ONLY
   if(MQLInfoInteger(MQL_TESTER)==0 &&
      MQLInfoInteger(MQL_OPTIMIZATION)==0)
     {
      Print("This expert was created for demonstration! It is not enabled for real trading !");
      ExpertRemove();
      return(INIT_FAILED);
     }
#endif

   ma_fast_handle = iMA(_Symbol,PERIOD_CURRENT,ma_fast,0,MODE_EMA,PRICE_CLOSE);
   ma_slow_handle = iMA(_Symbol,PERIOD_CURRENT,ma_slow,0,MODE_EMA,PRICE_CLOSE);

   if(ma_fast_handle == INVALID_HANDLE ||
      ma_slow_handle == INVALID_HANDLE)
     {
      ExpertRemove();
      return(INIT_FAILED);
     }

   // Set Commission and shift
   _comission_manager_.add(_Symbol,_comission_,_shift_);

   // Add robot params
   BotParams params[];
   APPEND_BOT_PARAM(ma_fast,params);
   APPEND_BOT_PARAM(ma_slow,params);
   APPEND_BOT_PARAM(_sl_,params);
   APPEND_BOT_PARAM(_tp_,params);
   APPEND_BOT_PARAM(_lot_,params);
   APPEND_BOT_PARAM(_comission_,params);
   APPEND_BOT_PARAM(_shift_,params);

   // Add Instance CAutoUploader class (Article3)
   auto_optimiser = new CAutoUploader(&_comission_manager_,"SimpleMAMutex",params);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(ma_fast_handle != INVALID_HANDLE)
      IndicatorRelease(ma_fast_handle);
   if(ma_slow_handle != INVALID_HANDLE)
      IndicatorRelease(ma_slow_handle);

   // Delete CAutoUploaderclass (Article 3)
   delete auto_optimiser; 
  }

enum Direction
  {
   Direction_Long,
   Direction_Short,
   Direction_None
  };

//+------------------------------------------------------------------+
//| Calculate stop                                                   |
//+------------------------------------------------------------------+
double get_sl(const double price, const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Calculate take                                                   |
//+------------------------------------------------------------------+
double get_tp(const double price, const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Open position according to direction                             |
//+------------------------------------------------------------------+
void open_position(const double price,const Direction direction)
  {
   ...   
  }

//+------------------------------------------------------------------+
//| Get direction                                                    |
//+------------------------------------------------------------------+
Direction get_direction()
  {
   ...
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   auto_optimiser.OnTick(); // Save current date (Article 3)

   if(!PositionSelect(_Symbol))
     {
      Direction direction = get_direction();
      if(direction != Direction_None)
         open_position(iClose(_Symbol,PERIOD_CURRENT,0),direction);
     }
  }
//+------------------------------------------------------------------+

所有新添加的内容均用绿色标记。 我们按它们出现顺序研究它们。 首先,我们连接第三篇文章里中讲述的 AutoLoader 头文件。 该文件包含 CAutoUploader 类,其任务是下载积累的交易历史记录。 在 OnInit 回调中,我们将佣金添加到第二篇文章中讲述的相应 CCCM 类中。 另外,我们在向其添加 EA 参数后,实例化 CAutoUploader 类。 在 OnDeinit 回调中删除 CAutoUploader 类实例,该实例将初始调用析构函数,在该析构函数中,交易报告将保存到 xml 文件之中(第一篇文章)。 

EA 逻辑不变,但 OnTick 回调除外,在该回调中,将调用 CAutoUploader 类的 OnTick 方法。 该请求可以正确保存测试的开始和结束日期。 CAutoUploader 类仅在测试器中起作用,在实盘交易中不执行任何操作。 


结束语

所呈现的文章讲述了所创建优化管理器的功能。 如本文开头所提到的,本文应视作针对所得应用程序的一部指南。 应用程序里所实现的技术层面,将在其他文章中进行讲述。 本文还附带如下内容:

  • 自动优化器项目
  • 所讲述的 EA 代码

若要运行自动优化器程序,请使用 Visual Studio IDE 对其进行编译。 请注意,MQL5/Include/Libraries 目录应包含第一篇文章中讲述的 “ReportManager.dll” 库文件。 它在随附的 Auto Optimizer 项目中也要用到(需编译)。    

全部回复

0/140

量化课程

    移动端课程