从头开始开发智能交易系统(第 7 部分):添加价格成交量(Volume)指标(I)(基础篇)
📘

从头开始开发智能交易系统(第 7 部分):添加价格成交量(Volume)指标(I)(基础篇)

第 1/3 篇

「给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); }

MQL5 / C++
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 就能看到背景图随窗口缩放。

MQL5 / C++
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">//+------------------------------------------------------------------+
};

常见问题

先在图表属性里把成交量显示开关打开,否则后续创建的指标控件不会出现在图上,等于白接。
它把每段K线的成交量压成一条水平线,方便直接对比相邻K线的量能强弱,过滤掉视觉噪声。
小布可自动扫描品种图表的成交量控件状态并标出异常,你不用手动翻图表属性逐个核对。
检查图表的ShowVolumes类属性是否为true,以及父容器坐标范围是否覆盖了控件区域。
创建指标对象时显式传入自身定义的画布与刷新回调,别依赖终端自动挂载,才能随时重绘。