交易策略的色彩优化·综合运用
(3/3)· 当盈利、回撤与恢复系数挤进同一张色卡,参数筛选还需逐行翻表吗
◍ 把优化结果拼成可看的颜色表
这段逻辑负责把多因子优化后的数值直接拼成 HTML 表格片段,方便在 MT5 报告页里直观比对。它把 mxr、mxg、mxb 三个极值转成字符串塞进单元格,再补上 mni 到 mxi 的区间标识,最后用 return 把整段 s 交出去。 实际在 MT5 策略测试器里跑完优化,你可以把 report 这个字符串(由 HTMLStart 拼头、TableContent 填表、Color2DPlanes 画图、HTMLEnd 收尾)写进自定义报告,打开就是带配色的二维平面。外汇与贵金属品种参数空间大,这种拼表方式能帮你快速锁定因子组合的高概率区,但优化结果仅代表历史样本,实盘仍属高风险。
(class="type">class="kw">string)mxr+","+(class="type">class="kw">string)mxg+","+(class="type">class="kw">string)mxb+"));&class="macro">#x27;</span></span>></span>&nbsp;"+ (class="type">class="kw">string)mni+"-"+(class="type">class="kw">string)mxi+"<span class="tag"></<span class="keyword">td</span>></span>"; s=s+<span class="class="type">class="kw">string">"</tr>"</span>; s=s+<span class="class="type">class="kw">string">"<table>"</span>; <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 可对照)。外汇与贵金属回测可视化高风险,配色只是辅助判读,别拿颜色当信号本身。
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=""; 的重置。
s=s+"<span class="tag"><<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>></span>&nbsp<span class="tag"></<span class="keyword">td</span>></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"><<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>></span>&nbsp<span class="tag"></<span class="keyword">td</span>></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 对不对得上,比反复读文更有用。