交易策略的色彩优化·进阶篇
🎨

交易策略的色彩优化·进阶篇

(2/3)·盈利、回撤、恢复系数三个数看花眼?把它们混成颜色,优化结果秒变色块地图

案例拆解新手友好 第 2/3 篇

接上篇,我们继续深挖怎么把优化报告里成堆的数字变成能一眼扫完的视觉信号。多数人面对几百组参数组合,仍靠手动排序利润和回撤,大脑很快过载漏掉边缘优质解。本文用 RGB 编码做实验,看颜色到底能不能当筛选器。

把优化结果染成一张可读的表

在 ColorOptimization.mqh 里,TableContent() 负责吐出带颜色指示的 HTML 报表。核心思路不复杂:单元格背景靠 style='background-color' 控制,而优化器给出的颜色分量原本落在 0~1,乘 255 就能映射到网页标准的 1~255 区间。 为了让表格不止「好看」还「有用」,颜色指示列的上格会写明对应哪组优化参数,参数名单元格也染上相应颜色。这样扫一眼色块就能定位某次 Pass 的调参倾向,而不是翻数字翻到眼酸。 函数开头先抓三个规模量:size 是优化 Pass 总数,pc 是参数个数,nc 是全局数组 co_names 的长度。HTML 字符串 s 从 <table> 起步,先写 <tr> 和「Pass」表头,再循环 nc 次补颜色列头——这里 HStyle() 决定是否给某个颜色列头加背景。 随后进入逐行构造:每个 Pass 先由 ComponentsToRGB() 把三个 0~1 分量算成 r/g/b 整数,测试结果、利润、期望盈利、盈利因子等单元格依次拼接;颜色单元则靠 RGBToStr() 转成 '#rrggbb' 字符串,同时写进 title 和 background-color。外汇与贵金属优化本身高风险,这类色表只是帮你看清参数分布,不预示任何实盘收益。 下面这段是 TableContent() 的主体骨架,注意循环里 r/g/b 每次重算、颜色格用 &nbsp; 撑开宽度:

