在 MetaTrader 5 中使用 MATLAB 2018 的计算功能(基础篇)
📘

在 MetaTrader 5 中使用 MATLAB 2018 的计算功能(基础篇)

第 1/3 篇

让 MT5 直接调起 MATLAB 算因子

想在 MT5 里跑 MATLAB 2018 的矩阵运算,核心不是把代码塞进 MQL5,而是让两个进程通过本地通信交换数据。MT5 负责行情与下单,MATLAB 负责重计算量的统计建模,分工比纯 MQL5 写数值算法更省力。 实测环境为 MATLAB 2018 + MetaTrader 5 build 1881,作者在 2019 年 5 月 13 日发布的示例帖获得 3056 次查看、19 条讨论,说明这种跨平台调用在量化交易者里有稳定需求。 具体落地时,可用 MQL5 的 Socket 或文件映射把 K 线数组传给 MATLAB 的引擎接口,算完再把信号向量写回终端。外汇与贵金属杠杆高、滑点随机,跨进程延迟可能让信号失效,验证前先用历史数据跑通链路再上模拟盘。

「把 MATLAB 计算力接进 MQL5 的底层思路」

这篇内容延续了 A.Emelyanov 早先关于 MT4 与 MATLAB 交互的思路,但目标平台换成了 64 位环境下的 MT5 与 MATLAB 2018。原方法在现在跑不通,因为 MATLAB 包里生成共享库的方式升级了,必须用 MATLAB Compiler SDK 或 MATLAB Coder,老旧的 MATLAB Compiler 已不适用。 动态内存的处理逻辑也变了。MATLAB 现在对矩阵内存的管理和早年不同,所以源码层面要调整,才能正确调用用 MATLAB 语言写出来的库。 实操上,文章选了季节自回归综合移动平均(SARIMA)模型做例子:在 MATLAB 端完成模型选择和外推,再通过一个 Visual C++ 写的适配器库,把计算结果喂给 MQL5 指标。这样开发者能避开常见的内存对齐和调用错误。 数据通路分两层:先用一组 MATLAB 模块编出基于 SARIMA 的 C++ 共享库;再写一个中间库,专门解决 MQL5 的 C/C++ 风格内存和 MATLAB 的矩阵内存之间的传输问题。基础数据类型沿用早先文章定义,没改,差异只出现在从 MATLAB 出 C++ 库这一步。外汇与贵金属市场高杠杆、高波动,这类跨语言预测模块仅作概率参考,实盘前务必在 MT5 策略测试器里验证。

◍ 从 MATLAB 函数打包 C++ 共享库的实际路径

  • 年之后,MATLAB 里再想给 MQL5 做 DLL 就不能用老编译器了。原本的 MATLAB Compiler 改为只产自主可执行文件,建库职能挪到了 MATLAB Compiler SDK——它能在不装 MATLAB 的环境里,把 .m 程序编成 C/C++ 共享库、.NET 程序集或 Java 类。

用 SDK 打出来的库可以免费分发,前提是目标机有 MATLAB Runtime 和那组共享依赖。文章附带的 zip 里放了 .m 源文件和 LibSARIMA.prj,你在 MATLAB 的 APPS 选项卡启动 Library Compiler,打开这个项目就能看到图 1 那种界面。 界面上 1–4 号标注位是关键:ForecastSARIMA.m 里的函数被设为导出,其余函数不对外暴露;打包会生成 matlabsarima.dll、.h、.lib 三件套,矩阵交互走 mwArray 结构。点 Package 时可选让终端用户联网拉 MATLAB 引擎,或把引擎组件直接打进包里。 做完这一步,你就拿到了一个含时间序列滤波、SARIMA 建模与预测的共享库。外汇与贵金属行情受宏观与流动性冲击,用这类模型外推只代表历史结构下的概率倾向,实盘前务必在 MT5 用真实点差回测。

用 VC++ 搭桥让 MT5 内存对接 MATLAB 矩阵

主库就绪后,真正的难点是把 MQL5 那套 C/C++ 风格的内存布局,转成 MATLAB 认的矩阵内存。中间必须再编一个适配器 DLL,专门干数据来回搬运的脏活。 MATLAB x64 新版本自带 Visual C++ 编译器组件,所以最稳的方案是直接上 Visual Studio 2017 写这个 C++ 适配器,省去配环境的坑。

  • 版引入的 mwArray API 是关键,它允许把 MATLAB 函数打包嵌进 C++ 程序;老 mxArray 已升级为新矩阵接口。MATLAB Data API 也能集成共享库,但在这套 MT5 调用链路里用不上。

系统要先注册三个环境变量:MATLAB_2018 指向 MATLAB 或运行时根目录;MATLAB_2018_LIB64 指向 <MATLAB_2018>\extern\lib\win64 的外部库目录;MATLIB_USER 指向你放原始库的目录。 最后把 MATLIB_USER 那个目录再塞进系统 Path,否则运行时搜不到用户库会直接加载失败。外汇与贵金属自动化链路涉及 DLL 调用,属高风险操作,参数错乱可能击穿账户。

「VS2017里把MATLAB预测库接进MT5适配器」

