![MеtaTrader 4 和 MATLAB Engine 的交互(虚拟 MATLAB 机)](https://c.mql5.com/i/articles/overlay.png)
MеtaTrader 4 和 MATLAB Engine 的交互(虚拟 MATLAB 机)
简介
MetaTrader 4 和 MATLAB 数学包由于其良好的特性(包括在创建复杂计算系统中的“灵活性”),非常受用户的欢迎。 MATLAB 跟外部应用程序连接有三种主要方式,但我只推荐其中的一种 - 使用虚拟桌面 MATLAB Engine。 这种方法保证跟完整的 MATLAB 包完全兼容。 很多程序员出于以下原因回避这种方法:
- 很多用户发现它较慢。 如果跟从 MATLAB 的 DLL 库直接调用函数相比,的确比较慢。 主要延迟发生在调用虚拟机的操作开始时,因为调用大量的库,需要将其上传到调用过程的虚拟空间(本例中是 MetaTrader 4)。
- 项目的可转移性 这种问题的确存在,当把项目转移到另一台计算机时(以及使用直接调用时),所有的 MATLAB DLL 库也必须转移,以了解后者(即启动队列)之间的“关系”。
- 必须了解 C++ 或 Fortran。 不过,如果你了解 MQL4,就可以轻松学习 C++,反之亦然。
我推荐这种方法的原因:
- 这是跟外部程序连接的方法中最可靠和不受 MATLAB 版本影响的一个。 你可以更改 MATLAB 的版本,而你的指标或 Expert Advisor 根本不会注意到这点。 这是最重要的优势。
- 它具有相对快速的开发方法。 不需要调试程序,并且编写 DLL 包装程序也不困难。
- 多个指标和/或 Expert Advisor 的“普通桌面”。 当我们需要基于多个指标的数据进行决策或在实现金字塔交易时,我认为这种方法非常有用。
本文描述了连接 MetaTrader 4 和 MATLAB(ver.7.4.0(R2007a))的方法, 通过在 Borland C++ Builder 6 编写的“DLL 包装程序”来实现。 喜欢 Microsoft 产品的程序员必须使示例适应他们的编译器(考虑到任务的复杂性,祝你好运!)
I.设定任务
首先,需要确定从哪里着手启动项目。 我们将开发过程分为三部分:
- 开发 MATLAB 中实现指标/EA 计算的 M-函数。
- 开发连接 MATLAB 和 MetaTrader 4 的“DLL-包装程序”。
- 开发 MQL 程序。
II. 开发 M-函数
这可能是最有趣和运行时间较长的过程,包含以下操作:
1. 将数据从 MetaTrader 4 预导出到 MATLAB。
上图显示了手动将数据导出到 MATLAB 的过程。 当导出结束时,在 MATLAB 桌面将创建变量。
2. 查找正确的公式、公式参数的范围等等。
该过程很有创造性,也很重要,但指标和/或 Expert Advisor 的数学算法开发并非本文讨论的内容。 你可以在关于 MATLAB 的文献中找到相关信息。
3. MATLAB 中的 M-函数创建
了解 C++和/或 MQL4 的程序员在创建该函数时都没有困难 - 另外,所有的变量都具有相同的数据类型 - “矩阵”。 也就是说,清晰的定义一个变量为数组还是多维数组并不重要 - 语言自己会完成定义。 我认为数据类型的选择过程并不重要。 我始终使用 mxREAL。 虽然使用了更多的内存,但却没有造成任何困惑。 更多详情可见于参考文献 1 和 2。 在给出的示例中,使用了高频过滤器。
III. 开发“DLL-包装程序”
我们对这一点进行详细的阐述,因为它就像空气之于人一样重要。 每个后期绑定的 DLL 库必须符合以下条件:
- 必须具有用于运行后进行“废品”收集和清除内存的内部函数。
- 必须支持多线程,即同时支持超过一个线程的运行。
- 必须位于一定的目录下,请参阅:项目文件的位置。
“DLL-包装程序”的主要外部函数是 MATLAB Engine 的 API 界面和标准 C++ 输入/输出库的一个函数。 MATLAB Engine MATLAB Engine ,只包含 8 个函数:
Engine *pEng = engOpen(NULL) – 调用 MATLAB 桌面的函数,该参数始终为 NULL,返回指针到桌面描述符,它对于其他函数的运行是不可缺少的,变量被设定为全局变量。
int exitCode = engClose(Engine *pEng) – 关闭桌面的函数,到桌面描述符的 pEng 指针,返回一个不重要的值,因为该函数在 DLL 关闭时调用且并不重要,返回 MATLAB 桌面“用户”的数量。
mxArray *mxVector = mxCreateDoubleMatrix(int m, int n, int ComplexFlag) – 为 MATLAB 桌面创建矩阵的函数,返回指针到变量矩阵。 需要创建跟 MATLAB 兼容的变量。 一般的数据数组和/或简单的数据类型无法发送到 MATLAB!
mxArray *mxVector – 指向变量矩阵的指针;
int m – 行数;
int m – 列数;
ComplexFlag – 复数类型,为了 MetaTrader 4 的正确运行,始终使用 mxREAL。
void = mxDestroyArray(mxArray *mxVector) – 擦除 MATLAB 矩阵的函数,为清除内存所需。 经常删除不再需要的数据,否则会出现内存问题或结果的“重叠”。
mxArray *mxVector – 指向变量矩阵的指针;
int = engPutVariable( Engine *pEng, char *Name, mxArray *mxVector) – 发送变量到桌面的函数。 不能只是创建 mxArray 类型的变量,而且要发送到 MATLAB。
Engine *pEng – 指向桌面“描述符”的指针;
char *Name – 在 MATLAB 桌面的变量名称,char 类型;
mxArray *mxVector – 指向变量矩阵的指针;
mxArray *mxVector = engGetVariable(Engine *pEng, char *Name) – 从桌面接受变量的函数,是上一个函数的逆函数。 可以接收 mxArray 类型的变量。
mxArray *mxVector – 指向变量矩阵的指针;
Engine *pEng – 指向桌面“描述符”的指针;
char *Name – MATLAB 桌面字符类型的变量名称。
double *p = mxGetPr(mxArray *mxVector) – 接收指向数据数组指针的函数,用于复制数据以及 memcpy(…)。 在接收/写入 mxArray 类型的变量时,使用该函数提取/粘贴一个简单类型(int、double...)的变量。
double *p – 指向双精度类型数组的指针;
mxArray *mxVector – 指向变量矩阵的指针;
int = engEvalString(Engine *pEng, char *Command) – 发送命令到桌面的函数。 Command 行中 的命令将由 MATLAB 桌面执行。
Engine *pEng – 指向桌面“描述符”的指针;
char *Command – MATLAB 命令,字符类型行。
只有一个使用内存的函数:
void *pIn = memcpy (void *pIn, void *pOut, int nSizeByte) – 将 pOut 变量(数组)复制(克隆)至大小为 nSizeByte 字节的 pIn 变量的函数。
注意: 留意数组的维度。 它们必须相等,否则 pIn 数组应大于 pOut。
“DLL-包装程序”导出函数的要求
为了使 MetaTrader 4 能够使用 MATLAB,应该编写函数传递程序。 我们来看一下构建这种函数的要求。 从 MetaTrader 4 调用的任何函数必须是 __stdcall,即通过堆栈传递参数,函数清除堆栈。 下面是函数的声明方式:
extern "C" __declspec(dllexport) <variable_type> __stdcall Funcion(<type> <name>);
extern "C" __declspec(dllexport) - 告诉 C++ 编译器函数为外部函数,在导出表中编写。
<variable_type> - 要返回变量的类型,可以是:void、bool、int、double,复合类型和指针无法传递;查看更多;
__stdcall – 关于参数传递到函数和返回的协议;
Function – 你的函数名称;
<type> <name> - 输入变量的类型和名称;变量的最大数量是 64。
这是函数定义的原型,另请注意 __stdcall
bool __stdcall Funcion (<type> <name>) { //…… }
除此之外,还要创建一个扩展名为 def 的文件。 这通常是一个文本文件,描述库名称和导出函数的名称。 如果该文件不存在,你的文件将会自己“编造”失真的函数名称,这样会使 DLL 的使用复杂化。 下面是文件示例:
LIBRARY NameDll EXPORTS NameFunctionA NameFunctionB
LIBRARY –指向 DLL 名称的助词。
EXPORTS – 助词,表示将枚举下面的函数名称。
NameFunctionA、NameFunctionB – DLL 函数的名称。
但 MQL 对其施加了限制: 因为该语言没有指针,它没有动态内存,所以无法从 DLL 库传递数组、结构等。 但在 MetaTrader 中,数据可以写成数组,通过函数的引用进行传递。 结果可以写在由 MetaTrader 创建的数组内,DLL 接收该数组的指针。 但该数组必须是某种维度,而且不能是指标线(该限制可能跟 MetaTrader 4 中特定的内存分配有关)。
现在,在了解了如何编写和调用哪些函数后,我们来看一个“DLL-包装程序”的典型算法:
1. 在第一次调用 DLL 时,使用 engOpen() 函数启动 MATLAB Engine;
2. 从 MetaTrader 获得数据并送回,DLL 函数;
2.1. 由 mxCreateDoubleMatrix() 函数创建变量;
2.2. 复制数据到 mxVector 变量;memcpy() 和 mxGetPr() 函数;
2.3. 传递变量到 MATLAB 桌面,engPutVariable() 函数;
2.4. 传递公式/代码到 MATLAB 桌面,engEvalString() 函数;
2.5. 接收 MATLAB 桌面的反馈,engGetVariable() 函数;
2.6. 将值返回 MetaTrader,memcpy() 和 mxGetPr() 函数;
3. 使用 engClose() 函数关闭 MATLAB,当从 MetaTrader 进程的地址区加载 DLL 时删除所有的 mxDestroyArray() 变量。
现在我们来创建“DLL-包装程序”的架构:
/*--------------------------------------------------------------------------- ** Libraries + *.lib + *.def: ** libeng.lib** libmx.lib ** libmex.lib** project_name.def */ #include <windows.h>#include <memory.h>#include "engine.h" //--------------------------------------------------------------------------- extern "C" __declspec(dllexport)<variable_type>__stdcall Funcion(<type><name>); //--------------------------------------------------------------------------- int WINAPI DllEntryPoint(HINSTANCE hinst,unsigned long reason,void *lpReserved) { /* ** reason for DLL call */ switch(reason) { case DLL_PROCESS_ATTACH: /* ** DLL uploaded into the address space of the process */ break; case DLL_PROCESS_DETACH: /* **DLL loaded from the address space of the process */ break; } return TRUE; } //--------------------------------------------------------------------------- bool __stdcall Funcion(<type><name>) { …… } //---------------------------------------------------------------------------
项目程序集
下图显示了如何将库和 *.def 文件添加到项目:
下面是“DLL-包装程序”项目所需的文件列表:
- libeng.lib – 位于: \Program Files\MATLAB\R2007a\extern\lib\win32\borland\
- libmx.lib – 位于: \Program Files\MATLAB\R2007a\extern\lib\win32\borland\
- libmex.lib – 位于: \Program Files\MATLAB\R2007a\extern\lib\win32\borland\
- имя_проекта.def – 该文件应如上所述在记事本中创建。
应将 engine.h 文件从 \Program Files\MATLAB\R2007a\extern\\include 复制到 \Program Files\Borland\CBuilder6\Include 文件夹 - 这样你就不必每次为编译器指定路径了。
注意: 这些说明仅限于在 Borland C++ Builder 6 中汇编项目!
IV. 开发一个 MQL4 程序
我们将考虑只跟“DLL 包装程序”函数的声明和参数传递相关的问题。 为了声明函数,需要下面的语言结构:
#import "HighPass.dll" void ViewAnsFilter(); bool TestDllFilter(); bool AdaptiveHighFilter(double& nInVector[], int nSizeVector, double nSizeWind, double dAmplit); void MakeBuffFilter(int nSize); void DestrBuffFilter(); #import
其中:
#import"HighPass.dll" – DLL 库的关键词和名称;
void MakeBuffFilter(int nSize); - 函数名称、要返回的值的类型、传递值的名称和类型。
注意! “[]” 在传递数组时使用, 如果 dll 对该数据数组写下反馈时,需要使用与号字符 "&" !对该数据数组写下反馈时,需要使用与号字符 “&” !在 MQL 4 中没有其他方式从外部程序传递数组! 要传递的数组必须具有特定的维度,且不能是指标数组!
V. 文件位置
在建立项目后,所有的项目文件都应正确放置:
*.dll 和 *.m - 目录中的库文件和m-函数位于 \Program Files\MetaTrader\experts\libraries;
*.mql 位于其正常位置,也就是说,如果它是一个指标,则位于‘indicators’文件夹,如果是 EA,则位于‘experts’,如果是脚本,则位于‘scripts’文件夹。
注意: 在启动指标或 Expert Advisor 时,可能会出现服务器繁忙的警告:
这种情况下,请等待 5-10 秒钟,直到工具栏出现 Console Matlab 并单击“重试”。
附言: 我有一个 512 RAM、Celeron M 2100 的笔记本电脑,在过滤器运行、图表数量为 5(总缓冲区为 500 х 8 х 5 = 20 000 字节大小)时没感觉到延迟。 所以,最终的选择在于你! 而我已经做出了选择。 如果出现延迟,在 MATLAB 中可以轻松实现分布式计算系统,也就是说,在连接到一个局域网的不同电脑上可以启动多个桌面。
参考文献列表
- Built-in MATLAB Help.
- "Matlab 5.х Calculations, Visualization, Programing" N.N. Martynov.
- C++ Builder 6. Reference Manual" A.Y. Arkhangelski.
- Built-in MQL4 Help.
总结
本文中,我们讨论了用于绑定 MetaTrader 4 和 MATLAB 数学包的“DLL-包装程序”开发的基础知识。 我们没有涉及提供多个指标和/或 Expert Advisor 运行的问题 - 将在下一篇文章中对其进行探讨。 附件中包含了因使用高频过滤器而改进过的 MACD。
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/1567
![图形界面III:简单与多功能按钮(第一章)](https://c.mql5.com/2/22/Graphic-interface_3__1.png)