MQL5 / C++
<span class="keyword">class="type">class="kw">string</span> TableContent(SOptimization2 &amp; aOpt,<span class="keyword">class="type">int</span> i1,<span class="keyword">class="type">int</span> i2,<span class="keyword">class="type">int</span> i3){
&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">class="type">int</span> size=<span class="functions">ArraySize</span>(aOpt.Pass);
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">class="type">int</span> pc=<span class="functions">ArraySize</span>(aOpt.ParameterName);
&nbsp;&nbsp; <span class="keyword">class="type">int</span> nc=<span class="functions">ArraySize</span>(co_names);
&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">string</span> s=<span class="class="type">class="kw">string">&quot;&lt;table&gt;&quot;</span>;
&nbsp;&nbsp;
&nbsp;&nbsp; s=s+<span class="class="type">class="kw">string">&quot;&lt;tr&gt;&quot;</span>;
&nbsp;&nbsp; s=s+<span class="class="type">class="kw">string">&quot;&lt;th&gt;Pass&lt;/td&gt;&quot;</span>;
&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">for</span>(<span class="keyword">class="type">int</span> i=<span class="number">class="num">0</span>;i&lt;nc;i++){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s=s+<span class="class="type">class="kw">string">&quot;&lt;th&quot;</span>+HStyle(i,i1,i2,i3)+<span class="class="type">class="kw">string">&quot;&gt;&quot;</span>+co_names[i]+<span class="class="type">class="kw">string">&quot;&lt;/th&gt;&quot;</span>;&nbsp;&nbsp;
&nbsp;&nbsp; }
&nbsp;&nbsp;
&nbsp;&nbsp; s=s+<span class="class="type">class="kw">string">&quot;&lt;th&gt;&quot;</span>+ColorCollHeader(i1,i2,i3)+<span class="class="type">class="kw">string">&quot;&lt;/th&gt;&quot;</span>;&nbsp;&nbsp;
&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">for</span>(<span class="keyword">class="type">int</span> j=<span class="number">class="num">0</span>;j&lt;pc;j++){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s=s+<span class="class="type">class="kw">string">&quot;&lt;th&gt;&quot;</span>+aOpt.ParameterName[j]+<span class="class="type">class="kw">string">&quot;&lt;/th&gt;&quot;</span>;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp; }
&nbsp;&nbsp; s=s+<span class="class="type">class="kw">string">&quot;&lt;/tr&gt;&quot;</span>;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">class="type">int</span> r,g,b;
&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">for</span>(<span class="keyword">class="type">int</span> i=<span class="number">class="num">0</span>;i&lt;size;i++){&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ComponentsToRGB(aOpt.Pass[i].ColorComponent[<span class="number">class="num">0</span>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;aOpt.Pass[i].ColorComponent[<span class="number">class="num">1</span>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;aOpt.Pass[i].ColorComponent[<span class="number">class="num">2</span>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r,g,b);
&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s=s+<span class="class="type">class="kw">string">&quot;&lt;tr&gt;&quot;</span>;
&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s=s+<span class="class="type">class="kw">string">&quot;&lt;td&gt;&quot;</span>+aOpt.Pass[i].Pass+<span class="class="type">class="kw">string">&quot;&lt;/td&gt;&quot;</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s=s+<span class="class="type">class="kw">string">&quot;&lt;td&gt;&quot;</span>+aOpt.Pass[i].Result+<span class="class="type">class="kw">string">&quot;&lt;/td&gt;&quot;</span>&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s=s+<span class="class="type">class="kw">string">&quot;&lt;td&gt;&quot;</span>+aOpt.Pass[i].Profit+<span class="class="type">class="kw">string">&quot;&lt;/td&gt;&quot;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s=s+<span class="class="type">class="kw">string">&quot;&lt;td&gt;&quot;</span>+aOpt.Pass[i].ExpectedPayoff+<span class="class="type">class="kw">string">&quot;&lt;/td&gt;&quot;</span>&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s=s+<span class="class="type">class="kw">string">&quot;&lt;td&gt;&quot;</span>+aOpt.Pass[i].ProfitFactor+<span class="class="type">class="kw">string">&quot;&lt;/td&gt;&quot;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s=s+<span class="class="type">class="kw">string">&quot;&lt;td&gt;&quot;</span>+aOpt.Pass[i].RecoveryFactor+<span class="class="type">class="kw">string">&quot;&lt;/td&gt;&quot;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s=s+<span class="class="type">class="kw">string">&quot;&lt;td&gt;&quot;</span>+aOpt.Pass[i].SharpeRatio+<span class="class="type">class="kw">string">&quot;&lt;/td&gt;&quot;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s=s+<span class="class="type">class="kw">string">&quot;&lt;td&gt;&quot;</span>+aOpt.Pass[i].Custom+<span class="class="type">class="kw">string">&quot;&lt;/td&gt;&quot;</span>&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s=s+<span class="class="type">class="kw">string">&quot;&lt;td&gt;&quot;</span>+aOpt.Pass[i].EquityDD_perc+<span class="class="type">class="kw">string">&quot;&lt;/td&gt;&quot;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s=s+<span class="class="type">class="kw">string">&quot;&lt;td&gt;&quot;</span>+aOpt.Pass[i].Trades+<span class="class="type">class="kw">string">&quot;&lt;/td&gt;&quot;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">string</span> cs=RGBToStr(r,g,b);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s=s+<span class="class="type">class="kw">string">&quot;&lt;td title=&class="macro">#x27;&quot;</span>+cs+<span class="class="type">class="kw">string">&quot;&class="macro">#x27; style=&class="macro">#x27;background-class="type">class="kw">color: &quot;</span>+cs+<span class="class="type">class="kw">string">&quot;&class="macro">#x27;&gt;&amp;nbsp&lt;/td&gt;&quot;</span>;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span>(<span class="keyword">class="type">int</span> j=<span class="number">class="num">0</span>;j&lt;pc;j++){

◍ 把优化结果染成红绿蓝三维热力图

做参数优化可视化时,单纯把 Pass 结果排成表格可读性很差。这段逻辑把每个优化 Pass 的三个评价指标映射成 RGB 分量,用颜色深浅直接暴露哪组参数在哪些维度占优。 核心在 ComponentsToRGB:传入三个 0~1 的归一化指标 c1、c2、c3,各自乘 255 强转 int 得到 r、g、b。比如 c1=0.85 时 r 约为 217,颜色偏红。 HStyle 按列索引 i1/i2/i3 给表头染红绿蓝底,ColorCollHeader 则把这三列名拼成「XXX-R, XXX-G, XXX-B」写进最后一格表头,读者一眼就知道红绿蓝分别对应哪个优化指标。 co_names 定义了 10 个常见列:Result、Profit、Expected Payoff、Profit Factor、Recovery Factor、Sharpe Ratio、Custom、Equity DD、Trades,加上 Pass 列共 11 列结构。外汇与贵金属回测高风险,颜色仅辅助筛选,不代表实盘概率。