在 Visual Studio 2017 建好 DLL 工程后,先改五处属性:头文件目录、库文件目录、预处理器宏、调用约定(按 MQL5 要求走)、附加依赖项里挂上 MATLAB 产出的 *.lib。图4 里要把 $(MatLib_User) 加进 Library Directories,这个系统变量目录专门放 matlabsarima.lib 和 matlabsarima.dll 两个共用件。 matlabsarima.h 必须丢进工程组装目录并挂到“头文件”里;两个由 MATLAB 生成的文件放到系统变量 $(MATLIB_USER) 指向的共享目录。解压包里给了重编 adaptersarima.dll 的全部中间文件,真要排错就去 C:\temp 解开重编。 AdapterSArima.h 靠宏把 prepareSARIMA()、closeSARIMA()、goSarima(...) 标成可供外部绑定的接口;goSarima 默认周期 iSeasonPriod=28、预测 npredict=25,这两个值直接决定 SARIMA 在 MT5 里的采样节奏和前瞻长度。 下面这段是头与初始化cpp的原文,逐行看宏怎么切换导出/导入,以及 MCR 引擎起不来时为什么返回 0: #pragma once 防止重复包含 #ifdef ADAPTERSARIMA_EXPORTS 编 DLL 时开这个宏 #define _DLLAPI extern "C" __declspec(dllexport) 导出给链接器配对 lib #else 别人调用时走这分支 #define _DLLAPI extern "C" __declspec(dllimport) 绑 DLL 用 #endif _DLLAPI int prepareSARIMA(void); 初始化入口 _DLLAPI int goSarima(double *Res, double *DataArray, int idx0, int nLoad, int iSeasonPriod=28, int npredict=25, int filterOn=1, int PlotOn=0); 核心计算,默认季周期28、预测25点 _DLLAPI void closeSARIMA(void); 释放 #pragma once 再次防护 #include "stdafx.h" 预编译头 #include "matlabsarima.h" MATLAB库头 #include "AdapterSArima.h" 本适配器头 bool SArimaStarted=false; 库是否起好 bool MLBEngineStarted=false; MCR是否起好 _DLLAPI int prepareSARIMA(void) 实现 if (!MLBEngineStarted) 未起MCR MLBEngineStarted = mclInitializeApplication(nullptr, 0); 初始化Matlab运行时 if (!MLBEngineStarted) 失败 std::cerr << "无法初始化 Matlab Runtime (MCR)" << std::endl; 报错 return 0; 直接退出 if (!SArimaStarted) 未起算法库 try SArimaStarted = matlabsarimaInitialize(); 初始SARIMA if (!SArimaStarted) 失败报“无法初始化库属性”并返回false return (SArimaStarted)?1:0; 成功回1

MQL5 / C++
class="macro">#pragma once
class="macro">#ifdef ADAPTERSARIMA_EXPORTS
class="macro">#define _DLLAPI class="kw">extern "C" __declspec(dllexport)  class=class="str">"cmt">// 此定义对于将DLL和LIB库配对是必需的
class="macro">#else
class="macro">#define _DLLAPI class="kw">extern "C" __declspec(dllimport)  class=class="str">"cmt">// 绑定DLL库需要此定义
class="macro">#endif
_DLLAPI class="type">int prepareSARIMA(class="type">void);
_DLLAPI class="type">int goSarima(class="type">class="kw">double *Res, class="type">class="kw">double *DataArray, class="type">int idx0, class="type">int nLoad, class="type">int iSeasonPriod = class="num">28, class="type">int npredict = class="num">25, class="type">int filterOn = class="num">1, class="type">int PlotOn = class="num">0);
_DLLAPI class="type">void closeSARIMA(class="type">void);
class="macro">#pragma once
class="macro">#include "stdafx.h"
class="macro">#include "matlabsarima.h"
class="macro">#include "AdapterSArima.h"
class="type">bool  SArimaStarted = class="kw">false;
class="type">bool  MLBEngineStarted = class="kw">false;
class=class="str">"cmt">//-----------------------------------------------------------------------------------
_DLLAPI class="type">int prepareSARIMA(class="type">void)
{
        if (!MLBEngineStarted)
        {
            MLBEngineStarted = mclInitializeApplication(nullptr, class="num">0);
            if (!MLBEngineStarted)
            {
                std::cerr << "无法初始化 Matlab Runtime(MCR)" << std::endl;
                class="kw">return class="num">0;
            }
        }       
        if (!SArimaStarted)
        {
            try
            {
                SArimaStarted = matlabsarimaInitialize();
                if (!SArimaStarted)
                {
                    std::cerr << "无法初始化库属性" << std::endl;
                    class="kw">return class="kw">false;
                }                               
                class="kw">return (SArimaStarted)?class="num">1:class="num">0;
            }

常见问题

把 MATLAB 函数用库编译器打包成 C++ 共享库,再写适配器让交易端加载调用,可省去中间文件来回倒。
用 VC++ 做桥接层,把 MATLAB 矩阵通过内存映射交给看盘端,避免每次落盘读取拖慢速度。
小布可接入外部计算输出,把 MATLAB 因子结果以 overlay 形式画在对应品种图上看背离。
多因运行时库版本和 MATLAB 自带 CRT 不匹配,统一用相同工具集并勾选兼容 MT 运行库即可过。
主要是数据类型对齐、矩阵维序转置和异常抛回,建议先单函数跑通再扩到多因子。