这是“连续前行优化”系列中的新篇章。 在此,我会演述所创建的程序,可实现编程的自动优化。 之前的文章论述了终端侧和函数库侧的程序实现细节,这些程序可与生成的优化报告一起运作。 您可以在以下链接查看这些文章:
本文演示了所创建产品的概况,可作为指南。 已创建的自动优化器功能可以扩展。 因此,我们要进一步讨论的算法可以轻松地替换为您自己的优化算法,从而令您可以实现任何期望的想法。 另外,深入探讨会揭示所创建程序的内部结构。 本文主要目的在于阐述运用得到的应用程序进行操控的机制及其能力。 事实是,我的同事有时发现很难快速理解它。 然而,自动优化器的设置和运用非常容易 — 我会进一步展示这一点。 因此,本文可视为应用程序使用指南,其中涵盖了所有可能的陷阱和设置细节。
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 年间隔能提供 4 次压力测试和 1 个最终优化。
现在我们已经讨论了应用程序执行的过程,我们来研究一下它的用法。 该系列文章,是我先前有关优化过程管理图形界面文章的逻辑延续:
文章介绍了如何在终端中以受控过程运行优化或测试。 本系列中采用相同的方法。 不过,基本区别之一是控制过程不是作为终端的附加实现的,而是作为独立程序实现的。 这种方法可供计算机上安装的所有终端使用。 在先前的系列文章中,我们创建了一个扩展,可以从工作终端启动。 该扩展程序可供计算机上启动的所有终端设备使用。 当前应用程序还可以访问计算机上安装的所有终端,但是每次只能操控一个终端,且它可以启动任何所需的终端。
为确保自动优化器成功运行,请确保在启动应用程序之前关闭选定的终端。
该应用程序的运行方式如下:
稍后将研究优化机制的实现,而本章节之目的是以最少的技术细节来讲述程序操作过程。 不要忘记,您可将自己的算法添加到负责优化的代码部分当中。 自动优化器是启动优化过程的控制程序,但这些过程的逻辑可能不同。 此处是完结版应用程序得主选项卡屏幕截图。
如果您细看屏幕截图,位于绿色区域的第一个名为 “Select Optimiser” 的组合框,可提供程序中已实现的优化类型的选择。
应用程序图形界面切分为 2 个主栏。 主栏之一是设置(Settings)。 当我们需要启动或停止优化时,会于此处开始操作。 该栏在第二部分中有过详细讲述。 另一个板面 “Result” 则是显示优化结果的位置。
首先,在启动自动优化器时,我们需要选择要使用的终端。 终端选择原理与先前启动优化的图形界面相同。 换言之,自动优化器将找到此计算机上安装的所有终端(只有那些采用标准安装过程的终端,直接复制/导入的不在此列)。
“设置(Settings)”栏分为 4 个部分。 每个屏幕部分的边框都可以拖拽。 当算法有很多输入参数时,这尤其方便。
同样,由于设置文件的文本格式,我们无法区分算法参数的格式。 例如,所有枚举参数都作为整数显示。 这就是为什么决定在屏幕的第二个板面中以字符串形式显示参数列表的原因。 如果这不利于您直接在自动优化器中配置优化步骤,和其他机器人参数,您可以在终端里执行所有必需的设置。 然后,在更改测试器里的板面之后(或在关闭终端之后),设置将被写入期望的文件。 您所要做的就是在自动优化器中选择所需的算法 — 它会立即随您的设置一同加载。
从自动优化器启动优化或测试时,需要设置以下字段:
一旦完成设置后,单击 “Start/Stop” 来启动该过程。 再次单击此按钮可中断优化过程。 在优化过程中,其状态将显示在自动优化器窗口底部的进度栏和文本标签中。 优化结束后,优化结果将上传到 “Result” 板面,且进度栏将重置为初始状态。 不过,如果单击 “Start / Stop” 运行测试,则终端不会自动关闭。 这样做是出于方便起见,它允许用户查检所有必需的数据。 一旦您研究过所需数据,请手动关闭终端,以便继续自动优化器的操作。 请不要忘记终端应始终关闭,因为应用程序必须能够独立掌管终端。
另外,您应该在启动优化之前配置优化器本身。 这并非强制性需求,但是优化管理器的结构允许创建自定义优化器,以及为每个优化器设置独立的参数。 单击优化器选择器组合框旁边的 “GUI” 按钮可以打开设置。 已实现的优化器的设置如下:
没必要保存输入的参数值(这里没有 “Save” 按钮),因为它们会自动保存。 在启动优化之前,请关闭此窗口,从而防止在优化过程中意外更改参数。
启动优化后,请勿干预该过程。 还有,直至优化停止前,请勿从图表中删除 EA。 否则,优化器将把这种状况视为出错,因为日期不匹配。 一旦该过程完成,并关闭终端后,优化器会将优化报告加载到 Results 板面,您可以在其中评估完成的工作。 其结构显示在以下板面中:
结果版面也分为多个部分,并用数字标记,从而可简化说明。 第一部分出具优化通关测试,分为两个版面(“Selected pass” 和 “Optimisations”)。 第一个容器版面称为 “Selected pass”,其中包含选定的优化通关递次。 这些通关递次分为两个版面(“Forward” 和 “History”)。 我们看看优化参数如何在这些版面之间分布。 例如,指定以下日期:
优化将在历史间隔内执行。 然后,利用过滤和排序筛选最佳参数(请参阅上一章中的说明)。 选择之后执行两项测试:一项针对历史记录,另一项针对前向验证时间间隔。 进而,将最佳参数的测试结果添加到 “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 类仅在测试器中起作用,在实盘交易中不执行任何操作。
所呈现的文章讲述了所创建优化管理器的功能。 如本文开头所提到的,本文应视作针对所得应用程序的一部指南。 应用程序里所实现的技术层面,将在其他文章中进行讲述。 本文还附带如下内容:
若要运行自动优化器程序,请使用 Visual Studio IDE 对其进行编译。 请注意,MQL5/Include/Libraries 目录应包含第一篇文章中讲述的 “ReportManager.dll” 库文件。 它在随附的 Auto Optimizer 项目中也要用到(需编译)。
本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...
移动端课程