可视化使用选定标准优化的结果·综合运用
(3/3)·把多品种余额、回撤与收藏曲线塞进同一张 GUI,优化结果不再靠肉眼翻表
「GUI 交互事件的三种触发路径」
在 MT5 自定义优化分析面板里,图形界面的响应靠的是自定义事件和图表事件两套机制。鼠标点表格行、下拉框换标准都走 ON_CLICK_LIST_ITEM 自定义事件;键盘选行则依赖 CHARTEVENT_KEYDOWN。事件处理函数里按元件 ID 逐一匹配,命中即返回 true,否则继续往下查。 点行高亮时,程序先用 element_id 比对主表格 ID,不一致直接退出。匹配后从第一列取 pass 索引,再调 CFrameGenerator::GetFrameData 展开该 pass 对应的品种与权益数据,最后在结果图里高亮对应余额曲线。下拉框有三个标准可选:自定义回测返回值、总利润、采收率,选中项索引会映射成列号 1、2 或 5。 键盘选行由 CProgram::SelectingResultsUsingKeys 处理,接收按键代码。KEY_UP / KEY_DOWN 做行号 ±1,KEY_HOME / KEY_END 跳首末行;若已无高亮、重复选中或越界则退出。通过检查后才重绘高亮并补滚动条。外汇与贵金属优化结果受点差跳空影响大,这类交互仅辅助筛参,实盘前仍需手动复核高风险品种。 下面代码是三处核心逻辑的原样片段,黄底行即事件分发与列映射的关键调用。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 通过鼠标左键点击突出显示表格行 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CProgram::TableRowSelection(class="kw">const class="type">long element_id) { class=class="str">"cmt">//--- 突出显示表格行 if(element_id!=m_table_main.Id()) class="kw">return(class="kw">false); class=class="str">"cmt">//--- 从表格中取得通过索引 class="type">class="kw">ulong pass=(class="type">class="kw">ulong)m_table_main.GetValue(class="num">0,m_table_main.SelectedItem()); class=class="str">"cmt">//--- 根据通过索引取得数据 m_frame_gen.GetFrameData(pass); class=class="str">"cmt">//--- 把交易品种加入表格 GetFrameSymbolsToTable(); class=class="str">"cmt">//--- 根据通过索引在图形中突出显示曲线 SelectCurve(pass); class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 根据指定的标准显示结果 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CProgram::ShowResultsBySelectedCriteria(class="kw">const class="type">long element_id) { class=class="str">"cmt">//--- 检查元件 ID if(element_id!=m_criterion.Id()) class="kw">return(class="kw">false); class=class="str">"cmt">//--- 定义取得最佳结果的标准索引 class="type">int index=m_criterion.GetListViewPointer().SelectedItemIndex(); class="type">int column_index=(index<class="num">1)? class="num">1 : (index==class="num">1)? class="num">2 : class="num">5; m_frame_gen.OnChangedSelectionCriteria(column_index); class=class="str">"cmt">//--- 可视化接收到的最佳结果 GetBestOptimizationResults(); class=class="str">"cmt">//--- 把数据输出到优化结果表格 GetFrameDataToTable(); class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 事件处理函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CProgram::OnEvent(class="kw">const class="type">int id,class="kw">const class="type">long &lparam,class="kw">const class="type">class="kw">double &dparam,class="kw">const class="type">class="kw">string &sparam) { ... class=class="str">"cmt">//--- 点击表格行的事件 if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM) { class=class="str">"cmt">//--- 突出显示表格行
用方向键驱动回测结果导航
在 MT5 自定义面板里,回测结果往往以表格形式堆叠,手动点选效率低。把键盘事件接进表格选中逻辑,能让交易者用上下键、Home、End 直接漫游所有通过项。 核心入口是 OnEvent 捕获 CHARTEVENT_KEYDOWN,把按键值 lparam 丢给 SelectingResultsUsingKeys。函数先取当前高亮行 selected_row=m_table_main.SelectedItem(),再按 KEY_UP / KEY_DOWN 做 -- 或 ++,KEY_HOME 跳 0、KEY_END 跳 RowsTotal()-1。 越界与重复选中会被直接拦掉:selected_row 等于 WRONG_VALUE、等于已选行、或落在 [0, RowsTotal()) 之外时返回 false,不刷新界面。合法移动后 SelectRow + Update 重绘,并同步纵向滚动条。 选中后从表格第 0 列取通过索引 ulong pass,调 GetFrameData(pass) 抓数据、GetFrameSymbolsToTable 填品种、SelectCurve(pass) 在图上线。外汇与贵金属回测结果导航涉及杠杆与滑点,实际操作高风险,按键逻辑仅解决交互效率,不预示任何收益。
class="type">bool CProgram::SelectingResultsUsingKeys(class="kw">const class="type">long key) { class=class="str">"cmt">//--- 取得突出显示行的索引 class="type">int selected_row=m_table_main.SelectedItem(); class=class="str">"cmt">//--- 定义方向和行数,用于移动滚动条 class="kw">switch((class="type">int)key) { case KEY_UP : selected_row--; break; case KEY_DOWN : selected_row++; break; case KEY_HOME : selected_row=class="num">0; break; case KEY_END : selected_row=(class="type">int)m_table_main.RowsTotal()-class="num">1; break; } class=class="str">"cmt">//--- 如果 (class="num">1) 没有突出显示的行 或者 (class="num">2) 之前选择的行已经突出显示 或者 (class="num">3) 我们超出了列表范围 if(selected_row==WRONG_VALUE || selected_row==m_table_main.SelectedItem() || selected_row<class="num">0 || selected_row>=(class="type">int)m_table_main.RowsTotal()) class="kw">return(class="kw">false); class=class="str">"cmt">//--- 突出显示行,并移动滚动条 m_table_main.SelectRow(selected_row); m_table_main.Update(); m_table_main.GetScrollVPointer().Update(true); class=class="str">"cmt">//--- 从突出显示表格行中取得通过国的索引 class="type">class="kw">ulong pass=(class="type">class="kw">ulong)m_table_main.GetValue(class="num">0,m_table_main.SelectedItem()); class=class="str">"cmt">//--- 根据通过索引取得数据 m_frame_gen.GetFrameData(pass); class=class="str">"cmt">//--- 把交易品种加入表格 GetFrameSymbolsToTable(); class=class="str">"cmt">//--- 根据通过索引在图形中突出显示曲线 SelectCurve(pass); class="kw">return(true); } class="type">void CProgram::OnEvent(class="kw">const class="type">int id,class="kw">const class="type">long &lparam,class="kw">const class="type">class="kw">double &dparam,class="kw">const class="type">class="kw">string &sparam) { class=class="str">"cmt">//--- 按下了按钮 if(id==CHARTEVENT_KEYDOWN) { class=class="str">"cmt">//--- 使用按键选择结果 if(SelectingResultsUsingKeys(lparam)) class="kw">return; class=class="str">"cmt">//--- class="kw">return; } }
◍ 画得少,看得清
把优化结果塞进帧数组之后,真正值钱的是筛选视角而不是堆指标。原文思路里,除了余额和统计值,整份测试报告都能落进帧数组,再用下拉框或勾选框拼出自定义公式来挑结果——没有 GUI 这层壳,这种交互基本没法落地。 MQL5.zip 里那 74.2 KB 的包含六个文件,MacdSampleCFrames.mq5 是改过的多品种 MACD Sample,FrameGenerator.mqh 管优化结果操作,CreateGUI.mqh 实现面板方法。读者直接拖进 MT5 就能跑,但注意 2021 年有用户反馈新版 EasyAndFastGUI 改了 AddItem 行为,面板空白,全新 MT5 重装才解决。 下面这段是社区里补的状态栏修复插入行,本质是在循环里给每项拼独立 id 再 AddItem,避开批量添加时的索引错乱: //--- 指定部件数量并设置其属性 for(int i=0;i<STATUS_LABELS_TOTAL; i++) { string status_add="status_bar_"+IntegerToString(i); // 插入一行以修复错误。 m_status_bar.AddItem(status_add,width[i]); } 外汇与贵金属优化结果可视化只是辅助筛样,不等于信号可靠,实盘前务必在策略测试器里用历史数据复验,杠杆品种回测偏差可能很大。
class=class="str">"cmt">//--- 指定部件数量并设置其属性 for(class="type">int i=class="num">0;i<STATUS_LABELS_TOTAL; i++) { class="type">class="kw">string status_add="status_bar_"+IntegerToString(i); class=class="str">"cmt">// 插入一行以修复错误。 m_status_bar.AddItem(status_add,width[i]); }