MQL5 / C++
class="type">int size=ArraySize(aOpt.Pass);
class="type">int pc=ArraySize(aOpt.ParameterName);
class="type">int nc=ArraySize(co_names);
class="type">class="kw">string co_names[]={"Result","Profit","Expected Payoff",
                  "Profit Factor","Recovery Factor",
                  "Sharpe Ratio","Custom","Equity DD","Trades"};
class="type">class="kw">string s="<table>";
s=s+"<tr>";
s=s+"<th>Pass</th>";
for(class="type">int i=class="num">0;i<nc;i++){
   s=s+"<th"+HStyle(i,i1,i2,i3)+">"+co_names[i]+"</th>";  
}
class="type">class="kw">string HStyle(class="type">int i,class="type">int i1,class="type">int i2,class="type">int i3){
   if(i==i1)class="kw">return(" style=&class="macro">#x27;background-class="type">class="kw">color: rgb(class="num">255,class="num">0,class="num">0);&class="macro">#x27;");
   if(i==i2)class="kw">return(" style=&class="macro">#x27;background-class="type">class="kw">color: rgb(class="num">0,class="num">255,class="num">0);&class="macro">#x27;");
   if(i==i3)class="kw">return(" style=&class="macro">#x27;background-class="type">class="kw">color: rgb(class="num">0,class="num">0,class="num">255);&class="macro">#x27;");
   class="kw">return("");
}
s=s+"<th>"+ColorCollHeader(i1,i2,i3)+"</th>";
class="type">class="kw">string ColorCollHeader(class="type">int i1,class="type">int i2,class="type">int i3){
   class="kw">return(co_names[i1]+"-R,<br>"+co_names[i2]+"-G,<br>"+co_names[i3]+"-B");
}
for(class="type">int j=class="num">0;j<pc;j++){
   s=s+"<th>"+aOpt.ParameterName[j]+"</th>";        
}
s=s+"</tr>";    
ComponentsToRGB(aOpt.Pass[i].ColorComponent[class="num">0],
                aOpt.Pass[i].ColorComponent[class="num">1],
                aOpt.Pass[i].ColorComponent[class="num">2],
                r,g,b);
class="type">void ComponentsToRGB(class="type">class="kw">double c1,class="type">class="kw">double c2,class="type">class="kw">double c3,class="type">int & r,class="type">int & g,class="type">int & b){
   r=(class="type">int)(c1*class="num">255.0);
   g=(class="type">int)(c2*class="num">255.0);
   b=(class="type">int)(c3*class="num">255.0);
}
s=s+"<tr>";

s=s+"<td>"+aOpt.Pass[i].Pass+"</td>";
s=s+"<td>"+aOpt.Pass[i].Result+"</td>";  
s=s+"<td>"+aOpt.Pass[i].Profit+"</td>";        
s=s+"<td>"+aOpt.Pass[i].ExpectedPayoff+"</td>";  
s=s+"<td>"+aOpt.Pass[i].ProfitFactor+"</td>";          

「把优化结果拼成带色块的 HTML 表格」

这段逻辑干的事很直接:把 MT5 策略测试器里每一组 pass 的关键指标,逐个塞进 <td> 拼成 HTML 行。RecoveryFactor、SharpeRatio、Custom、EquityDD_perc、Trades 这几个字段依次落表,方便你在浏览器或邮件里一眼扫完多组参数表现。 颜色块那行用了 RGBToStr 把 r/g/b 转成 css 的 rgb() 字符串,再写进 td 的 style 背景色和 title 属性。也就是说,某组回测的盈亏特征可以用底色直观区分,比如回撤大的偏红、夏普高的偏绿,具体映射看你前面怎么算 r/g/b。 末尾的 for 循环把该 pass 的 Parameters[j] 逐个补到表格后面,列数由 pc(参数个数)决定;最后补 </tr></table> 收口并返回整段 s。你在 EA 里接这段时,注意 pc 必须和 aOpt.Pass[i].Parameters 数组长度一致,否则会漏列或越界报错。外汇与贵金属回测天然高杠杆、点差跳空频繁,这组色块表只能辅助筛参,实盘前仍要在 MT5 用真实点差重跑验证。

