交易策略的色彩优化·综合运用
🎨

交易策略的色彩优化·综合运用

(3/3)· 当盈利、回撤与恢复系数挤进同一张色卡,参数筛选还需逐行翻表吗

案例拆解 第 3/3 篇
多数人优化完只在表格里按 Profit 排序挑最高值,却忽略了回撤和采收系数早已失控。把三维参数压成一行数字看,容易漏掉组合之间的结构性冲突。用颜色编码不是装饰,是给肉眼一个快速排雷的通道。

◍ 把优化结果拼成可看的颜色表

这段逻辑负责把多因子优化后的数值直接拼成 HTML 表格片段,方便在 MT5 报告页里直观比对。它把 mxr、mxg、mxb 三个极值转成字符串塞进单元格,再补上 mni 到 mxi 的区间标识,最后用 return 把整段 s 交出去。 实际在 MT5 策略测试器里跑完优化,你可以把 report 这个字符串(由 HTMLStart 拼头、TableContent 填表、Color2DPlanes 画图、HTMLEnd 收尾)写进自定义报告,打开就是带配色的二维平面。外汇与贵金属品种参数空间大,这种拼表方式能帮你快速锁定因子组合的高概率区,但优化结果仅代表历史样本,实盘仍属高风险。

MQL5 / C++
(class="type">class="kw">string)mxr+","+(class="type">class="kw">string)mxg+","+(class="type">class="kw">string)mxb+"));&class="macro">#x27;</span></span>&gt;</span>&amp;nbsp;"+
(class="type">class="kw">string)mni+"-"+(class="type">class="kw">string)mxi+"<span class="tag">&lt;/<span class="keyword">td</span>&gt;</span>";
s=s+<span class="class="type">class="kw">string">"&lt;/tr&gt;"</span>;
s=s+<span class="class="type">class="kw">string">"&lt;table&gt;"</span>;&nbsp;&nbsp;
<span class="keyword">class="kw">return</span>(s);
<span class="keyword">class="type">class="kw">string</span> report=HTMLStart(<span class="class="type">class="kw">string">"Color Optimization"</span>,<span class="class="type">class="kw">string">"style2.css"</span>)+
TableContent(opt,Factor1,Factor2,Factor3)+
Color2DPlanes(opt)+HTMLEnd();

给优化器挂上 CMY 颜色模型

原色彩优化模块的结构分得比较开,想换一套配色逻辑不用推倒重来。照着 RGB 的架子复制出一份 ColorOptimization2 的 .mqh 与 .mq5,就能在 MT5 里直接加一套 CMY 模型做对照。 先在头文件里落两个宏和全局变量:MODEL_RGB 记 0、MODEL_CMY 记 1,再用一个 int 型 co_ColorModel 承接运行时的选择。配合 input 枚举,用户在脚本属性窗里点一下就能切模型,OnStart 开头那行 co_ColorModel=ColorModel 负责把界面值灌进去。 核心改动在 ComponentsToRGB()。CMY 的三个分量落在 0~1 区间,跟 RGB 的线性放大不同,得用 (1-C/M/Y)*255 反推。下面这段代码把分支写清楚了,CMY 分支直接甩给 CMYtoRGB 处理。 辅助显示也得跟着改:HStyle() 里 CMY 的表头底色换成青、品红、黄对应的补色(0,255,255 / 255,0,255 / 255,255,0);ColorCollHeader() 把列名后缀从 -R/-G/-B 换成 -C/-M/-Y。主表与颜色平面的 title 属性顺手改掉,跑出来的报告就不串色了。 实测切到 CMY 后,最优值倾向以黑色呈现,其余色块分布和 RGB 平面明显不一样(原图 4、5 可对照)。外汇与贵金属回测可视化高风险,配色只是辅助判读,别拿颜色当信号本身。

