指标间的数据交换:易如反掌!(基础篇)
「指标间直接交换数据其实很简单」
在 MT5 里,不同自定义指标之间不需要借助全局变量或文件就能互相读取缓冲区数据,核心靠的是 iCustom 拿到句柄后用 CopyBuffer 取数。这比 MT4 时代用全局变量中转要干净,也避免了多品种多周期下的竞态。 一个常见误区是以为指标只能吃价格序列。实际上只要目标指标已经算好某 buffer,调用方在 OnCalculate 里拿到的就是现成数值,延迟只取决于图表刷新节奏。外汇与贵金属市场高杠杆、跳空频繁,直接读缓冲比落盘再读更不容易踩到旧数据。 下面这段演示如何在一个指标里取另一个指标的 buffer 0: [int handle = iCustom(NULL, 0, "SomeIndicator", 14);] [if (handle == INVALID_HANDLE) return 0;] [CopyBuffer(handle, 0, 0, rates_total, buffer);] 第一行用 iCustom 在当前图表、当前周期加载名为 SomeIndicator 的指标,参数 14;第二行判空,句柄无效直接退出;第三行把目标指标第 0 号缓冲区从 0 到 rates_total 拷进本地 buffer。开 MT5 新建指标把这段接上,改个指标名就能立刻验证数据是否贯通。
class="type">int handle = iCustom(NULL, class="num">0, "SomeIndicator", class="num">14); if (handle == INVALID_HANDLE) class="kw">return class="num">0; CopyBuffer(handle, class="num">0, class="num">0, rates_total, buffer);
别用‘不可能’堵死提问
很多刚接触 MQL5 的 trader 不爱翻文档,开口就是‘怎么让 EA 在贵金属跳空时自动撤单’,得到的回复往往是‘没门’‘不可能’。这其实堵死了真正值得验证的路径。 外汇与贵金属市场高杠杆、滑点频发,看似‘做不到’的需求,往往只是默认函数没覆盖边界情况。 ‘永不说不’这条工程圈老话,对写指标和 EA 的人同样适用:先想能不能用现有接口拼出来,再谈限制。
◍ 为什么 iCustom 总让你重复算指标
很多老手也反复问过一类问题:图表上已经挂了指标 A,指标 B 想直接读 A 的缓冲区,而不是每次用 iCustom 重新拉一遍。MQL4 里每调一次 iCustom 就给调用方克隆一份指标实例;MQL5 改用句柄,原本只算一次,但句柄不是万灵药——若指标吃算力,终端互斥等待几乎靠每次重新初始化十几秒来兜底。 社区常见的绕法是在物理磁盘或内存映射文件、走 DLL 共享内存、用客户端全局变量做中转,甚至套接字、邮件槽也都见过。这些路子各有可取处,但通病是先复制数据再分发:既多吃 CPU,又引出传输数据相关性的新麻烦。 本文要定义的痛点很直接:能否建一套环境,对图表已附加指标提供数据访问,且满足三条——不复制数据、只需微改现有方法代码、MQL 代码为主(DLL 仅嵌少量 C++ 字符串)。笔者用 C++ Builder 写 DLL,客户端覆盖 MT4 与 MT5,下文源码以 MQL5 呈现,MT4 版附于原文,两者差异后续拆解。外汇与贵金属市场高杠杆高风险,这类底层优化只解决工程效率,不预示任何交易结果。
「动态数组在内存里是怎么排的」
要写指标缓冲区,得先搞清楚 MQL 动态数组在内存里的分布方式。官方文档对这块语焉不详,但实测和推理能拼出关键结论:动态数组的数据在任意时刻都是一段连续内存,元素一个挨一个排;数组扩容时如果身后没空位,整段会被搬到有足够空间的新区域,而不是拆成碎片用引用链管理。 为什么能这么判断?当数组按引用传给外部 DLL 时,传过去的是首元素指针,后面元素顺序紧跟。若采用分块存储,调用 DLL 前就得先把全量数据拷到一段连续区,返回再合并——这跟整段搬移干的活没区别,反而更麻烦。我们基于该假设做的产品能正确跑起来,也算侧面印证。 落到写代码上:在 OnCalculate() 被调用的那一瞬,你的 double[] 缓冲区(假设维度一致、类型都是 double)就是一块连续 double 序列。知道了这点,用 memcpy 思路做跨缓冲区批量搬运、或把首地址丢给 DLL 做指标加速,才不会因为内存布局误判而读飞。外汇与贵金属行情高波动,这类底层误用会直接让信号错位,回测和实盘前务必在 MT5 用注释打印首地址验证连续性。
用 DLL 把数组地址当整数使
MQL5 官方明确不支持通用指针,但指针本质就是内存单元的序号——也就是个整数。MT5 跑 MQL 程序处理整数毫无压力,所以绕开语言限制的思路很直接:让 C++ 写的 DLL 把指针强转成 int 交回来。
C++ 指针是四字节数据类型,用 int 接最顺手。符号位无所谓,负就负,只要 32 位地址位原样保留即可;选 int 而不用 unsigned 是为了 MQL4/MQL5 两边代码尽量一致,老 MQL4 根本没有无符号整型。
下面这段 DLL 导出函数就是整套机制的核心。GetPtr 收一个 double 数组首地址,直接 (int)a 丢回给 MQL;GetValue 拿回整数地址和偏移 i,还原成 double* 再取下标;SetValue 同理写回。实测在 32 位 MT5 构建下地址能完整塞进 int,64 位终端则需改 long,否则高位截断会读飞。
拿到地址后,MQL 侧只要存下那个长型变量,就能随时调 GetValue/SetValue 像操作指针数组一样碰内存。外汇和贵金属杠杆高、运行时内存模型随终端版本变,这类黑魔法只建议在本地回测或私有工具里验证,别挂实盘 EA 盲目依赖。
class="kw">extern "C" __declspec(dllexport) class="type">int __stdcall GetPtr(class="type">class="kw">double *a) { class="kw">return((class="type">int)a); } class="kw">extern "C" __declspec(dllexport) class="type">class="kw">double __stdcall GetValue(class="type">int pointer,class="type">int i) { class="kw">return(((class="type">class="kw">double*) pointer)[i]); } class="kw">extern "C" __declspec(dllexport) class="type">void __stdcall SetValue(class="type">int pointer,class="type">int i,class="type">class="kw">double value) { ((class="type">class="kw">double*) pointer)[i]=value; }