简介:适合正在研究MT4自动化交易与跟单功能的C#开发者,这份压缩包提供了一套可直接引用的MT4服务器接口动态链接库,并附带一份扩展标记语言格式的接口配置或说明文档。资源定位为轻量级接口封装工具,主要作用是帮助使用者免去自行编写底层通信逻辑的繁琐过程,在项目中快速获得与MT4服务器交互的能力,从而把开发重点放在交易策略、订单管理、账户数据读取或程序化同步跟单等上层业务环节。压缩包内共有2个文件,分别是提供调用接口的动态链接库,以及记录接口参数或调用方式的扩展标记语言文档,二者配合即可完成基础集成工作;整体大小只有174KB,体量小巧,下载和部署都很方便。目前已有331人浏览学习,尤其适合刚接触MT4二次开发的技术人员作为接口应用样板进行研读,也适合需要搭建简易跟单系统或交易盯盘工具的中级开发者做参考借鉴。通过对照说明文档与动态链接库中的可用接口,能够较快理清MT4服务器接口的调用脉络,节省项目初期的调研和试验成本。
1. 拆开 mt4demo.zip 之前,先搞懂这个包要解决什么问题
拿到一个名为mt4demo.zip_gravity1qr_mt4apidll_mt4api跟单_mt4dll接口_zip的压缩包,第一反应不是急着解压,而是先看它把哪几样东西捆在了一起:demo 示例、一个叫 gravity1qr 的策略或信号源标识、MT4 API 的 DLL、还有跟单。合起来翻译成人话就是——一包集齐了「用 MT4 DLL 接口做跨账户跟单」的最小可跑工程。MT4 本身不带跟单功能,跨账户复制订单要么靠 EA 之间用全局变量或文件传信号,要么靠 DLL 直接拉信号源的持仓数据再加工,后者是多数商用跟单系统的底座。这篇笔记就沿着这个包背后的技术路线展开:DLL 接口怎么写、MQL4 怎么调、跟单参数怎么设、哪些坑绕不开。适合手里攥着这种 demo 但不知道怎么下手的开发者,也适合想从零搭一套 MT4 跟单桥的人照着复现。
2. 认识 mt4demo 包内的标准配置:DLL 接口原理与文件真伪
2.1 一个规范的 MT4 DLL 跟单包通常长什么样
这类压缩包解压之前,先按 MT4 的标准目录习惯预判它的内部结构。大多数跟单 demo 包会包含四个部分:
| 路径 | 文件类型 | 作用 |
|---|---|---|
MQL4/Experts/ | .mq4/.ex4 | 跟单主 EA,负责轮询、下单、平仓 |
MQL4/Libraries/ | .dll | MT4 API 扩展,负责拉取信号源持仓、处理数据 |
MQL4/Include/ | .mqh | 公共常量与函数声明,比如订单类型映射、参数结构体 |
docs/或包根目录 | .txt/.pdf | 部署说明、参数含义、DLL 依赖说明 |
gravity1qr多半是包内某个策略代号或者信号源的标识符,1qr可能是手数档位或资金档位,具体含义以包内说明文档为准。拿到包先别双击运行任何.exe,先把 README 找出来看一遍,确认这个 DLL 依赖什么版本的 MT4 编译器。MT4 从 build 600 之后语法变化很大,新版本不再支持老式#property写法,直接把旧.mq4硬编译进新平台会出现大量报错。
2.2 MT4 API DLL 到底解决了什么原生做不到的事
MQL4 是一门能力被刻意收窄的语言。它跑在 MT4 终端进程内,能下单、能读行情、能读写文件,但没有稳定的网络请求接口,跨进程通信手段也十分有限。而 DLL 接口正好补上这块短板:用 C++ 写一个动态链接库,MQL4 通过#import把函数引进来,双方在同一进程内直接调用,传递参数、拿返回值。
跟单场景对 DLL 的需求尤其明显。一个账户要复制另一个账户的订单,有两条常见路线:
- 信号源 EA 把每次开平仓写入本地文件或全局变量,跟随端 EA 周期性读取。实现简单,但文件写入延迟高,且多账户、多终端并行时有写锁冲突。
- 用 DLL 直接连接信号源所在的 MT4 终端或 broker 接口,拉取持仓列表和成交记录。跟随端拿到的是订单级数据,可以精确复制方向、手数、止损止盈,这是多数人选择 DLL 跟单的核心原因。
常见做法是 DLL 内部通过 MT4 提供的 API(比如 MT4 Manager API,或者操作终端本地数据库)读取指定账户的持仓快照,封装成简单结构体或数组返回给 MQL4 层。
2.3 解压与文件真伪检查:别让 DLL 当黑匣子
在 Windows 上解压可以直接用资源管理器,但建议用命令行或 7-Zip 拿到完整输出,方便看隐藏文件和路径长度。PowerShell 下可以用:
Expand-Archive -Path mt4demo.zip -DestinationPath .\mt4demo_src然后立即做三件事。第一,确认 DLL 的位数。MT4 终端是 32 位进程,DLL 必须是 32 位编译,否则加载直接报LoadError 113。用 Visual Studio 自带的 dumpbin 查:
dumpbin /headers mt4apidll.dll | Select-String "machine"输出x86表示 32 位,x64表示 64 位。第二,看 DLL 的导出函数列表:
dumpbin /exports mt4apidll.dll导出表里应该能看到QueryPositions、GetServerTime这类函数名。如果导出表空空如也,或者只有DllMain,说明这个 DLL 不是按 MT4 调用约定导出的,八成加载后调不到。第三,跑一遍哈希校验,和文档里给出的 SHA256 比对,防止下载过程文件损坏或被人替换过:
Get-FileHash .\mt4demo_src\MQL4\Libraries\mt4apidll.dll -Algorithm SHA2562.4 压缩包自身的坑:伪加密、密码与损坏文件
热词里反复出现 zip 伪加密和 zip 密码移除,这确实是接手不明压缩包时常遇到的第一个门槛。伪加密的典型表现是:用 Windows 资源管理器双击能列出文件名,但解压时提示输入密码,而包内又没有密码说明。原因是 zip 的加密标志位被手工改过,或者制作方用工具做了个假加密外壳。判断方法很简单:用 7-Zip 打开,如果能看到文件列表且每个文件前没有锁形图标,那基本就是伪加密。处理方式是把加密标志位还原,常见做法是用十六进制编辑器打开 zip,定位到全局位标志的第 0 位,把09改成00,保存后即可正常解压。这个操作只对伪加密有效,真加密拿不到密码时不要硬来,找包作者要才是正路。
另一种翻车是解压中途报missing zip entry或unexpected end of data,通常是文件在传输过程中被截断。先用zip -T或 7-Zip 的测试功能确认损坏范围,再考虑重新下载。这个包如果是从网盘下的,优先重下一次而不是花时间修复。
3. 编译 mt4apidll:从 C++ 导出函数到 MQL4 的 Import 声明
3.1 为什么 MT4 只认 32 位 DLL,以及编译环境怎么选
MT4 终端本身是 32 位程序,所以它只能加载 32 位的 DLL。如果你拿 Visual Studio 默认的 x64 配置编译,放到MQL4/Libraries目录里,MT4 日志会不停刷cannot load library或者LoadError 113,而且MessageBox都弹不出来。这不是 DLL 写得不对,是位数不匹配。编译环境选择上,Visual Studio 2019/2022 用 x86 平台工具集是最省事的;不想装 VS 的话,MinGW-w64 配合-m32参数也可以,但要注意运行库依赖,MinGW 编出来的 DLL 在别的机器上可能缺 libgcc 运行时。
MT4 调用 DLL 时默认按stdcall调用约定工作,所以导出函数要显式写成__stdcall。很多人图省事用__cdecl,在本地测试偶尔能跑,换台机器或换个 MT4 build 就随机崩溃,这种玄学问题最浪费排查时间。
3.2 用 C++ 写一个最小可用的导出函数
打开一个空项目,新建mt4apidll.cpp,下面是一个从 MT4 终端取服务器时间的示例。这里故意不碰复杂业务,先把调用链路跑通。
#include <windows.h> #include <ctime> extern "C" __declspec(dllexport) int __stdcall GetServerTime(int* year, int* month, int* day) { time_t rawTime; time(&rawTime); struct tm timeInfo; localtime_s(&timeInfo, &rawTime); *year = timeInfo.tm_year + 1900; *month = timeInfo.tm_mon + 1; *day = timeInfo.tm_mday; return 0; } BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved) { return TRUE; }extern "C"避免 C++ 符号改编,__declspec(dllexport)声明导出,__stdcall匹配 MT4 的调用约定。三个指针参数用于回写数据,返回0代表成功。
编译成mt4apidll.dll后,放到MQL4/Libraries/目录下。然后在 MT4 的 MetaEditor 里新建一个测试脚本调用它。
#import "mt4apidll.dll" int GetServerTime(int& year, int& month, int& day); #import void OnStart() { int year, month, day; int ret = GetServerTime(year, month, day); Print("ret=", ret, " time=", year, ".", month, ".", day); }这里int&在 MQL4 里对应 C++ 的指针参数,year、month、day是输出型参数。跑通后你会看到日志打印当前时间,说明 DLL 加载、调用、参数传递三个环节全部正常。这一步是整个跟单工程的骨架,后面所有持仓数据、账户信息都要走同一个通道。
3.3 字符串和数组跨 DLL 边界该怎么传
跟单 DLL 必然涉及字符串,比如账户号、订单注释、品种代码。MQL4 的string是宽字符,而 C++ 侧常用char*,直接当指针传最容易出乱码甚至崩溃。常见做法是避开字符串,用int或double数组传数据。例如要传一个订单注释标签,先把它编码成整数常量,在 DLL 侧再映射回字符串。如果非得传字符串,MQL4 侧声明uchar数组,调用StringToCharArray转成字节,DLL 侧按char*接收:
uchar commentBytes[]; StringToCharArray("order_copied", commentBytes); #import "mt4apidll.dll" int SetCommentTag(uchar& tag[], int length); #import数组传递的规则:MQL4 侧用引用方式uchar& tag[],C++ 侧接收char* tag,配合长度参数确保不会越界。这是 MT4 DLL 开发里最容易被忽略的边界,一旦越界写坏内存,MT4 终端会直接崩溃而不只是报错。
3.4 编译产物放置与首次加载的开关设置
DLL 放错目录是新手最常见的翻车点。MQL4/Libraries/下的文件,MT4 才能通过#import按文件名加载。如果你把 DLL 直接丢到Experts目录,MT4 加载时会提示找不到库。另外注意 64 位操作系统下的重定向问题:不要往C:\Windows\System32里放这个 DLL,就放在终端数据目录下。
首次加载还会遇到安全策略拦截。MT4 在「工具 → 选项 → EA」里有两道开关:允许导入 DLL 的全局开关,以及每个 EA 单独设置里「允许导入外部 DLL 的勾选」。两道都开才放行。打开的瞬间日志会刷一条 DLL 加载记录,没刷就是被拦了。这条记录很重要,后面排错全靠它。
4. 用 MT4 API 跑通跟单:从信号源到跟随端的核心参数映射
4.1 跟单的三种主流模式与实际场景选型
MT4 跟单在散户里热度一直不减,「雪球跟单」这类社区模式让很多人以为跟单就是输入一个账号密码然后自动复制。放到 MT4 生态里,跟单桥的实现模式大致分三种。
第一种是文件信号模式。信号源 EA 把每次开单、平仓的订单信息追加写入本地 CSV,跟随端 EA 轮询读取。优点是实现简单、不依赖 DLL,缺点是同一终端多账户时文件锁冲突明显,且不能跨机部署。
第二种是内存共享模式。DLL 内部用 Windows 共享内存或命名管道和另一个进程通信,信号源端进程把持仓变化写进共享内存,跟随端进程读取。延迟低,但需要额外维护一个信号采集进程。
第三种是 API 拉取模式,也是这个 demo 包最典型的架构。跟随端 DLL 直接调用 MT4 Manager API 或终端本地数据库,读取信号源账户的持仓列表,按需映射下单。Manager API 方式需要代理服务器地址和权限,终端本地数据库方式则要求信号源和跟随端在同一台机器的同一 MT4 数据目录下。个人跟单场景推荐从终端数据库方式起步,零权限成本,先跑通再考虑升级到 Manager API。
4.2 信号流设计:DLL 怎么把持仓快照交给跟随端
我一般会把 DLL 设计成无状态查询模式:跟随端 EA 按固定间隔调用一个查询函数,DLL 读取信号源持仓快照,写入调用方传入的数组,返回订单数量。一个最小化的导出函数长这样:
extern "C" __declspec(dllexport) int __stdcall QueryPositions( int account, int* ticketArray, int* typeArray, double* lotArray, double* openPriceArray, int maxCount) { // 伪代码:从 MT4 终端数据库读取 account 的当前持仓 // rows = ReadOpenOrders(account); // for (i = 0; i < min(rows, maxCount); i++) // { // ticketArray[i] = rows[i].ticket; // typeArray[i] = rows[i].type; // OP_BUY / OP_SELL // lotArray[i] = rows[i].lots; // openPriceArray[i] = rows[i].openPrice; // } // return min(rows, maxCount); return 0; }account是信号源账户号,maxCount限制单次最大返回条数,防止跟随端数组越界。调用者需要预先按maxCount大小初始化四个数组。这里没有传字符串,全部是整数和浮点数,避开字符串编码问题。DLL 内部实际读取逻辑依赖 broker 类型和 MT4 安装方式,可能是解析MQL4/Profiles下的配置,也可能是走 Manager API 的持仓接口。
4.3 跟随端 EA 的轮询与幂等开平仓
跟随端 EA 的核心职责是定时调用QueryPositions,拿信号源持仓快照,跟本地已复制的持仓做差量,然后开新单、平缺失单。一个基础版 EA 的骨架如下:
#import "mt4apidll.dll" int QueryPositions(int account, int& tickets[], int& types[], double& lots[], double& openPrices[], int maxCount); #import input int SourceAccount = 123456; input int MasterMagic = 202401; input double LotMultiplier = 1.0; input int ScanIntervalSeconds = 1; int copiedTickets[]; datetime lastScan = 0; void OnTick() { if (TimeCurrent() - lastScan < ScanIntervalSeconds) return; lastScan = TimeCurrent(); int maxCount = 200; ArrayResize(copiedTickets, maxCount); int tickets[], types[]; double lots[], openPrices[]; ArrayResize(tickets, maxCount); ArrayResize(types, maxCount); ArrayResize(lots, maxCount); ArrayResize(openPrices, maxCount); int count = QueryPositions(SourceAccount, tickets, types, lots, openPrices, maxCount); for (int i = 0; i < count; i++) { if (!IsAlreadyCopied(tickets[i])) { OpenCopyOrder(types[i], lots[i] * LotMultiplier); MarkCopied(tickets[i]); } } SyncClosedOrders(count, tickets); }IsAlreadyCopied检查全局变量或数组里是否已存在这个信号源 ticket,防止同一订单被重复开。MarkCopied记录已处理订单。SyncClosedOrders对比两边持仓,信号源已平掉的单子跟随端也要平。TimeCurrent() - lastScan做秒级节流,避免OnTick每跳都去拉 DLL,把 CPU 打满。
4.4 必调参数表:从 demo 到实盘只动这几个
| 参数 | 默认值 | 说明 | 调整建议 |
|---|---|---|---|
SourceAccount | 无 | 信号源账户号 | 确认是真实账户号而非 MT4 登录名 |
MasterMagic | 202401 | 信号源 EA 的魔法数 | 只跟这个 magic 的订单,避免混入手动单 |
LotMultiplier | 1.0 | 手数倍率 | 资金差大时调到 0.1,单位净值小配 0.01 |
ScanIntervalSeconds | 1 | 轮询间隔 | 实盘不小于 1,低于 1 秒无意义 |
MaxSlippage | 3 | 最大滑点(点) | 行情剧烈时调到 5 以上 |
MaxLot | 10 | 单笔最大手数 | 防手数倍率乘出超限仓位 |
严格止损和控制手数倍率是跟单的底线。倍率设成 0 会被当成平仓信号,这是最常见的误配置,后面避坑章会细说。
4.5 手数映射的策略逻辑:固定倍率与资金占比
手数映射只有两种主流做法。固定倍率用源单量 × LotMultiplier,线性透明,适合两账户资金量级接近的情况。资金占比映射则是源单量 × (跟随端净值 / 信号源净值),能自适应资金差异,但 D 值一旦剧烈波动,开单量会忽大忽小。实盘里我习惯在 demo 阶段先用固定倍率跑两周,确认信号源风格——如果是马丁策略,单量波动极大,必须套一层MaxLot上限,否则一次跟单就能把账户打到爆仓边缘。
5. MT4 DLL 接口与跟单排错:5 个绕不开的踩坑点
5.1 现象:DLL 加载失败,日志报 LoadError 113
原因:最常见的 64 位与 32 位不匹配,其次是 DLL 依赖的 VC 运行库缺失。日志里cannot load library后面跟的数字,113 就是位数错误。
解决:确认 MT4 安装目录下的terminal.exe是 32 位进程,然后用 dumpbin 核对 DLL 的 machine 字段。VC 运行库缺失的话,装对应版本的vcredist_x86.exe。另外检查 DLL 是否被 Windows 标记为「来自网络」而锁定,右键属性 → 解除锁定,这个细节能卡住一整天的部署进度。
5.2 现象:DLL 调用成功后频繁乱码或闪退
原因:字符串参数在 MQL4 与 C++ 之间的编码不一致。MQL4 的string默认是宽字符,C++ 按 ANSI 或 UTF-8 处理,两种编码在内存里长度完全不同,读错一个字节就是乱码,写越界就是闪退。
解决:跟单业务里尽量不用字符串传参。订单注释、品种代码这类数据在 DLL 侧全部映射成int常量,边界传递用整型数组,字符串只留在两边的配置映射表里。确实需要字符串时用uchar数组加长度参数,并提前ArrayResize足够容量。
5.3 现象:DLL 调试进程里正常,挂到 MT4 里就随机崩溃
原因:MT4 终端是长时间运行的进程,DLL 的函数在每根 tick 上被反复调用,任何内存泄漏或越界都会被放大。调试器里运行几分钟没问题,挂一天就崩,多半是数组长度上限写死、没有检查越界。
解决:所有数组读写都加上限判断,查询函数里的maxCount严格约束写入长度,EA 侧传入的数组容量必须大于等于maxCount,留有 10% 冗余。DLL 内部不要用静态缓冲区存订单快照,改用局部std::vector或每次调用重新分配,避免线程冲突。
5.4 现象:同一信号单被重复开仓
原因:跟随端 EA 没有记录已处理的信号源 tick,每次查询都把同一持仓当成新单。这是轮询式跟单最容易翻车的业务逻辑。
解决:已复制映射表要做到「持久化」。用GlobalVariableSet存一个序列化字符串,或者写入本地 CSV,重启终端后还能恢复。否则 MT4 一重启,内存里的已复制列表清空,全部历史订单会再来一轮,账户瞬间堆满。
5.5 现象:zip 解压时提示密码,但包内文档说无密码
原因:包制作者打包时不小心设置了伪加密,或者用了带密码工具但忘记把密码写进 README。伪加密的特征是 7-Zip 能预览文件内容,但资源管理器解压时要求输入密码。
解决:优先联系包作者问密码。如果只是标志位被修改的伪加密,用十六进制编辑器把 zip 头部的全局位标志从09改回00即可,这个操作只对伪加密生效,对真加密无效。改完后重新解压,先跑哈希校验再进下一步。顺带提醒一句:从不明渠道下载的跟单 DLL 不要直接放在主力交易终端里,先在虚拟机或隔离环境跑干净再转投实盘。
6. Demo 账户验证跟单质量:延迟、手数映射与稳定性检查
验证跟单质量不能靠肉眼看,要量化三个指标:延迟、映射正确率、DLL 内存稳定性。搭一个双 demo 测试环境,装两份 MT4 客户端,一份跑信号源,一份跑跟随端。
延迟测试用同一个品种、同一时刻手动开单,在跟随端日志里打两行时间戳——signal_source_time和copy_order_done_time,差值就是总延迟。常见环境下轮询间隔 1 秒、同机部署时延迟在 1.5 到 2.5 秒之间属正常。如果超过 5 秒,先看经纪商服务器时区是不是跟本地时间差太多,再看轮询间隔和 DLL 查询耗时。
批量开平仓测试建议连续开 50 笔迷你单,中间带方向切换,覆盖 OP_BUY 和 OP_SELL 的双向映射。逐一核对每笔复制单的方向、手数倍率、止损止盈偏移,最多允许一两笔因为点差问题滑点超限,方向反了必须零容忍。
稳定性检查看两项:MT4 终端内存占用是否随时间线性增长,以及 DLL 调用耗时是否逐渐变大。连续跑 24 小时,用任务管理器记录 terminal.exe 的内存曲线,如果持续上涨超过 30%,基本可以断定 DLL 里有内存泄漏,必须回头检查每个数组释放逻辑。
我自己的习惯是:任何跟单桥先跑满一周 demo,中间手动模拟一次断网重连、一次 MT4 重启,确认已复制列表能自动恢复,才考虑放小资金实盘。跟单这种自动执行系统,黑匣子状态越少越好;日志打不满,宁可不跑也不要裸奔。希望帮到你。
本文还有配套的精品资源,点击获取