MQL5 / C++
class="macro">#define MODEL_RGB class="num">0
class="macro">#define MODEL_CMY class="num">1
class="type">int co_ColorModel;
enum EColorModel{
   RGB=MODEL_RGB,
   CMY=MODEL_CMY
};
class="kw">input EColorModel        ColorModel   =   RGB;
co_ColorModel=ColorModel;
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){
   if(co_ColorModel==MODEL_RGB){
      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);
   }
   else if(co_ColorModel==MODEL_CMY){
      CMYtoRGB(c1,c2,c3,r,g,b);
   }
}
class="type">void CMYtoRGB(class="type">class="kw">double C,class="type">class="kw">double M,class="type">class="kw">double Y,class="type">int & R,class="type">int & G,class="type">int & B){
   R=(class="type">int)((class="num">1.0-C)*class="num">255.0);
   G=(class="type">int)((class="num">1.0-M)*class="num">255.0);
   B=(class="type">int)((class="num">1.0-Y)*class="num">255.0);
}
class="type">class="kw">string HStyle(class="type">int i,class="type">int i1,class="type">int i2,class="type">int i3){
   if(co_ColorModel==MODEL_RGB){
      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;");
   }
   else if(co_ColorModel==MODEL_CMY){
      if(i==i1)class="kw">return(" style=&class="macro">#x27;background-class="type">class="kw">color: rgb(class="num">0,class="num">255,class="num">255);&class="macro">#x27;");
      if(i==i2)class="kw">return(" style=&class="macro">#x27;background-class="type">class="kw">color: rgb(class="num">255,class="num">0,class="num">255);&class="macro">#x27;");
      if(i==i3)class="kw">return(" style=&class="macro">#x27;background-class="type">class="kw">color: rgb(class="num">255,class="num">255,class="num">0);&class="macro">#x27;");      
   }
   class="kw">return("");
}
class="type">class="kw">string ColorCollHeader(class="type">int i1,class="type">int i2,class="type">int i3){
   if(co_ColorModel==MODEL_RGB){
      class="kw">return(co_names[i1]+"-R,<br>"+co_names[i2]+"-G,<br>"+co_names[i3]+"-B");
   }
   else if(co_ColorModel==MODEL_CMY){
      class="kw">return(co_names[i1]+"-C,<br>"+co_names[i2]+"-M,<br>"+co_names[i3]+"-Y");   
   }
   class="kw">return "";
}
class="type">class="kw">string cs=RGBToStr(r,g,b);

「颜色模型切换时的表格单元拼接」

在把优化结果渲染成 HTML 表格时,背景色单元格的拼接逻辑依赖当前选择的颜色模型。RGB 模式下直接用 RGBToStr(r,g,b) 算出的 cs 作为背景色与 title;CMY 模式下则改用 CMYToStr 取各通道分量拼出可读文本,但背景色仍由 cs 决定。 下面这段拼接代码里,先拼了一个带 title 和 background-color 的 td,再按 co_ColorModel 分支决定 ts 内容:RGB 时 ts=cs,CMY 时 ts 来自 aOpt.Pass[i] 的三个 ColorComponent。注意同一段 s 拼接出现了两次几乎一样的 td 结构,第二次才真正带入分支后的 ts。 标题行也分模型处理:RGB 用最小/最大 RGB 值对拼成「min/max」加换行;CMY 则取 mni 与 mxi 索引对应通道的 CMY 分量拼串。若你在 MT5 里改了颜色模型却发现悬停提示不变,优先查 title 变量是否被后面空串覆盖——原文中就有一次 string title=""; 的重置。

MQL5 / C++
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 ts="",cs=RGBToStr(r,g,b);
if(co_ColorModel==MODEL_RGB){
   ts=cs;
}
else if(co_ColorModel==MODEL_CMY){
   ts=CMYToStr(aOpt.Pass[i].ColorComponent[class="num">0],
               aOpt.Pass[i].ColorComponent[class="num">1],
               aOpt.Pass[i].ColorComponent[class="num">2]);
}
s=s+"<span class="tag">&lt;<span class="keyword">td</span><span class="attribute"> title=<span class="value">&class="macro">#x27;"+ts+"&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 title=RGBToStr(mnr,mng,mnb)+"/"+RGBToStr(mxr,mxg,mxb)+"\n";
class="type">class="kw">string title="";
if(co_ColorModel==MODEL_RGB){
   title=RGBToStr(mnr,mng,mnb)+"/"+RGBToStr(mxr,mxg,mxb)+"\n";
}
else if(co_ColorModel==MODEL_CMY){
   title=CMYToStr(aOpt.Pass[mni].ColorComponent[class="num">0],
                 aOpt.Pass[mni].ColorComponent[class="num">1],
                 aOpt.Pass[mni].ColorComponent[class="num">2])+"/"+
         CMYToStr(aOpt.Pass[mxi].ColorComponent[class="num">0],
                  aOpt.Pass[mxi].ColorComponent[class="num">1],
                  aOpt.Pass[mxi].ColorComponent[class="num">2])+"\n";
}