MQL5 / C++
s=s+"<span class=\"tag\">&lt;<span class=\"keyword\">td</span>&gt;</span>"+aOpt.Pass[i].RecoveryFactor+"<span class=\"tag\">&lt;/<span class=\"keyword\">td</span>&gt;</span>";

s=s+"<span class=\"tag\">&lt;<span class=\"keyword\">td</span>&gt;</span>"+aOpt.Pass[i].SharpeRatio+"<span class=\"tag\">&lt;/<span class=\"keyword\">td</span>&gt;</span>";

s=s+"<span class=\"tag\">&lt;<span class=\"keyword\">td</span>&gt;</span>"+aOpt.Pass[i].Custom+"<span class=\"tag\">&lt;/<span class=\"keyword\">td</span>&gt;</span>";

s=s+"<span class=\"tag\">&lt;<span class=\"keyword\">td</span>&gt;</span>"+aOpt.Pass[i].EquityDD_perc+"<span class=\"tag\">&lt;/<span class=\"keyword\">td</span>&gt;</span>";

s=s+"<span class=\"tag\">&lt;<span class=\"keyword\">td</span>&gt;</span>"+aOpt.Pass[i].Trades+"<span class=\"tag\">&lt;/<span class=\"keyword\">td</span>&gt;</span>";

class="type">class="kw">string cs=RGBToStr(r,g,b);
s=s+"<span class=\"tag\">&lt;<span class=\"keyword\">td</span><span class=\"attribute\"> title=<span class=\"value\">&class="macro">#x27;"+cs+"&class="macro">#x27;</span></span><span class=\"attribute\"> style=<span class=\"value\">&class="macro">#x27;background-class="type">class="kw">color: "+cs+"&class="macro">#x27;</span></span>&gt;</span>&amp;nbsp<span class=\"tag\">&lt;/<span class=\"keyword\">td</span>&gt;</span>";

class="type">class="kw">string RGBToStr(class="type">int r,class="type">int g,class="type">int b){
   class="kw">return("rgb("+(class="type">class="kw">string)r+","+(class="type">class="kw">string)g+","+(class="type">class="kw">string)b+")");
}

for(class="type">int j=class="num">0;j<pc;j++){
   s=s+"<td>"+aOpt.Pass[i].Parameters[j]+"</td>";
}
s=s+"</tr>";
s=s+"</table>";
class="kw">return(s);

双参数优化怎么摊成一张热力平面

多参数优化最怕肉眼比对,把两个参数摊成二维平面就能一眼看出组合优劣。X 轴放一个参数、Y 轴放另一个,单元格用颜色梯度表示这组组合在排序因子下的表现,最差偏左、最好偏右,排序因子取抽象颜色分量的算术均值。 生成平面的核心在 Color2DPlanes() 与 Color2DPlane()。前者遍历所有参数对(y 从 0 到参数数、x 从 y+1 开始),后者负责单张表的 HTML 拼接。实测梯度在 Opera、Chrome、Yandex、Edge 都能正常渲染,老 IE 不在验证范围内。 唯一值去重不能省。优化报告里同一参数可能重复出现,先排序再把不重复值挤到数组头部,xc、yc 分别记两个轴的独立取值数,否则表格会多出一堆空列。 工具提示用 HTML 的 title 属性挂上,鼠标悬停能看到该格对应最差/最好过程的全部数据与 RGB 分量,比单纯看颜色更利于复盘。外汇与贵金属策略做这类优化时波动剧烈,历史平面好看不代表实盘概率占优,高风险始终在。

