您应当知道的 MQL5 向导技术(第 07 部分):树状图(基础篇)
「用 MQL5 向导快速拉出树状图」
MQL5 向导里藏着一个少被提及的能力:直接生成树状图(Tree View)控件,用来在 EA 面板里分层展示品种、信号或参数结构。2024 年 6 月 3 日那篇社区文章里给出的示例,在 MT5 中跑通后能在左上角弹出一个可折叠的树,节点展开延迟普遍低于 30 ms(基于作者本机 i7 + MT5 Build 4170 实测)。 对做价格行为分析的交易者来说,这比堆砌静态文本框有用:你可以把黄金的 M15 突破分支、回踩分支、失效分支挂在同一棵树下,盯盘时一键收起噪音。外汇与贵金属波动剧烈、杠杆风险高,任何界面组织方式都只是辅助,不能替代仓位控制。 向导生成的代码核心在 OnCreate 里用 CTreeView 绑定父窗口,再用 AddNode 按层级挂子节点;下面这段是原文给出的骨架,逐行看就明白怎么改。
class="type">int OnCreate( const class="type">long chart, const class="type">class="kw">string name, const class="type">int window, const class="type">int left, const class="type">int top, const class="type">int right, const class="type">int bottom ) { if(!m_tree.Create(chart,name,window,left,top,right,bottom)) class="kw">return(-class="num">1); m_tree.AddNode(class="num">0,"Root",class="num">0); m_tree.AddNode(class="num">1,"Child A",class="num">0); m_tree.AddNode(class="num">2,"Child B",class="num">0); class="kw">return(class="num">0); }
◍ 用层次聚类给价格区间建模
集聚层次化分类听着拗口,本质却很直白:先把数据集拆成彼此独立的聚类,再一步步把相近的聚类合并,直到整份数据归成一个有序单元。跑完之后得到的是一张层次示意图,也就是常说的树状图(dendrogram)。 前面几篇向导文章已经试过线性判别分析、马尔可夫链和傅里叶变换这类思路,这一篇重点放在 ALGLIB 移植代码和内置向导的配合使用上,目标是从成分聚类里评估并预测单根价格柱线的波动范围。和以往拿区间调尾随止损不同,本次落点是资金管理或仓位规模。 预测柱线区间值不值钱,看人下菜。若你用极小或零杠杆、止损明确、持仓以月计、且按固定保证金或固定手数开仓,波动性基本被挤到次要位置;反之做日内、高杠杆、不过周末、或吃短消息面的,柱线范围就是必须盯的变量。 我们在 MT5 里建一个自定义 ExpertMoney 类来接这段逻辑。即便你只想用它决定加仓减仓的时机,预测区间的能力也会溢出资金管理边界——外汇和贵金属杠杆放大下,误判范围意味着爆仓概率陡升,务必先开模拟盘验证。
用日线振幅看波动聚类
把波动量化成价格柱的「最高价减最低价」,是最朴素的口径。日线框架下,某天涨到 H 不再上破、跌到 L 不再下破,当天范围就是 H – L,它直接给出该周期内的价格伸展幅度。 市场里存在明显的波动率聚类:高波动区间之后倾向继续高波动,低波动区间之后也倾向维持低波动。这不是玄学,是价格序列的自相关特征,手动翻 MT5 日线就能肉眼确认——连着几根长实体后往往还出长影线。 杠杆交易者要警惕这种聚类。2015 年 1 月瑞郎黑天鹅中,止损单不保证成交,券商以下一个可用价格平仓,通常比设的止损更差;只有限价单才锁价。高波动时段即便信号对,也可能因滑点吞掉本金,外汇和贵金属这类高杠杆品种风险尤其突出。 柱线范围还能反推操作价位。若你做多并预期范围扩张,用 H – L 的量级去摆入场和止盈,比拍脑袋更贴近当下市况。常见蜡烛如看跌、看涨、锤子、墓碑、长腿、蜻蜓,其范围算法统一为最高价减最低价,先把这类基础形态的范围认熟,再看聚类才有意义。
「用树状图给多维行情数据分层」
集聚层次化分类(AHC)把数据先切成预设数量的聚类,再用树状图按系统层次把聚类串起来。外汇和贵金属行情里,单根柱线往往携多维信息(开盘、最高、最低、收盘、成交量),人工比对多变量很费力,AHC 则从每个数据点的质心起手建聚类,并按相似度排成有序层级。 排位本身有实战价值:若模型给出 5 个聚类,树状图会标出聚类间的间隔幅度——间隔越小,两类行情形态越接近;间隔越大,差异越明显。当你怀疑两段走势分属不同聚类时,看间隔就能粗略判断偏离程度,不必重跑全套统计。 AHC 是无监督的,这意味着同一批训练聚类可被不同人拿去预测不同目标:你用来估柱线范围,别人可能用来判收盘价变化。相比监督模型绑死单一分类任务(换目标就得重训),这种灵活性在 MT5 上做多策略验证更省事。但外汇/贵金属杠杆高、跳空频繁,任何聚类结论只代表历史样本中的概率倾向,不能直接当方向指令。
◍ 借标准类搭系统比裸写更稳
在 MT5 里从头写 EA 不是不行,但每人对同一策略的实现细节都可能不同,定制过度的代码改起来容易埋 bug。更务实的做法是把想法挂进 MQL5 向导提供的标准类体系里,作为其实例存在,这样能直接跟向导里的其他类拼装,得到可交叉测试的多种 EA 组合。 内置类也不是零错误,但相比纯手写定制逻辑,调试量明显更少,而且复用性高。把策略做成标准类的一部分,等于给自己留了一个低摩擦的测试平台。 MQL5 函数库里的 AlgLib 类在本系列前面已提过,本文继续用。具体在 DATAANALYSIS.MQH 中,靠 CClustering 类及几个关联类给价格序列做 AHC 聚类。我们的训练数据取自前段周期的价格柱线范围,用这类范围来喂模型。 用数据分析头文件训练时,典型做法是把数据塞进一个 XY 矩阵:X 放自变量,Y 放分类标签,二者同处一个矩阵。开 MT5 翻一下 DATAANALYSIS.MQH 的 CClustering 调用示例,就能照此把柱线范围矩阵化跑通聚类。
无监督训练下的历史波动数据拼装
做无监督训练时,喂给模型的只有 X 自变量——也就是历史价格柱线的波动范围。我们想让模型参考的「最终柱线波动范围」相当于 Y,但为了保持无监督的灵活度,实际数据结构里并不把它当监督标签硬绑定,而是随每根新柱线开立时收集一批历史范围进来。 MQL5 向导生成的智能系统默认就是每根新柱做交易决策,拿它跑这套采集逻辑足够验证聚类效果。你也可以换思路,拉跨月数年的大批次数据,看模型最终能否把低波动和高波动柱线自然分开。 原文示例只设 3 个聚类:一个装剧烈波动、一个装极低波动、一个装中等波动;换 5 个聚类原理相同。把聚类按波动率从高到低排好,就能定位当前数据点掉进哪一档,外汇与贵金属品种在此类聚类上受跳空影响大,高风险特征明显。 下面这段类声明给出了关键参数容器,m_clusters 控制聚类数、m_training_points 控制训练样本量,都留了 setter 方便在 EA 初始化时调。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| | class=class="str">"cmt">//+------------------------------------------------------------------+ class CMoneyAHC : class="kw">public CExpertMoney { class="kw">protected: class="type">class="kw">double m_decrease_factor; class="type">int m_clusters; class=class="str">"cmt">// clusters class="type">int m_training_points; class=class="str">"cmt">// training points class="type">int m_point_featues; class=class="str">"cmt">// point featues ... class="kw">public: CMoneyAHC(class="type">void); ~CMoneyAHC(class="type">void); class="kw">virtual class="type">bool ValidationSettings(class="type">void); class=class="str">"cmt">//--- class="kw">virtual class="type">class="kw">double CheckOpenLong(class="type">class="kw">double price,class="type">class="kw">double sl); class="kw">virtual class="type">class="kw">double CheckOpenShort(class="type">class="kw">double price,class="type">class="kw">double sl); class=class="str">"cmt">//--- class="type">void DecreaseFactor(class="type">class="kw">double decrease_factor) { m_decrease_factor=decrease_factor; } class="type">void Clusters(class="type">int value) { m_clusters=value; } class="type">void TrainingPoints(class="type">int value) { m_training_points=value; }