从头开始开发智能交易系统(第 7 部分):添加价格成交量(Volume)指标(I)(基础篇)
「给EA接上成交量指标的第一刀」
在 MT5 里做价格行为类 EA,很多人只盯收盘价和均线,却把 Volume 这个内建指标晾在一边。其实 iVolume 能直接拿到每根 K 线的真实成交量,用来过滤假突破比单纯看影线更硬。 MQL5 的成交量接口不走自定义缓冲,直接调 iVolume(_Symbol, _Period, i) 就能取第 i 根柱的量。注意 MT5 外汇品种成交量本质是 tick 计数,不是交易所真实手数,贵金属同理,解读时得带上这个前提,否则会把刷单行情误判成资金涌入。 下面这段是挂上成交量句柄的最小骨架,跑之前先在 EURUSD 的 M15 上印出最近 10 根量,验证数据通道没断: int vol_handle = iVolume(_Symbol, _Period); if(vol_handle == INVALID_HANDLE) return; for(int i=0;i<10;i++){ double v = iVolume(_Symbol,_Period,i); Print("bar ",i," volume=",v); }
class="type">int vol_handle = iVolume(_Symbol, _Period); if(vol_handle == INVALID_HANDLE) class="kw">return; for(class="type">int i=class="num">0;i<class="num">10;i++){ class="type">class="kw">double v = iVolume(_Symbol,_Period,i); Print("bar ",i," volume=",v); }
◍ 水平量能指标为何值得上图
做价格行为交易的人,图表上常缺不了一个水平成交量指标。它专门统计某个价位上在特定时段内累积的成交量,用来判断资金在哪些价格停留最久。和那些靠读盘口跳动的人常用的均线、动量类工具不同,这类指标只认价格本身发生的成交密度。 很多人以为这种指标必须零延迟实时刷新,其实未必。我自己的经验是,约 1 秒的延迟在读取上基本无感,不会漏掉关键的量价结构。MetaTrader 5 平台跑这类计算若每次 tick 都重算,反而容易拖慢终端。 本文不展开讲怎么读指标示数,重点放在如何写出一个不拖累 MT5 性能的水平量能指标。若你实在要实时,别在指标内部改逻辑,而是去 EA 调用它的地方做即时取数——代价只是极小的概率性性能波动,延迟基本可忽略。外汇与贵金属波动剧烈,用任何量价工具都先在小周期回测验证。
图表属性没配对,控件就废了
价格成交量类指标的控制界面本身极简,真正卡人的地方不在按钮,而在加载图表的属性面板。主控项若没点亮,栅格不显示,你连拖拽调整指标尺寸都做不到——这不是代码 bug,是 MT5 图表底层的显示开关被关了。 界面上实际只有两个控件:一个标定显示尺寸,一个指定成交量分析的起始位。动画里能直接看到,栅格消失后控件区变成死区,鼠标拖不动也改不了范围。 外汇与贵金属波动大、跳空频繁,这类依赖图表栅格的可视化工具在高波动时段可能失真,实盘前务必在模拟盘验证显示逻辑。开 MT5 随便挂一个成交量指标,右键图表属性把「显示网格」勾上,再回来拖控件,立刻能复现这个依赖关系。
「把绘图控制权握在自己手里」
为了少写重复代码,这一版把 C_Wallpaper 和 C_VolumeAtPrice 的公共部分抽进新的 C_C_Canvas 类,主类只剩核心逻辑。有人会问为什么不直接用 MT5 自带的 C_Canvas——作者偏好完全掌控绘制过程,你也完全可以改用官方类,不影响后续成交量分布的实现。 真正的主角是 C_VolumeAtPrice,它只暴露七个函数:Init 按用户参数初始化;Update 按间隔刷新价量数据;Resize 改图像尺寸方便细看;DispatchMessage 收消息;后三个 FromNowOn、SetMatrix、Redraw 是私有的,分别管系统变量、成交量矩阵和维护映像。 变量声明里有一处高亮测试很关键:用宏判断避免和引入文件里的定义冲突。MQL5 编译器在覆盖已有定义时只给警告,真碰上了排查极费时间,这段预处理能提前拦掉。另一个该盯的是 def_SizeMaxBuff,它决定成交量数组长度,实测当前值已覆盖绝大多数品种和周期,代表最低价到现价间即时报价跳数的容纳上限,必要时可手动改大。 下面这段 C_WallPaper 类已经瘦身完毕,继承自制绘图类,仅留 Init 与 Resize 两个对外方法。Init 里若类型是纯色直接返回,否则按终端宽高建画布并加载 WallPapers 目录下的位图,最后强制置底;Resize 则跟着终端尺寸重拉位图。开 MT5 把这段贴进新指标,改 szName 就能看到背景图随窗口缩放。
class C_WallPaper : class="kw">public C_Canvas { class="kw">protected: enum eTypeImage {IMAGEM, LOGO, COR}; class=class="str">"cmt">//+------------------------------------------------------------------+ class="kw">private : class="kw">public : class=class="str">"cmt">//+------------------------------------------------------------------+ ~C_WallPaper() { Destroy(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool Init(class="kw">const class="type">class="kw">string szName, class="kw">const eTypeImage etype, class="kw">const class="type">char cView = class="num">100) { if (etype == C_WallPaper::COR) class="kw">return true; if (!Create(szName, class="num">0, class="num">0, Terminal.GetWidth(), Terminal.GetHeight())) class="kw">return class="kw">false; if(!LoadBitmap(etype == C_WallPaper::IMAGEM ? "WallPapers\\" + szName : "WallPapers\\Logos\\" + _Symbol, cView)) class="kw">return class="kw">false; ObjectSetInteger(Terminal.Get_ID(), szName, OBJPROP_BACK, true); class="kw">return true; } class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void Resize(class="type">void) { ResizeBitMap(Terminal.GetWidth(), Terminal.GetHeight()); } class=class="str">"cmt">//+------------------------------------------------------------------+ };