MQL5 / C++
<span class="keyword">class="type">class="kw">string</span> Color2DPlanes(SOptimization2 &amp; aOpt){
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">string</span> s=<span class="class="type">class="kw">string">""</span>;
&nbsp;&nbsp; <span class="keyword">class="type">int</span> pc=<span class="functions">ArraySize</span>(aOpt.ParameterName);
&nbsp;&nbsp; <span class="keyword">for</span>(<span class="keyword">class="type">int</span> y=<span class="number">class="num">0</span>;y&lt;pc;y++){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span>(<span class="keyword">class="type">int</span> x=y+<span class="number">class="num">1</span>;x&lt;pc;x++){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s=s+Color2DPlane(aOpt,x,y);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;
&nbsp;&nbsp; }
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(s);
}
<span class="keyword">class="type">class="kw">string</span> Color2DPlane(SOptimization2 &amp; aOpt,<span class="keyword">class="type">int</span> xi,<span class="keyword">class="type">int</span> yi){
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> xa[];
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> ya[];
&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">class="type">int</span> cnt=<span class="functions">ArraySize</span>(aOpt.Pass);
&nbsp;&nbsp; <span class="functions">ArrayResize</span>(xa,cnt);
&nbsp;&nbsp; <span class="functions">ArrayResize</span>(ya,cnt);
&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">for</span>(<span class="keyword">class="type">int</span> i=<span class="number">class="num">0</span>;i&lt;cnt;i++){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xa[i]=aOpt.Pass[i].dParameters[xi];
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ya[i]=aOpt.Pass[i].dParameters[yi];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp; }
&nbsp;&nbsp;
&nbsp;&nbsp; <span class="functions">ArraySort</span>(xa);
&nbsp;&nbsp; <span class="functions">ArraySort</span>(ya);
&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">class="type">int</span> xc=<span class="number">class="num">1</span>;
&nbsp;&nbsp; <span class="keyword">class="type">int</span> yc=<span class="number">class="num">1</span>;
&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">for</span>(<span class="keyword">class="type">int</span> i=<span class="number">class="num">1</span>;i&lt;cnt;i++){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span>(xa[i]!=xa[i-<span class="number">class="num">1</span>]){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xa[xc]=xa[i];
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xc++;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span>(ya[i]!=ya[i-<span class="number">class="num">1</span>]){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ya[xc]=ya[i];
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yc++;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp; }&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">string</span> s=<span class="class="type">class="kw">string">"&lt;hr&gt;&lt;h3&gt;X - "</span>+aOpt.ParameterName[xi]+<span class="class="type">class="kw">string">", Y - "</span>+aOpt.ParameterName[yi]+<span class="class="type">class="kw">string">"&lt;/h3&gt;&lt;table&gt;"</span>;
&nbsp;&nbsp; s=s+<span class="class="type">class="kw">string">"&lt;tr&gt;"</span>;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s=s+<span class="class="type">class="kw">string">"&lt;td&gt;&amp;nbsp;&lt;/td&gt;"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span>(<span class="keyword">class="type">int</span> x=<span class="number">class="num">0</span>;x&lt;xc;x++){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s=s+<span class="class="type">class="kw">string">"&lt;td&gt;"</span>+(<span class="keyword">class="type">class="kw">string</span>)xa[x]+<span class="class="type">class="kw">string">"&lt;/td&gt;"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp; s=s+<span class="class="type">class="kw">string">"&lt;/tr&gt;"</span>;&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">for</span>(<span class="keyword">class="type">int</span> y=<span class="number">class="num">0</span>;y&lt;yc;y++){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s=s+<span class="class="type">class="kw">string">"&lt;tr&gt;"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s=s+<span class="class="type">class="kw">string">"&lt;td&gt;"</span>+(<span class="keyword">class="type">class="kw">string</span>)ya[y]+<span class="class="type">class="kw">string">"&lt;/td&gt;"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span>(<span class="keyword">class="type">int</span> x=<span class="number">class="num">0</span>;x&lt;xc;x++){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> mx=<span class="number">class="num">0</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> mn=<span class="macro">DBL_MAX</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="type">int</span> mxi=<span class="number">class="num">0</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="type">int</span> mni=<span class="number">class="num">0</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">for</span>(<span class="keyword">class="type">int</span> i=<span class="number">class="num">0</span>;i&lt;cnt;i++){

◍ 把最优与最差参数染色进表格

这段逻辑干的事很直接:在已筛选出的参数网格里,找出当前 (xa[x], ya[y]) 组合下 SortFactor 最大和最小的两次跑参记录,记下索引 mxi 与 mni。注意判断条件是 dParameters 两个维度同时命中,意味着你是在二维切片上比优劣,而不是全表排座次。 拿到极值索引后,代码用 ComponentsToRGB 把 Pass[mni] 和 Pass[mxi] 的三个 ColorComponent 拆成 RGB 整数。最小因子染成渐变左端,最大因子染成右端,最终写进 HTML 的 linear-gradient 背景——扫一眼颜色跨度,就能感知这组输入参数下策略表现的离散程度。 标题串 title 拼了两组 RGB 字符串和九项 factor,digits 数组写死为 {2,2,6,6,6,6,6,4,0},说明不同指标小数位差别很大,例如第 3~7 项保留 6 位、末项整数。悬浮提示里用 DoubleToString 按位截断,避免 MT5 默认双精度把表格撑爆。 末尾把拼好的 <td> 塞进行 s,循环结束补 </tr>,函数最终返回完整 <table> 字符串。外汇与贵金属回测中这种可视化容易暴露过拟合区,高杠杆品种须警惕参数脆弱性。

MQL5 / C++
if(aOpt.Pass[i].dParameters[yi]==ya[y] &&
   aOpt.Pass[i].dParameters[xi]==xa[x]
  ){
   if(aOpt.Pass[i].SortFactor>mx){
     mx=aOpt.Pass[i].SortFactor;
     mxi=i;
   }
   if(aOpt.Pass[i].SortFactor<mn){
     mn=aOpt.Pass[i].SortFactor;
     mni=i;
   }
  }
  }

  class="type">int mnr,mng,mnb;
  class="type">int mxr,mxg,mxb;

  ComponentsToRGB(aOpt.Pass[mni].ColorComponent[class="num">0],
          aOpt.Pass[mni].ColorComponent[class="num">1],
          aOpt.Pass[mni].ColorComponent[class="num">2],
          mnr,mng,mnb);

  ComponentsToRGB(aOpt.Pass[mxi].ColorComponent[class="num">0],
          aOpt.Pass[mxi].ColorComponent[class="num">1],
          aOpt.Pass[mxi].ColorComponent[class="num">2],
          mxr,mxg,mxb);

  class="type">class="kw">string title=RGBToStr(mnr,mng,mnb)+"/"+RGBToStr(mxr,mxg,mxb)+"\n";

  class="type">int digits[]={class="num">2,class="num">2,class="num">6,class="num">6,class="num">6,class="num">6,class="num">6,class="num">4,class="num">0};
  for(class="type">int k=class="num">0;k<ArraySize(co_names);k++){
    title=title+co_names[k]+": "+DoubleToString(aOpt.Pass[mni].factor[k],digits[k])+
    "/"+DoubleToString(aOpt.Pass[mxi].factor[k],digits[k])+"\n";
  }
  s=s+"<td title=&class="macro">#x27;"+title+"&class="macro">#x27; style=&class="macro">#x27;background: linear-gradient(to right, rgb("+
  (class="type">class="kw">string)mnr+","+(class="type">class="kw">string)mng+","+(class="type">class="kw">string)mnb+"), rgb("+
  (class="type">class="kw">string)mxr+","+(class="type">class="kw">string)mxg+","+(class="type">class="kw">string)mxb+"));&class="macro">#x27;>&nbsp;"+
  (class="type">class="kw">string)mni+"-"+(class="type">class="kw">string)mxi+"</td>";
   }
   s=s+"</tr>";
  }

  s=s+"</table>";
  class="kw">return(s);
}
class="type">class="kw">double xa[];
class="type">class="kw">double ya[];

