连续前行优化 (第七部分): 将自动优化器的逻辑部分与图形绑定,并通过程序控制图形·综合运用
📘

连续前行优化 (第七部分): 将自动优化器的逻辑部分与图形绑定,并通过程序控制图形·综合运用

第 3/3 篇

「回测结束后的界面状态复位逻辑」

在 MT5 优化器外壳的 C# 桥接层里,Model_PropertyChanged 是监听底层回测引擎状态翻转的核心入口。当 e.PropertyName 命中 "StopTest" 或 "ResumeEnablingTogle",说明测试收尾或按钮锁需要解除,此时把 EnableMainTogles 置 true,并把 Status 清空、Progress 归 0,再通过 dispatcher.Invoke 把这三个属性变更推给 GUI 线程。 若命中 "AllOptimisationResults",代表一轮优化_pass_列表已刷新:先 Clear 旧的 ReportDateBorders,再遍历 model.AllOptimisationResults 的 Keys 把日期边界塞回去,SelectedReportDateBorder 强制选 0 号位。随后用 ReplaceBotFixedParam 把 Expert / Deposit / Currency / Laverage 四个固定项按已加载结果覆写,最后弹 MessageBox "Report params where updated" 告知载入完成。 这段机制对外汇与贵金属品种同样适用,但跨品种优化时点差与杠杆跳变可能引发结果失真,回测结论仅具概率意义,实盘前必须在 MT5 用真实点差重跑验证。

