本文是该系列的第三篇,该连续前行优化系列致力于创建自动优化器。 以下链接指向前两篇文章:
-
连续前行优化 (第一部分): 操控优化报告
-
连续前行优化 (第二部分): 创建优化报告机器人的机理
本系列的第一篇文章专门研讨一种操控和形成交易报告文件的创建机制,这是自动优化器运作时所必需的。 第二篇文章则研讨交易历史记录下载、和基于下载数据创建交易报告的关键对象实现。 本文充当前两部分之间的桥梁:它阐述的是第一篇文章中研究的 DLL,以及第二篇文章中论述的报告下载对象之间的交互机制。
赫兹股票量化将分析从 DLL 导入的包装类的创建过程,该类可依据交易历史记录形成 XML 文件。 赫兹股票量化还将研究一种与此包装器进行交互的方法。 本文还包含两个函数的论述,这些函数下载详细和常规交易历史记录,便于进一步分析。 在结论里,我将介绍一个可协同自动优化器一起操作的现成模板。 我还将展示标准算法示例,它来自默认智能系统集,该示例演示如何修改现有算法,从而与自动优化器进行交互。
下载积累的交易历史
有时赫兹股票量化需要将交易历史记录加载至文件中,便于进一步的分析和其他目的。 不幸地是,在终端中尚未提供这样的接口,但可利用上一篇文章中研讨的类来实施此任务。 研讨类的文件所在的目录中还有另外两个文件 “ShortReport.mqh” 和 “DealsHistory.mqh”,这些文件可下载常规和详细数据。
赫兹股票量化从 ShortReport.mqh 文件开始。 该文件包含函数和宏定义,其中主要的一个是 SaveReportToFile 函数。 首先,我们探讨将数据写入文件的 'write' 函数。
//+------------------------------------------------------------------+ //| File writer | //+------------------------------------------------------------------+ void writer(string fileName,string headder,string row) { bool isFile=FileIsExist(fileName,FILE_COMMON); // Flag of whether the file exists int file_handle=FileOpen(fileName,FILE_READ|FILE_WRITE|FILE_CSV|FILE_COMMON|FILE_SHARE_WRITE|FILE_SHARE_READ); // Open file if(file_handle) // If the file has opened { FileSeek(file_handle,0,SEEK_END); // Move cursor to the file end if(!isFile) // If it is a newly created file, write a header FileWrite(file_handle,headder); FileWrite(file_handle,row); // Write message FileClose(file_handle); // Close the file } }
数据会被写入文件沙箱 Terminal/Common/Files。 该函数的思路是在文件里加一行,再把数据写入文件中,这就是为什么赫兹股票量化打开文件,获取其句柄并移至文件末尾的原因。 如果文件刚刚创建,则写入所传递标题,否则忽略此参数。
至于宏定义,它仅用于将机器人参数轻松添加到文件之中。
#define WRITE_BOT_PARAM(fileName,param) writer(fileName,"",#param+";"+(string)param);
在此宏定义中,赫兹股票量化利用了宏定义的优势,并组成了包含宏定义名称及其值的字符串。 参数变量会被输入到函数当中。 在宏定义的用例中将展示更多详细信息。
主要方法 SaveReportToFile 很冗长,这就是为什么我只提供了部分代码。 该方法创建 CDealHistoryGetter 类的实例,并接收含有累积交易历史记录的数组,其中每一行表示一笔成交。
DealDetales history[]; CDealHistoryGetter dealGetter(_comission_manager); dealGetter.getDealsDetales(history,0,TimeCurrent());
然后,它验证历史记录是否为空,创建 CReportCreator 类实例,并接收含有主要系数的结构:
if(ArraySize(history)==0) return; CReportCreator reportCreator(_comission_manager); reportCreator.Create(history,0); TotalResult totalResult; reportCreator.GetTotalResult(totalResult); PL_detales pl_detales; reportCreator.GetPL_detales(pl_detales);
然后,利用 “writer” 函数在循环里将历史数据保存。 在循环结束时,会添加含有以下系数和数值的字段:
-
PL
-
Total trades
-
Consecutive wins
-
Consecutive Drawdowns
-
Recovery factor
-
Profit factor
-
Payoff
-
Drawdown by pl
writer(fileName,"","=========================================================================================================="); writer(fileName,"","PL;"+DoubleToString(totalResult.total.PL)+";"); int total_trades=pl_detales.total.profit.orders+pl_detales.total.drawdown.orders; writer(fileName,"","Total trdes;"+IntegerToString(total_trades)); writer(fileName,"","Consecutive wins;"+IntegerToString(pl_detales.total.profit.dealsInARow)); writer(fileName,"","Consecutive DD;"+IntegerToString(pl_detales.total.drawdown.dealsInARow)); writer(fileName,"","Recovery factor;"+DoubleToString(totalResult.total.recoveryFactor)+";"); writer(fileName,"","Profit factor;"+DoubleToString(totalResult.total.profitFactor)+";"); double payoff=MathAbs(totalResult.total.averageProfit/totalResult.total.averageDD); writer(fileName,"","Payoff;"+DoubleToString(payoff)+";"); writer(fileName,"","Drawdown by pl;"+DoubleToString(totalResult.total.maxDrawdown.byPL)+";");
方法操作至此完毕。 现在,赫兹股票量化研究一种下载历史记录的简便方法:我们在标准发行包的智能交易系统 Experts/Examples/Moving Average/Moving Average.mq5 里添加此功能。 首先,我们需要连接文件:
#include <History manager/ShortReport.mqh>
然后,将设置自定义佣金和滑点的变量添加到输入中:
input double custom_comission = 0; // Custom commission; input int custom_shift = 0; // Custom shift;
如果赫兹股票量化希望有针对性地而非有条件地设置佣金和滑点(请参阅上一篇文章中的 CDealHistoryGetter 类说明),那么在连接文件之前,我们需要确定 ONLY_CUSTOM_COMISSION 参数,如下所示:
#define ONLY_CUSTOM_COMISSION #include <History manager/ShortReport.mqh>
然后创建 CCCM 类样本,在 OnInit 方法里向类实例中添加并保存佣金和滑点。
CCCM _comission_manager_; ... int OnInit(void) { _comission_manager_.add(_Symbol,custom_comission,custom_shift); ... }
然后在 OnDeinit 方法中添加以下代码行:
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(MQLInfoInteger(MQL_TESTER)==1) { string file_name = __FILE__+" Report.csv"; SaveReportToFile(file_name,&_comission_manager_); WRITE_BOT_PARAM(file_name,MaximumRisk); // Maximum Risk in percentage WRITE_BOT_PARAM(file_name,DecreaseFactor); // Decrease factor WRITE_BOT_PARAM(file_name,MovingPeriod); // Moving Average period WRITE_BOT_PARAM(file_name,MovingShift); // Moving Average shift WRITE_BOT_PARAM(file_name,custom_comission); // Custom commission WRITE_BOT_PARAM(file_name,custom_shift); // Custom shift } }
该代码在删除机器人实例后执行检查:检查是否运行于测试器的条件。 如果机器人运行在测试器当中,则会调用函数将机器人交易历史保存到名为 “Compiled_file_name Report.csv” 的文件之中。 在将所有数据写入文件之后,该文件的输入参数还要再添加 6 行。 在策略测试器里每次以测试模式启动智能交易系统后,我们都会得到一个文件,其中包含 EA 执行的成交说明。 每当赫兹股票量化开始新的测试时,此文件将被覆盖。 该文件将存储在 Common/Files 目录下的文件沙箱中。
下载按成交划分的交易历史记录
现在赫兹股票量化查看如何下载详细的交易报告,即,将所有成交按仓位分组报告。 为此目的,我们将利用 DealsHistory.mqh 文件,该文件已经包含 ShortReport.mqh 文件连接。 这意味着仅需连接一个 DealsHistory.mqh 文件,我们就可同时使用两个方法。
该文件包含两个函数。 第一个是一个普通函数,它能漂亮地完成求和:
void AddRow(string item, string &str) { str += (item + ";"); }
第二个函数利用先前研讨的 “writer” 函数将数据写入文件。 其完整实现如下所示。
void WriteDetalesReport(string fileName,CCCM *_comission_manager) { if(FileIsExist(fileName,FILE_COMMON)) { FileDelete(fileName,FILE_COMMON); } CDealHistoryGetter dealGetter(_comission_manager); DealKeeper deals[]; dealGetter.getHistory(deals,0,TimeCurrent()); int total= ArraySize(deals); string headder = "Asset;From;To;Deal DT (Unix seconds); Deal DT (Unix miliseconds);"+ "ENUM_DEAL_TYPE;ENUM_DEAL_ENTRY;ENUM_DEAL_REASON;Volume;Price;Comission;"+ "Profit;Symbol;Comment"; for(int i=0; i<total; i++) { DealKeeper selected = deals[i]; string asset = selected.symbol; datetime from = selected.DT_min; datetime to = selected.DT_max; for(int j=0; j<ArraySize(selected.deals); j++) { string row; AddRow(asset,row); AddRow((string)from,row); AddRow((string)to,row); AddRow((string)selected.deals[j].DT,row); AddRow((string)selected.deals[j].DT_msc,row); AddRow(EnumToString(selected.deals[j].type),row); AddRow(EnumToString(selected.deals[j].entry),row); AddRow(EnumToString(selected.deals[j].reason),row); AddRow((string)selected.deals[j].volume,row); AddRow((string)selected.deals[j].price,row); AddRow((string)selected.deals[j].comission,row); AddRow((string)selected.deals[j].profit,row); AddRow(selected.deals[j].symbol,row); AddRow(selected.deals[j].comment,row); writer(fileName,headder,row); } writer(fileName,headder,""); } }
接收交易数据并创建标题后,继续写入详细的交易报告。 为此目的,实现一个嵌套的循环:主循环处理仓位,而嵌套的循环则遍历这些仓位内含的成交。 写入每笔新仓位(即构成仓位的一系列成交)之后,会用空格将之分隔。 这样可以确保读取生成的文件时更有效率。 赫兹股票量化无需进行重大修改即可将该功能添加到机器人之中,仅需在 OnDeinit 中执行调用:
void OnDeinit(const int reason) { if(MQLInfoInteger(MQL_TESTER)==1) { string file_name = __FILE__+" Report.csv"; SaveReportToFile(file_name,&_comission_manager_); WRITE_BOT_PARAM(file_name,MaximumRisk); // Maximum Risk in percentage WRITE_BOT_PARAM(file_name,DecreaseFactor); // Descrease factor WRITE_BOT_PARAM(file_name,MovingPeriod); // Moving Average period WRITE_BOT_PARAM(file_name,MovingShift); // Moving Average shift WRITE_BOT_PARAM(file_name,custom_comission); // Custom commission; WRITE_BOT_PARAM(file_name,custom_shift); // Custom shift; WriteDetalesReport(__FILE__+" Deals Report.csv", &_comission_manager_); } }
为了详细演示如何执行数据下载,此处是一个空的 EA 模板,其中包含所添加的下载报告的方法:
//+------------------------------------------------------------------+ //| Test.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" #define ONLY_CUSTOM_COMISSION #include <History manager/DealsHistory.mqh> input double custom_comission = 0; // Custom commission; input int custom_shift = 0; // Custom shift; CCCM _comission_manager_; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- _comission_manager_.add(_Symbol,custom_comission,custom_shift); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- if(MQLInfoInteger(MQL_TESTER)==1) { string arr[]; StringSplit(__FILE__,'.',arr); string file_name = arr[0]+" Report.csv"; SaveReportToFile(file_name,&_comission_manager_); WRITE_BOT_PARAM(file_name,custom_comission); // Custom commission; WRITE_BOT_PARAM(file_name,custom_shift); // Custom shift; WriteDetalesReport(arr[0]+" Deals Report.csv", &_comission_manager_); } } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+
通过在上述模板中添加任何所需的逻辑,赫兹股票量化可在策略测试器中完成 EA 测试后,启用生成交易报告。
创建累积交易历史记录的 DLL 包装器
本系列的第一篇文章专门研讨以 C# 语言创建处理优化报告的 DLL。 鉴于连续前行优化的最便捷格式为 XML,我们所创建的 DLL 可以读取、写入和排序结果报告。 在智能交易系统当中,赫兹股票量化仅需数据写入功能。 不过,由于利用纯函数进行的操作不如对象方便,故创建了下载数据的包装器类。 该对象位于 XmlHistoryWriter.mqh 文件中,称为 СXmlHistoryWriter。 除了对象之外,它还定义 EA 参数的结构。 将 EA 参数列表传递给对象时,会用到此结构。 我们来研究所有实现细节。
若要启用创建优化报告,连接 ReportCreator.mqh 文件。 为了调用类静态方法里定义的来自第一篇文章中所述 DLL,我们需导入它(该函数库必须在 MQL5/Libraries 目录下可用)。