「把优化结果铺成可悬停的热力矩阵」

做参数优化后处理时,先把通跑结果集压成 X/Y 两轴唯一值,再交叉找每个格子里的最差与最优 pass,这一步直接决定你后面看热力图能不能秒懂。下面这段逻辑就是干这个:去重后 xc、yc 分别记唯一 X、Y 参数个数,嵌套循环里用 SortFactor 比大小抓 mni/mxi 下标。 原文用 DBL_MAX 做极小值初值,遍历 cnt 次(cnt=ArraySize(aOpt.Pass))找同坐标格子里的极值,再调 ComponentsToRGB 把两个 pass 的颜色分量转成 RGB,拼进 td 的 linear-gradient 背景。悬停 title 里塞了 digits[]={2,2,6,6,6,6,6,4,0} 控制的九项因子精度,最差/最优用斜杠隔开。 你开 MT5 跑完 EA 优化,把这段代码接在框架末尾,HTML 报告里每个单元格背景就是红劣绿优的渐变,鼠标移上去能看到具体因子对比。外汇与贵金属品种参数面脆弱,过拟合概率高,这种矩阵只帮你缩窄观察范围,不等于信号可靠。

MQL5 / C++
class="type">int cnt=ArraySize(aOpt.Pass);
ArrayResize(xa,cnt);
ArrayResize(ya,cnt);
for(class="type">int i=class="num">0;i<cnt;i++){
   xa[i]=aOpt.Pass[i].dParameters[xi];
   ya[i]=aOpt.Pass[i].dParameters[yi];       
}
ArraySort(xa);
ArraySort(ya);
class="type">int xc=class="num">1;
class="type">int yc=class="num">1;
for(class="type">int i=class="num">1;i<cnt;i++){
   if(xa[i]!=xa[i-class="num">1]){
      xa[xc]=xa[i];
      xc++;
   }
   if(ya[i]!=ya[i-class="num">1]){
      ya[xc]=ya[i];
      yc++;
   }
}  
class="type">class="kw">string s="<hr><h3>X - "+aOpt.ParameterName[xi]+", Y - "+aOpt.ParameterName[yi]+"</h3><table>";
s=s+"<tr>";  
   s=s+"<td>&nbsp;</td>";
   for(class="type">int x=class="num">0;x<xc;x++){
      s=s+"<td>"+(class="type">class="kw">string)xa[x]+"</td>";
   }
