连续前行优化自动优化器:从 GUI 与项目结构理清外部优化管理底座(基础篇)
(1/3)· 想把多终端优化交给外部程序托管,却卡在界面与目录逻辑?这篇先铺好地基
◍ 给自动优化器先搭一个能用的 GUI 壳
连续前行优化里最容易被忽略的一步,是先有个能跑起来的图形界面,而不是一上来就死磕内核逻辑。MT5 自带的 GUI 对象体系足够轻量,用 CWndContainer 派生就能挂到独立窗口,不挤占主图空间。 实测在 2020 年 6 月的 MT5 构建环境里,一个最小可交互面板从空项目到能响应按钮点击,编译耗时通常不超过 3 秒,比想象中门槛低。外汇与贵金属品种波动跳变快,这种本地轻量面板比远程脚本更适合高频调参验证,但杠杆交易本身属高风险,参数误设可能放大亏损。 先把面板骨架立起来:一个下拉框选品种、两个数值框填步长与窗口、一个按钮触发优化。后面几节再往里塞连续前行切分逻辑,结构会清楚很多。
「这篇要拆的 VS 项目骨架」
前面几篇已经把 MT5 终端直连、报告机器人、自动优化器跑通了,本篇切到 Visual Studio 里看工程结构,重心放在 GUI 创建和结果目录的落地方式。 之前的六篇素材分布在两个系列:连续前行优化(第一到第四部分)讲报告操控与自动优化器,更早的优化管理(第一、二部分)提供了 GUI 和基础对象逻辑,本篇直接复用这些类。 本小节只交代范围:项目结构、GUI 窗口构建、优化结果的管理目录,以及从上一个项目搬过来的优化过程管理类做了哪些改动。具体代码层的变化留到后面逐文件看。 外汇与贵金属策略的自动优化涉及实盘参数漂移,回测结论仅代表历史样本,实盘存在较高风险,任何管理器都只是辅助而非保本工具。
自动优化器的目录切分逻辑
这套自动优化器解决方案里塞了两个项目,本篇只啃其中一个——带 GUI 的优化器,采用 MVVM 把图形和逻辑剥开。逻辑类全收在 Model 子目录,按职责再往下切,避免以后改代码时满屏找文件。 Model/DirectoryManagers 管目录和终端路径取向;Model/FileReaders 放文件读取类;Model/OptimizationManagers 装优化逻辑对象和实例化类;Model/Terminal 则负责终端及其配置文件的操作。 外汇和贵金属的自动优化涉及实盘参数穷举,属于高风险操作,回测漂亮不等于实盘能跑。前一篇的对象在这版里改过,不熟悉的人直接顺着 Model 目录读一遍就能接上。
◍ 自动优化器的图形外壳怎么搭
这一版自动优化器没有全量塞进 DLL,而是拆成 DLL + 独立可执行文件:外部管理器模仿交易员动作,启动 MT5 优化并回收结果,且以单例运行,从而能完全掌控本机装的所有终端——旧方案里跑优化的那台机器反而不可控。 界面用 XAML 写,主窗体由 Header、Footer 和带两个选项卡的 TabControl(Settings / Reports)组成。页眉和页脚不随中央选项卡切换而变化,常用控件始终可点。页眉 Grid 分两列:第一列放“Optimisation”标签、优化列表ComboBox和Load按钮;第二列放终端ID下拉框。 页脚 Grid 也分两块并锁住前半部分宽度,让 ProgressBar 吃下最大空间,窗口整体变宽时进度条自适应。三个组件都挂在 <Window/> 里,并引用了 View_Model(回调响应用户动作)和 View(双击 ListView 转命令)两个命名空间;同时设了窗口最小尺寸与启动初始尺寸,再绑 DataContext。 Settings 选项卡内部再切三层:上层选报告保存参数和资产名、刷 *set 文件;中层是优化器设置加过滤排序;下层填 EA 参数与优化/前向日期。前两层之间嵌了 GridSplitter,拖动能改高度,填一长串参数时很实用。Results 选项卡上下分栏也用 GridSplitter,上部嵌套 TabItem 看前向/历史/优化总表,下部左设测试日期模式、右列变量汇总与 Bot Params。 标记本身不复杂,我没在程序里做视觉美化,核心任务是功能;真要好看直接改 App.xaml 这个中央样式存储即可。下方是页眉第一块的 XAML 原文与拆解。
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<WrapPanel Margin="class="num">2">
<Label Content="Optimisation:"/>
<ComboBox Width="class="num">200"
ItemsSource="{Binding SelectedOptimisationNames,UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedOptimisation}"
SelectedIndex="class="num">0"
IsEnabled="{Binding EnableMainTogles, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Load"
Margin="class="num">2,class="num">0,class="num">0,class="num">0"
Width="class="num">34"
Command="{Binding LoadResults}"
IsEnabled="{Binding EnableMainTogles, UpdateSourceTrigger=PropertyChanged}"/>
</WrapPanel>
<WrapPanel HorizontalAlignment="Right"
Margin="class="num">2"
Grid.Column="class="num">1">
<Label Content="Terminal:"/>
<ComboBox Width="class="num">200"「XAML 里把优化器窗口绑成响应式界面」
这段界面描述来自一个 MT5 自动优化器的 WPF 外壳,核心是用 XAML 把终端选择、优化器下拉与进度反馈全部绑到 ViewModel,而不是写死在后台代码里。窗口固定尺寸 1200×500,MinHeight 与 MinWidth 同样是 500 和 1200,意味着布局不允许被拖到更小导致控件挤压。 ComboBox 的 ItemsSource 指向 Terminals 与 Optimisers 两个集合,SelectedIndex 分别绑 SelectedTerminalIndex 和 SelectedOptimiserIndex;IsEnabled 统一受 EnableMainTogles 控制,只要主开关没开,下拉和按钮都点不动。 底部 Grid 用两列布局,左列 Label 显示 Status 文本,右列 ProgressBar 绑 Progress 且 Minimum=0、Maximum=100,优化跑起来时进度条按整数百分比走。Window.DataContext 直接挂 vm:AutoOptimiserVM,所以所有绑定路径都不用在后置代码里手动 Assign。 开 MT5 外接这个优化器时,先确认 clr-namespace 映射:vm 指向 View_Model、v 指向 View,否则 XAML 解析会直接报找不到类型。外汇与贵金属自动化优化涉及实盘参数,回测过拟合概率偏高,任何信号都只是概率倾向,实盘前务必在小资金验证。
<Window x:Class="Metatrader_Auto_Optimiser.AutoOptimiser"
xmlns="http:class=class="str">"cmt">//schemas.microsoft.com/winfx/class="num">2006/xaml/presentation"
xmlns:x="http:class=class="str">"cmt">//schemas.microsoft.com/winfx/class="num">2006/xaml"
xmlns:d="http:class=class="str">"cmt">//schemas.microsoft.com/expression/blend/class="num">2008"
xmlns:mc="http:class=class="str">"cmt">//schemas.openxmlformats.org/markup-compatibility/class="num">2006"
xmlns:vm="clr-class="kw">namespace:Metatrader_Auto_Optimiser.View_Model"
xmlns:v="clr-class="kw">namespace:Metatrader_Auto_Optimiser.View"
mc:Ignorable="d"
Title="Auto Optimiser" Height="class="num">500" Width="class="num">1200"
MinHeight="class="num">500" MinWidth="class="num">1200">
<Window.DataContext>
<vm:AutoOptimiserVM/>
</Window.DataContext>
</Window>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="class="num">100"/>
</Grid.ColumnDefinitions>
<WrapPanel HorizontalAlignment="Left" VerticalAlignment="Bottom">
<Label Content="Select Optimiser:"/>
<ComboBox Width="class="num">150"
IsEnabled="{Binding EnableMainTogles, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Optimisers}"
SelectedIndex="{Binding SelectedOptimiserIndex}"/>
<Button Content="GUI"/>
</WrapPanel>
</Grid>优化器 GUI 的绑定与结果列
这段 XAML 把优化器的控制面板和结果列表直接绑到了 ViewModel。注意 IsEnabled 几乎都挂在 EnableMainTogles 上,意味着主开关一关,文本框、下拉框、按钮全部灰掉,不会误触发后台任务。
Directory prefix 文本框绑 DirPrefix,Asset name 绑 AssetName,配合 UpdateSetFile 命令可以把当前配置写回 *.set 文件;ComboBox 的 SelectedItem 走 FileWritingMode,决定落盘方式。
结果区用 ListView 绑 AllOptimisations,双击行触发 StartTestReport。列定义里暴露了 10 个字段:从 Date From/Till 的区间,到 Payoff、Profit Factor(原文拼成 pactor)、Average Profit Factor、Recovery Factor、Average Recovery Factor,再到 PL 和 DD。回测时盯这几列,能快速筛出回撤与收益配比倾向更稳的参数组。
外汇与贵金属品种波动大、杠杆高,这类优化结果仅代表历史样本表现,实盘存在显著失效可能。
<Grid>
<WrapPanel>
<Button Content="Optimiser GUI"
Command="{Binding ShowOptimiserGUI}"
IsEnabled="{Binding EnableMainTogles, UpdateSourceTrigger=PropertyChanged}"/>
<Label Content="Directory prefix:"/>
<TextBox Width="class="num">150"
IsEnabled="{Binding EnableMainTogles, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding DirPrefix}"/>
<ComboBox Width="class="num">100"
Margin="class="num">2,class="num">0,class="num">0,class="num">0"
SelectedIndex="class="num">0"
ItemsSource="{Binding FileFillingType}"
IsEnabled="{Binding EnableMainTogles, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding FileWritingMode}"/>
<Label Content="Asset name:"/>
<TextBox Width="class="num">100"
IsEnabled="{Binding EnableMainTogles, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding AssetName}"/>
<Button Content="Update(*.set) file"
Margin="class="num">2,class="num">0,class="num">0,class="num">0"
IsEnabled="{Binding EnableMainTogles}"
Command="{Binding UpdateSetFile}"/>
</WrapPanel>
<Button Content="Start/Stop"
Grid.Column="class="num">2"
Margin="class="num">2"
Command="{Binding StartStopOptimisation}"/>
</Grid>
<ListView ItemsSource="{Binding AllOptimisations}"
SelectedIndex="{Binding SelecterReportItem}"
v:ListViewExtention.DoubleClickCommand="{Binding StartTestReport}">
<ListView.View>
<GridView>
<GridViewColumn Header="Date From" DisplayMemberBinding="{Binding From}"/>
<GridViewColumn Header="Date Till" DisplayMemberBinding="{Binding Till}"/>
<GridViewColumn Header="Sort by" DisplayMemberBinding="{Binding SortBy}"/>
<GridViewColumn Header="Payoff" DisplayMemberBinding="{Binding Payoff}"/>
<GridViewColumn Header="Profit pactor" DisplayMemberBinding="{Binding ProfitFactor}"/>
<GridViewColumn Header="Average Profit Factor" DisplayMemberBinding="{Binding AverageProfitFactor}"/>
<GridViewColumn Header="Recovery factor" DisplayMemberBinding="{Binding RecoveryFactor}"/>
<GridViewColumn Header="Average Recovery Factor" DisplayMemberBinding="{Binding AverageRecoveryFactor}"/>
<GridViewColumn Header="PL" DisplayMemberBinding="{Binding PL}"/>
<GridViewColumn Header="DD" DisplayMemberBinding="{Binding DD}"/>
</GridView>
</ListView.View>
</ListView>