◍ 从颜色分量反推参数贡献

优化测试里用 RGB 或 CMY 着色,本质是把参数组合映射成颜色。RGB 中三分量全 0 是黑、全满是白;单分量拉满得纯红/绿/蓝,两分量满值则得黄(红+绿)、青(绿+蓝)、洋红(红+蓝)。CMY 反过来,白减去分量值,全满反而是黑,类似颜料混多了发脏。 看图判贡献时,RGB 下偏红说明第一参数正向作用最强,黄色代表第一和第二参数共同贡献,绿色落点指向第三参数。CMY 里青色对应第一参数,蓝色是第一和第二,洋红是第二,红色是第二和第三,黄色第三、绿色第一和第三——两套模型只是色相翻转,解读逻辑一致。 实操时直接在 MT5 策略测试器的颜色图里瞄一眼主色调,就能粗略判断哪几个输入变量在驱动回测结果,不用先翻参数表。外汇与贵金属品种波动剧烈,这种视觉判读只辅助定位,不构成方向结论。

亮度指示与模糊逻辑的边界

颜色感知本质上是主观的,很难对某种配色方案在报表分析里的便利性下死结论。但至少亮度——即 RGB 分量越接近白色——能让你一眼读出三个参数的综合强弱,这比分别看三个数字省事。 当选择被自动化时(如本系列前文所述),排序表决策用的是三个值的算术平均。这其实已经踩进了模糊逻辑的门:最终值不靠非黑即白的判断,而是用连续量做折中。 更进一步的做法是用隶属度函数替代简单平均,但眼下缺的是实盘级回测。外汇与贵金属行情噪声大、高风险,这类视觉降维工具在 MT5 自定义指标里验证前,只能算概率层面的辅助,别当信号本身。

「把这条线请下神坛」

这套彩色优化报告附带了 9 个文件:HTMLReport.mqh、ColorOptimization.mqh、ColorOptimization.mq5、ColorOptimization2.mqh、ColorOptimization2.mq5、ReportOptimizer-555849.xml、ColorOptimization.htm、ColorOptimization2.htm 和 Style2.css,压缩包体积 64.67 KB。除脚本生成的 htm 报告外,其余需直接丢进 MT5 终端数据文件夹的 MQL5 目录,否则脚本调用会报找不到头文件。 从评论区实战反馈看,双参数优化用 RGB/CMY 着色能较快把参数集引向高绩效区,但超过两个参数后手动引导效率骤降,除非有超算支撑。外汇与贵金属市场杠杆高、滑点突发行情多,这类可视化仅是辅助定位,不等于策略盈利保证。 打开终端数据文件夹复制 MQL5 进去,跑一遍 ColorOptimization.mq5,看那张彩色地图和你自己的优化 xml 对不对得上,比反复读文更有用。

让小布替你把报告染上色
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到优化结果的色块映射,不必自己写 HTMLReport 解析。把重复劳动交给小布,你专注决策。

常见问题

本篇默认将 Profitability、Drawdown、Recovery Factor 映射为红绿蓝三通道,也可通过继承结构改用 Sharpe 或自定义参数,只要来源在 SPass 数组内即可。
可以,小布盯盘的品种页已集成报告解析与色块渲染,上传终端 MQL5/Files 下的报告文件就能看到分布,不用本地跑脚本。
倾向如此,但仅限同模型同参数轴;若某次自定义参数被换轴,色相接近也可能是不同维度叠加的错觉,需回查 Parameters 数组。
散点图在 Pass 数量上千时重叠严重,颜色把三变量压进单像素,适合快速扫视异常簇,属于概览层工具而非精确度量。
文末附件给出基于继承的改写示例,重写通道映射函数即可接入 HSV 或 Lab,注意终端对浮点色阶的截断精度。