MQL5 / C++
class="kw">private class="type">void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
  class=class="str">"cmt">// The test has completed, or you need to resume the availability of the buttons locked at the optimization or test start
  if (e.PropertyName == "StopTest" ||
      e.PropertyName == "ResumeEnablingTogle")
  {
    class=class="str">"cmt">// button accessibility class="kw">switch = true
    EnableMainTogles = true;
    class=class="str">"cmt">// Reset status and progress
    Status = "";
    Progress = class="num">0;
    class=class="str">"cmt">// Notify the GUI of changes
    dispatcher.Invoke(() =>
    {
      OnPropertyChanged("EnableMainTogles");
      OnPropertyChanged("Status");
      OnPropertyChanged("Progress");
    });
  }
  class=class="str">"cmt">// Changed the list of passed optimization passes
  if (e.PropertyName == "AllOptimisationResults")
  {
    dispatcher.Invoke(() =>
    {
      class=class="str">"cmt">// Clear the previously saved optimization passes and add new ones
      ReportDateBorders.Clear();
      foreach(var item in model.AllOptimisationResults.AllOptimisationResults.Keys)
      {
        ReportDateBorders.Add(item);
      }
      class=class="str">"cmt">// Select the very first date
      SelectedReportDateBorder = class="num">0;
      class=class="str">"cmt">// Fill in the fixed settings of the tester in accordance with the settings of the uploaded results
      ReplaceBotFixedParam("Expert", model.AllOptimisationResults.Expert);
      ReplaceBotFixedParam("Deposit", model.AllOptimisationResults.Deposit.ToString());
      ReplaceBotFixedParam("Currency", model.AllOptimisationResults.Currency);
      ReplaceBotFixedParam("Laverage", model.AllOptimisationResults.Laverage.ToString());
      OnPropertyChanged("OptimiserSettingsForResults_fixed");
    });
    class=class="str">"cmt">// Notify when data loading is complete
    System.Windows.MessageBox.Show("Report params where updated");
  }
  class=class="str">"cmt">// Filter or sort optimization passes
  if (e.PropertyName == "SortedResults" ||

◍ 属性变更驱动下的报告集合刷新

这段逻辑挂在 ViewModel 的属性变更通知上,靠 e.PropertyName 区分是哪一类数据到了,再借 dispatcher.Invoke 把更新塞回 UI 线程,避免跨线程写 ObservableCollection 直接抛异常。 FilteredResults 触发时只重设了 SelectedReportDateBorder 自身绑定,相当于通知界面边界索引变了;ForwardOptimisations 和 HistoryOptimisations 则是先 Clear 再逐条 new ReportItem(item) 填回,保证前后向与历史优化结果列表和底层 model 同步。 CSV 分支最简单,收到信号就弹个 MessageBox 提示 '(*.csv) File saved',实盘回测导出时你能靠这个确认落盘。 SelectedReportDateBorder 的 setter 里有个隐藏点:value 为 -1 时把内部字段归 0 并直接 return,不加载任何优化结果;否则按 ReportDateBorders[value] 从 model 的总表里取对应区间,清空 AllOptimisations 后重新 Add。ReportDateBorders 为空时同样提前 return,避免索引越界。

MQL5 / C++
if (e.PropertyName == "FilteredResults")
{
	dispatcher.Invoke(() =>
	{
		SelectedReportDateBorder = SelectedReportDateBorder;
	});
}
class=class="str">"cmt">// Updated forward optimization data
if (e.PropertyName == "ForwardOptimisations")
{
	dispatcher.Invoke(() =>
	{
		ForwardOptimisations.Clear();
		foreach(var item in model.ForwardOptimisations)
		{
			ForwardOptimisations.Add(new ReportItem(item));
		}
	});
}
class=class="str">"cmt">// Updated historical optimization data
if (e.PropertyName == "HistoryOptimisations")
{
	dispatcher.Invoke(() =>
	{
		HistoryOptimisations.Clear();
		foreach(var item in model.HistoryOptimisations)
		{
			HistoryOptimisations.Add(new ReportItem(item));
		}
	});
}
class=class="str">"cmt">// Save(*.csv) file with optimization/test results
if (e.PropertyName == "CSV")
{
	System.Windows.MessageBox.Show("(*.csv) File saved");
}
class="macro">#region Selected optimisation date border index keeper
class="kw">private class="type">int _selectedReportDateBorder;
class="kw">public class="type">int SelectedReportDateBorder
{
	get => _selectedReportDateBorder;
	set
	{
		AllOptimisations.Clear();
		if (value == -class="num">1)
		{
			_selectedReportDateBorder = class="num">0;
			class="kw">return;
		}
		_selectedReportDateBorder = value;
		if (ReportDateBorders.Count == class="num">0)
			class="kw">return;
		List<OptimisationResult> collection = model.AllOptimisationResults.AllOptimisationResults[ReportDateBorders[value]];
		foreach(var item in collection)
		{
			AllOptimisations.Add(new ReportItem(item));
		}
	}
}
class="macro">#endregion

记住这一条就够了

这套连续前行优化器的代码已经随文打包在 Auto_Optimiser.zip(125.7 KB)里,编译入口是 MetaTrader Auto Optimiser 项目,依赖第一篇文章里的 ReportManager.dll 丢进 MQL5/Libraries 才能跑通。没碰过 Visual Studio 的人,按 CTRL+SHIFT+B 是最省事的编译方式,成品会落在 bin/Debug 或 bin/Release 两个文件夹之一。 下一篇才会补上用户评论里提到的 UI 改进和自动分段功能,这一篇只交代清楚已有工程怎么接你自己的 EA——第三、四篇讲了机器人适配自动优化器的必需步骤,照着接就行。外汇和贵金属市场杠杆高、滑点狠,这类优化器只是把回测流程自动化,实盘表现仍可能大幅偏离历史。 真要动手验证,先编译附带的测试机器人,再拿你手头任一个 MT5 EA 走一遍第三、四篇的链接流程;跑通一次,比读十篇说明都管用。

常见问题

会在属性变更驱动下自动复位界面状态,避免旧配置污染新一轮优化;跑完直接看复位后的干净视图即可。
属性变更会触发报告集合自动刷新逻辑,正常不用手动重开;若没刷先检查是否真正提交了属性变更事件。
小布可在回测结束和属性变更时自动提示界面状态与报告集合是否已复位刷新,你把重复劳动交给小布,专注决策就行。
记住这一条就够了:任何图形状态变更都必须走程序属性驱动,别手动改界面,否则逻辑与图形会脱钩。
是,多在属性变更未正确触发报告刷新或复位时发生;用程序控制图形事件流就能避开大多数坑。