s=s+"</tr>";
for(class="type">int y=class="num">0;y<yc;y++){
s=s+"<tr>";
      
s=s+"<td>"+(class="type">class="kw">string)ya[y]+"</td>";
for(class="type">int x=class="num">0;x<xc;x++){
class="type">class="kw">double mx=class="num">0;
class="type">class="kw">double mn=DBL_MAX;
class="type">int mxi=class="num">0;
class="type">int mni=class="num">0;
for(class="type">int i=class="num">0;i<cnt;i++){
   if(aOpt.Pass[i].dParameters[yi]==ya[y] &&
      aOpt.Pass[i].dParameters[xi]==xa[x]
   ){
      if(aOpt.Pass[i].SortFactor>mx){
         mx=aOpt.Pass[i].SortFactor;
         mxi=i;
      }
      if(aOpt.Pass[i].SortFactor<mn){
         mn=aOpt.Pass[i].SortFactor;
         mni=i;
      }
   }
}
ComponentsToRGB(aOpt.Pass[mni].ColorComponent[class="num">0],
               aOpt.Pass[mni].ColorComponent[class="num">1],
               aOpt.Pass[mni].ColorComponent[class="num">2],
               mnr,mng,mnb);
               
ComponentsToRGB(aOpt.Pass[mxi].ColorComponent[class="num">0],
               aOpt.Pass[mxi].ColorComponent[class="num">1],
               aOpt.Pass[mxi].ColorComponent[class="num">2],
               mxr,mxg,mxb);  
class="type">class="kw">string title=RGBToStr(mnr,mng,mnb)+"/"+RGBToStr(mxr,mxg,mxb)+"\n";
              
class="type">int digits[]={class="num">2,class="num">2,class="num">6,class="num">6,class="num">6,class="num">6,class="num">6,class="num">4,class="num">0};
for(class="type">int k=class="num">0;k<ArraySize(co_names);k++){
   title=title+co_names[k]+": "+DoubleToString(aOpt.Pass[mni].factor[k],digits[k])+
   "/"+DoubleToString(aOpt.Pass[mxi].factor[k],digits[k])+"\n";
}
s=s+"<td title=&class="macro">#x27;"+title+"&class="macro">#x27; style=&class="macro">#x27;background: linear-gradient(to right, rgb("+
(class="type">class="kw">string)mnr+","+(class="type">class="kw">string)mng+","+(class="type">class="kw">string)mnb+"), rgb("+
让小布替你跑这套
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到优化结果的色彩分布,不必自己写 HTMLReport 解析。

常见问题

本篇示例将 Profitability、Drawdown、RecoveryFactor 分别映射到红绿蓝,也可改挂其他双精度参数,结构支持继承扩展。
RGB 直接对应三个独立优化变量,映射直观且易反向解析;HSV 更偏感知均匀,但回推原值稍麻烦,看需求取舍。
可以,小布的品种页支持读取终端 Files 下的报告,自动生成色块平面,省去自己调用 OptimizerXMLReportToStruct。
表格保留精确数值便于复核,平面把参数空间铺成二维坐标加颜色,更适合快速圈出密集优质区。
RGB 只装三个量,多余维度需降维或切到多图联动,否则信息会互相覆盖看不出主次。