量化交易软件:使用图形界面处理优化结果
这是处理和分析优化结果想法的续篇,前一篇文章包含了使用 MQL5 应用程序图形界面来可视化优化结果的方法描述,这一次,任务会更复杂一点: 赫兹量化将选择100个最佳的优化结果并且在图形界面的表格中显示它们。
另外,赫兹量化继续开发多交易品种余额图的想法,它也在另一篇文章中谈到了。让赫兹量化把这两篇文章的思路综合起来,使用户可以在优化结果表格中选择一行并且在独立的图表中得到多交易品种的余额和回撤图。在优化了EA交易的参数之后,交易者将可以进行结果的快速分析并且选择合适的工作参数。

编辑切换为居中
开发图形界面
测试 EA 交易的 GUI 将包含下面的原件:
用于控件的表单
用于显示额外总体信息的状态栏
用于把元件分组的页面:
框架
用于管理在优化之后滚动结果所显示余额结果的数量
在滚动结果时的延迟毫秒数
用于开始滚动结果的按钮
显示余额结果数量的图形
全部结果的图形
结果
最佳结果的表格
余额
用于在表格中选中结果的多交易品种余额图形
用于在表格中选中结果的回撤图形
数据帧重放过程指示
用于创建上面所列出元件的方法的代码位于独立的包含文件(include 文件) 中,可以在 MQL 程序类中使用:
//+------------------------------------------------------------------+ //| 用于创建应用程序的类 | //+------------------------------------------------------------------+ class CProgram : public CWndEvents { private: //--- 窗口 CWindow m_window1; //--- 状态条 CStatusBar m_status_bar; //--- 页面 CTabs m_tabs1; //--- 文本框 CTextEdit m_curves_total; CTextEdit m_sleep_ms; //--- 按钮 CButton m_reply_frames; //--- 图表 CGraph m_graph1; CGraph m_graph2; CGraph m_graph3; CGraph m_graph4; //--- 表格 CTable m_table_param; //--- 进度条 CProgressBar m_progress_bar; //--- public: //--- 创建图形界面 bool CreateGUI(void); //--- private: //--- 表单 bool CreateWindow(const string text); //--- 状态条 bool CreateStatusBar(const int x_gap,const int y_gap); //--- 页面 bool CreateTabs1(const int x_gap,const int y_gap); //--- 文本框 bool CreateCurvesTotal(const int x_gap,const int y_gap,const string text); bool CreateSleep(const int x_gap,const int y_gap,const string text); //--- 按钮 bool CreateReplyFrames(const int x_gap,const int y_gap,const string text); //--- 图表 bool CreateGraph1(const int x_gap,const int y_gap); bool CreateGraph2(const int x_gap,const int y_gap); bool CreateGraph3(const int x_gap,const int y_gap); bool CreateGraph4(const int x_gap,const int y_gap); //--- 按钮 bool CreateUpdateGraph(const int x_gap,const int y_gap,const string text); //--- 表格 bool CreateMainTable(const int x_gap,const int y_gap); //--- 进度条 bool CreateProgressBar(const int x_gap,const int y_gap,const string text); }; //+------------------------------------------------------------------+ //| 用于创建控件的方法 | //+------------------------------------------------------------------+ #include "CreateGUI.mqh" //+------------------------------------------------------------------+
正如上面描述的,表格将显示100个最佳优化结果 (从最大结果利润角度考虑)。因为 GUI 是在优化开始之前创建的,表格最开始是空的。列的数量和抬头文字是在优化数据帧处理类中确定的,
让赫兹量化创建一个含有以下功能的表格:
显示表头
排序选项
可以选择一行
固定选中的行 (没有去除选择的功能)
人工调节列的宽度
斑马风格的格式
用于创建表格的代码显示在下面,为了使表格固定在第二个页面, 表格对象传到页面对象的时候要指定页面的索引,在这种情况下,表格的 主类是 'Tabs' 元件. 这样,如果页面区域的大小有了改变,表格的大小将也会相对它的主元件而改变,这是在 'Table' 元件的属性中指定的.
//+------------------------------------------------------------------+ //| 创建主表格 | //+------------------------------------------------------------------+ bool CProgram::CreateMainTable(const int x_gap,const int y_gap) { //--- 保存主控件的指针 m_table_param.MainPointer(m_tabs1); //--- 附加到页面 m_tabs1.AddToElementsArray(1,m_table_param); //--- 属性 m_table_param.TableSize(1,1); m_table_param.ShowHeaders(true); m_table_param.IsSortMode(true); m_table_param.SelectableRow(true); m_table_param.IsWithoutDeselect(true); m_table_param.ColumnResizeMode(true); m_table_param.IsZebraFormatRows(clrWhiteSmoke); m_table_param.AutoXResizeMode(true); m_table_param.AutoYResizeMode(true); m_table_param.AutoXResizeRightOffset(2); m_table_param.AutoYResizeBottomOffset(2); //--- 创建控件 if(!m_table_param.CreateTable(x_gap,y_gap)) return(false); //--- 把对象加到对象组的统一数组中 CWndContainer::AddToElementsArray(0,m_table_param); return(true); }
保存优化结果
CFrameGenerator 类是实现用于操作优化结果的,赫兹量化将使用来自文章 在赫兹量化中可视化交易策略的优化 中的版本,并且在其中加入所需的方法。除了在帧中保存总余额和最终统计结果外,赫兹量化还需要单独保存每个交易品种的余额和回撤。CSymbolBalance 独立数组结构将用于保存余额,该结构有两个目的,数据被保存到它的数组中,然后会在一个通用数组中传给一个数据帧,在优化之后,数据将会从数据帧数组中展开再传回给这个结构的数组中,被显示在多交易品种图形中。
//--- 用于所有交易品种余额的数组 struct CSymbolBalance { double m_data[]; }; //+------------------------------------------------------------------+ //| 用于操作优化结果的类 | //+------------------------------------------------------------------+ class CFrameGenerator { private: //--- 余额结构 CSymbolBalance m_symbols_balance[]; };
交易品种的枚举使用‘;’来分隔,将会作为字符串型参数传给数据帧。最开始,数据是以字符串数组的形式全部保存到数据桢的,但是字符串数组是不能传给数据帧的,尝试给 FrameAdd() 函数传一个字符串数组,将会在编译的时候产生错误:
字符串数组和包含对象的结构不被允许
另一个选项是把报告写到一个文件中,再把文件传给数据帧。然而,这个选项是不合适的: 赫兹量化将必须把结果特别频繁地记录到硬盘中。
所以,我决定把所有所需数据收集到一个数组中,然后根据帧参数中包含的键值来展开数据。统计变量将包含在数组的最开始,随后是总的余额,以及每个交易品种分别的独立余额,最后是两个轴上分别的回撤数据。
下面的结构图显示了数组中封装数据的顺序,只使用了两个交易品种,这样是为了使结构足够短。

编辑
图 1. 数组中数据分布的顺序.
所以,我们需要键值来确定数组中每个范围的索引,统计变量的数量是常数,会进一步判断。赫兹量化将在表格中显示5个变量和通过数,确保这个结果的数组可以在优化之后访问到:
//--- 统计参数的数量 #define STAT_TOTAL 6
通过数
测试结果
利润 (STAT_PROFIT)
交易数量 (STAT_TRADES)
回撤 (STAT_EQUITY_DDREL_PERCENT)
采收率 (STAT_RECOVERY_FACTOR)
余额数据的大小对总数据和单独的交易品种数据是相同的,这个值将以一个double 类型参数传给 FrameAdd()函数。为了确定测试中使用的交易品种,我们将在每个通过中定义它们,具体是根据交易历史在 OnTester() 函数中实现的。这个信息将会以字符串型参数传给 FrameAdd() 函数。