添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
当传递任意类型的数组时,您需要知道:类型(维度)和元素数量,这通过使用 ArraySize() 函数获得。应特别注意 MetaTrader 5 中的索引编排 - 通常它是向后的(即,第一个元素包含的数据比后面的元素包含的要新)。使用 ArrayIsSeries() 函数检查这一点。而 MATLAB 具有如下的索引编排:第一个元素包含的数据比后面的元素包含的要旧 - 因此,如果标志 AS_SERIES 为 TRUE,您必须在发送数组至 MATLAB 前将其“反转”。基于上述内容,我们得出以下观点:
  • “反转”数组对于 MQL5 程序“不可见”,字符类型的数组和二维数组除外 - 让它们保持不变。
  • 反转对于 MATLAB 的所有数组“不可见”,并将 TRUE 分配给 AS_SERIES 标志,字符类型的数组和二维数组除外 - 让它们保持不变。
  • 在 MQL5 程序中,在根据“向后”索引编排创建的每个数组中 AS_SERIES 标志必须为 TRUE,字符类型的数组和二维数组除外 - 让它们保持不变。   此连接外部程序和 MATLAB 包的方法是开发人员所推荐的。接口由六个函数组成:

    Engine *pEng = engOpen(NULL) - 此函数调用 MATLAB 桌面,其参数始终为 NULL,并返回一个指向桌面描述符的指针。

    int exitCode = engClose(Engine *pEng) - 此函数关闭桌面,返回 MATLAB 桌面的剩余用户数量,其中:
  • Engine *pEng - 指向桌面描述符的指针。 void = mxDestroyArray(mxArray *mxVector) - 此函数用于销毁 MATLAB 矩阵,它是清除内存所必需的,其中:
  • mxArray *mxVector - 指向矩阵变量的指针。 int = engPutVariable(Engine *pEng, char *Name, mxArray *mxVector) - 此函数发送变量至桌面。您不仅要创建 mxArray 类型的变量,还要把它们发送至 MATLAB,其中:
  • Engine *pEng - 指向桌面描述符的指针。
  • char *Name - MATLAB 桌面字符类型的变量名。
  • mxArray *mxVector - 指向矩阵变量的指针。 mxArray *mxVector = engGetVariable(Engine *pEng, char *Name) - 此函数从桌面获取变量 - 是上一函数的逆函数。仅接受 mxArray 类型的变量,其中:
  • mxArray *mxVector - 指向矩阵变量的指针。
  • Engine *pEng - 指向桌面描述符的指针。
  • char *Name - MATLAB 桌面字符类型的变量名。 double *p = mxGetPr(mxArray *mxVector) - 此函数获取指向值数组的指针,它用于复制数据和 memcpy()(请参见章节 2.3“C++ 标准输入/输出库”),其中:
  • double *p - 指向双精度类型数组的指针。
  • mxArray *mxVector - 指向矩阵变量的指针。 int = engEvalString(Engine *pEng, char *Command) - 此函数发送命令至 MATLAB 桌面,其中:
  • Engine *pEng - 指向桌面描述符的指针。
  • char *Command - MATLAB 命令,字符类型字符串。   您可能已注意到,MATLAB Engine API 仅允许您创建双精度类型的 mxArray 结构。但该限制不会影响您的可能性,而是将影响库的算法。

    MCR (MCR instance) - 是 MATLAB 包的特殊库,可运行 MATLAB 环境在任何计算机上生成的独立应用程序/公用库。请注意,即使您有完成的 MATLAB 包,您仍需通过运行位于 <MATLAB>\Toolbox\compiler\deploy\win32 文件夹的 MCRInstaller.exe 文件安装 MCR 库。因此,在调用由 MATLAB 环境创建的任何公用库函数前,您需要调用 MCR 初始化函数:

    bool = mclInitializeApplication(const char **option, int count) – 如果 MCR 成功启动,返回 TRUE,否则返回 FALSE,其中:
  • const char **option - 选项字符串,与 mcc - R 中的类似;通常为 NULL
  • int count - 大小选项字符串,通常为 0。
    使用标准输入/输出库提供正确的数据复制。它的使用将使您从程序设计阶段生成的“愚蠢”错误中解放出来(例如: 很多新手编程人员仅复制指针到内存块而不是复制整个内存块 )。就整个输入/输出库而言,我们只对其中的一个函数感兴趣:

    void *pIn = memcpy(void *pIn, void *pOut, int nSizeByte) – 此函数从 pOut 复制(克隆)变量/数组至 pIn,复制大小为 nSizeByte 字节,其中:
  • void *pIn - 指向复制目的地数组的指针。
  • void *pOut - 指向复制目标的数组的指针。
  • int nSizeByte - 待复制数据的大小,该大小不能超出 pIn 数组的大小,否则将发生内存访问错误。   我们现在来讨论算法。我们将从 MQL5 功能块开始。细心的读者已注意到,它的重点是实施章节 “MATLAB 和 MQL5 中的数据类型” 中编写的内容。如果您跳过了该部分内容,您将很难理解为什么所有这些是必要的。
    mlInput <variable_type> 函数的算法几乎是相同的。我们使用 mlInputDouble() 函数来讨论它的工作情况,前者为 MATLAB 虚拟机提供 double 类型的变量的输入。
    原型请见此处:
    bool mlInputDouble(double &array[],int sizeArray, string NameArray) ,其中:
    array - double 类型变量或数组的引用。
    sizeArray - 数组大小(元素数量,而非字节数!)。
    NameArray - 字符串,包含 MATLAB 虚拟机的唯一变量名(名称必须符合 MATLAB 的要求)。
    使用 ArrayIsSeries() 函数 检查 索引类型。如果索引类型正确 - 将值传递到 mlxInputDouble() 函数。
    时序数组的其他索引:
    “反转”数组并将值传递到 mlxInputDouble() 函数。
  • 结束函数,将返回的值传递到 mlxInputDouble() 函数。 C/C++ 功能块要简单得多 - 它应提供从 double 类型到 mxArray 结构的数据转换。这通过 mxCreateDoubleMatrix() mxGetPr() memcpy() 函数完成。然后,它使用 engPutVariable() 函数将数据传递至 MATLAB 虚拟机,而提取数据则是使用 engGetVariable() 函数。再次注意具有前缀 Int Logical 的函数 - 如功能块示意图中所示,它们没有直接与 MATLAB 交互,而是通过 mlxInputDouble/mlxGetDouble mlxInputChar() 函数进行。它们行为的算法十分简单:调用 mlxInputDouble/mlxGetDouble 函数 - 将值作为 double(!) 输入/输出并发送“影子”MATLAB 命令以通过 mlxInputChar() 函数转换数据类型。 一个如下所述的常见问题可能会出现: “我有不同版本的编译器,或列表中没有这种编译器!(没有这些文件的情形十分少见)” 。我们来看一下如何手动创建公用库。我们将考虑在 Visual C++ 和 Borland C++ 中它是如何实现的:
  • 在 FAR 中打开 <MATLAB>\Bin\<win32/64> 文件夹,其中:
    <MATLAB> - MATLAB 主文件夹。
    <win32/64> - 用于 32 位操作系统的 win32 文件夹,或用于 64 位操作系统的 win64 文件夹。 对于 Borland C++ ,输入:implib libeng.lib libeng.dll。对于 libmx.dll 也是一样。 对于 Visual C++ ,输入:lib libeng.dll。对于 libmx.dll 也是一样。 其他编译器 :任何编程语言的任何编译器必须具有该实用程序 - 库管理器,通常这是一个控制台程序 <compiler _folder>\bin\*lib*.exe。
    //+------------------------------------------------------------------
    //| 引入函数的声明                                 |
    //+------------------------------------------------------------------
    #import "LibMlEngine.dll"
    void   mlxClose(void);                        //void – 意味着:不要传入任何参数!
    bool   mlxOpen(void);                         //void – 意味着: 不要传递和接收任何参数!
    bool   mlxInputChar(char &CharArray[]);       //char& CharArray[] – 意味着: 传递引用
    bool   mlxInputDouble(double &dArray[],
                          int sizeArray,
                          char &CharNameArray[]);
    bool   mlxInputInt(double &dArray[],
                       int sizeArray,
                       char &CharNameArray[]);
    bool   mlxInputLogical(double &dArray[],
                           int sizeArray,
                           char &CharNameArray[]);
    int    mlxGetDouble(double &dArray[],
                        int sizeArray,
                        char &CharNameArray[]);
    int    mlxGetInt(double &dArray[],
                     int sizeArray,
                     char &CharNameArray[]);
    int    mlxGetLogical(double &dArray[],
                         int sizeArray,
                         char &CharNameArray[]);
    int    mlxGetSizeOfName(char &CharNameArray[]);
    #import    
        
    #property copyright "2010, MetaQuotes Software Corp."
    #property link      "https://www.mql5.com/ru"
    #property version   "1.00"
    #import "MatlabEngine.ex5"
    bool mlOpen(void);
    void mlClose(void);
    bool mlInputChar(string array);
    bool mlInputDouble(double &array[],
                       int sizeArray,
                       string NameArray);
    bool mlInputInt(int &array[],
                    int sizeArray,
                    string NameArray);
    int mlGetDouble(double &array[],
                    string NameArray);
    int mlGetInt(int &array[],
                 string NameArray);
    bool mlInputLogical(bool &array[],
                        int sizeArray,
                        string NameArray);
    int mlGetLogical(bool &array[],
                     string NameArray);
    int mlGetSizeOfName(string strName);
    #import
    void OnStart()
    // 用于存储MATLAB输出的动态缓存
       double dTestOut[];
       int    nTestOut[];
       bool   bTestOut[];
    // 用于 MATLAB 输入的变量
       double dTestIn[] = {   1,     2,    3,     4};
       int    nTestIn[] = {   9,    10,   11,    12};
       bool   bTestIn[] = {true, false, true, false};
       int nSize=0;
    // 变量名和命令行
       string strComm="clc; clear all;"; // 命令行 - 清除屏幕和变量
       string strA     = "A";            // 变量 A
       string strB     = "B";            // 变量 B
       string strC     = "C";            // 变量 C
       ** 1. 运行 DLL
       if(mlOpen()==true)
          printf("MATLAB has been loaded");
          printf("Matlab ERROR! Load error.");
          mlClose();
          return;
       ** 2. 传输命令行
       if(mlInputChar(strComm)==true)
          printf("Command line has been passed into MATLAB");
       else printf("ERROR! Passing the command line error");
       ** 3. 传输 double 类型的变量
       if(mlInputDouble(dTestIn,ArraySize(dTestIn),strA)==true)
          printf("Variable of the double type has been passed into MATLAB");
       else printf("ERROR! When passing string of the double type");
       ** 4. 获取 double 类型的变量
       if((nSize=mlGetDouble(dTestOut,strA))>0)
          int ind=0;
          printf("Variable A of the double type has been got into MATLAB, with size = %i",nSize);
          for(ind=0; ind<nSize; ind++)
             printf("A = %g",dTestOut[ind]);
       else printf("ERROR! Variable of the double type double hasn't ben got");
       ** 5. 传输 int 类型的变量
       if(mlInputInt(nTestIn,ArraySize(nTestIn),strB)==true)
          printf("Variable of the int type has been passed into MATLAB");
       else printf("ERROR! When passing string of the int type");
       ** 6. 获取 int 类型的变量
       if((nSize=mlGetInt(nTestOut,strB))>0)
          int ind=0;
          printf("Variable B of the int type has been got into MATLAB, with size = %i",nSize);
          for(ind=0; ind<nSize; ind++)
             printf("B = %i",nTestOut[ind]);
       else printf("ERROR! Variable of the int type double hasn't ben got");
       ** 7. 传输 bool 类型的变量
       if(mlInputLogical(bTestIn,ArraySize(bTestIn),strC)==true)
          printf("Variable of the bool type has been passed into MATLAB");
       else printf("ERROR! When passing string of the bool type");
       ** 8. 获取 bool 类型的变量 E
       if((nSize=mlGetLogical(bTestOut,strC))>0)
          int ind=0;
          printf("Variable C of the bool type has been got into MATLAB, with size = %i",nSize);
          for(ind=0; ind<nSize; ind++)
             printf("C = %i",bTestOut[ind]);
       else printf("ERROR! Variable of the bool type double hasn't ben got");
       ** 9. 结束
       mlClose();
    Exported m-functions – void mlf<NameMfile>(int <number_of_return_values>,mxArray **<return_values>,mxArray *<input_values>, ...),其中:
  • <number_of_return_values> - 返回变量的数量(请勿与数组大小等混淆)。
  • mxArray **<return_values> - mxArray 结构的地址,m 函数工作的结果将返回此处。
  • mxArray *<input_values> - 指向 m 函数输入变量的 mxArray 结构的指针。 #include "mclmcr.h" #include "NEOSMA.h" /*--------------------------------------------------------------------------- ** DLL 全局变量(外部的) extern "C" __declspec(dllexport) bool __stdcall IsStartSMA(void); extern "C" __declspec(dllexport) bool __stdcall nSMA(double *pY, int nSizeY, double *pIn, int nSizeIn, double dN, double dAd); /*--------------------------------------------------------------------------- ** 全局变量 mxArray *TempY; mxArray *TempIn; mxArray *TempN; mxArray *TempAd; bool bIsNeoStart; //--------------------------------------------------------------------------- int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved) switch(reason) case DLL_PROCESS_ATTACH: bIsNeoStart = false; TempY = 0; //清除指向缓存的指针 TempN = 0; TempIn = 0; TempAd = 0; break; case DLL_PROCESS_DETACH: NEOSMATerminate(); //在从DLL中退出之前,删除旧数据 if(TempY != NULL) mxDestroyArray(TempY); if(TempN != NULL) mxDestroyArray(TempN); if(TempIn != NULL) mxDestroyArray(TempIn); if(TempAd != NULL) mxDestroyArray(TempAd); mclTerminateApplication(); return 1; //--------------------------------------------------------------------------- bool __stdcall IsStartSMA(void) if(bIsNeoStart == false) if(!mclInitializeApplication(NULL,0) ) MessageBoxA(NULL, (LPSTR)"Can't start MATLAB MCR!", (LPSTR) "MATLAB DLL: ERROR!", MB_OK|MB_ICONSTOP); return false; }else bIsNeoStart = NEOSMAInitialize(); return bIsNeoStart; //--------------------------------------------------------------------------- bool __stdcall nSMA(double *pY, int nSizeY, double *pIn, int nSizeIn, double dN, double dAd) ** 创建缓存 if(TempN == NULL){ TempN = mxCreateDoubleMatrix(1, 1, mxREAL);} mxDestroyArray(TempN); TempN= mxCreateDoubleMatrix(1, 1, mxREAL); if(TempIn == NULL){ TempIn = mxCreateDoubleMatrix(1, nSizeIn, mxREAL);} mxDestroyArray(TempIn); TempIn= mxCreateDoubleMatrix(1, nSizeIn, mxREAL); if(TempAd == NULL){ TempAd = mxCreateDoubleMatrix(1, 1, mxREAL);} mxDestroyArray(TempAd); TempAd= mxCreateDoubleMatrix(1, 1, mxREAL); ** 创建用于处理的数据 memcpy((char *)mxGetPr(TempIn), (char *) pIn, (nSizeIn)*8); memcpy((char *)mxGetPr(TempN), (char *) &dN, 8); memcpy((char *)mxGetPr(TempAd), (char *) &dAd, 8); ** 发送并从m函数中接收一个响应 if(mlfNeoSMA(1, (mxArray **)TempY, (mxArray *)TempIn, (mxArray *)TempN , (mxArray *)TempAd) == false) return false; ** 从m函数中返回计算后的向量并清除缓存 memcpy((char *) pY, (char *)mxGetPr(TempY), (nSizeY)*8); mxDestroyArray((mxArray *)TempY); TempY = 0; mxDestroyArray((mxArray *)TempN); TempN = 0; mxDestroyArray((mxArray *)TempIn); TempIn = 0; mxDestroyArray((mxArray *)TempAd); TempAd = 0; return true; nSMA.def(从 DllMatlab.zip):
    #property copyright "2010, MetaQuotes Software Corp."
    #property link      "nav_soft@mail.ru"
    #property version   "1.00"
    #import "nnSMA.dll"
    bool  IsStartSMA(void);
    bool  nSMA(double &pY[],
               int nSizeY,
               double &pIn[],
               int nSizeIn,
               double dN,
               double dAd);
    #import
    datetime    Time[];    // 时间坐标的动态数组
    double      Price[];   // 价格的动态数组
    double      dNeoSma[]; // 价格的动态数组
    void OnStart()
       int ind=0;
    // 运行 Dll
       if(IsStartSMA()==true)
          //--- 创建并填充数组
          CopyTime(Symbol(),0,0,301,Time);   // 时间数组 + 1
          ArraySetAsSeries(Time,true);       // 获取时间图表
          CopyOpen(Symbol(),0,0,300,Price);  //价格数组
          ArraySetAsSeries(Price,true);      // 获取开盘价
          ArrayResize(dNeoSma,300,0);        // 用于函数响应的保留空间
                                             // 获取数据
          if(nSMA(dNeoSma,300,Price,300,1,2)==false) return;
          // 指定数组的方向
          ArraySetAsSeries(dNeoSma,true);
          // 在图表上绘制数据
          for(ind=0; ind<ArraySize(dNeoSma);ind++)
             DrawPoint(IntegerToString(ind,5,'-'),Time[ind],dNeoSma[ind]);
    //+------------------------------------------------------------------
    void DrawPoint(string NamePoint,datetime x,double y)
      {  // 100% 准备好了。在图表上绘制数据使用箭头绘图。
    // 图表对象的主要属性
       ObjectCreate(0,NamePoint,OBJ_ARROW,0,0,0);
       ObjectSetInteger(0, NamePoint, OBJPROP_TIME, x);        // 时间坐标x
       ObjectSetDouble(0, NamePoint, OBJPROP_PRICE, y);        // 价格坐标y
    // 图表对象的附加属性
       ObjectSetInteger(0, NamePoint, OBJPROP_WIDTH, 0);       // 线宽
       ObjectSetInteger(0, NamePoint, OBJPROP_ARROWCODE, 173); // 箭头类型
       ObjectSetInteger(0, NamePoint, OBJPROP_COLOR, Red);     // 箭头颜色
    //+------------------------------------------------------------------
    
  •