MQL5 中的矩阵和向量操作(基础篇)
📘

MQL5 中的矩阵和向量操作(基础篇)

第 1/3 篇

◍ MQL5 里的矩阵与向量能干什么

MQL5 在语言层面加入了矩阵(matrix)和向量(vector)这两类特殊数据类型,专门用来处理一大类数值计算问题。它们不是靠外部库硬凑,而是语言原生支持,并带一组内置方法。 写代码时可以直接用接近数学记号的写法,比如矩阵相乘、转置、求逆,可读性比早年用数组手搓要高出一个层级。对于做多资产协方差、因子加权、批量回测参数矩阵的交易者,这套类型能明显压缩代码量。 官方帮助里的「矩阵和矢量方法」一节列出了全部内置方法签名与约束(如维度匹配规则)。想在 MT5 里验证,新建一个 EA 或脚本,直接声明 matrix m; 看编译器是否认,是最快确认你客户端版本支持与否的办法。外汇与贵金属杠杆高、滑点随机,任何数值模型都只是概率辅助,别当成确定性信号。

「从数组到矩阵:MQL5 为何另起炉灶」

传统 MQL5 数组按索引存取,一维和二维结构能覆盖多数行情数据存储需求。但一旦碰到线性代数类任务,嵌套循环会迅速拖垮代码可读性,最简单的矩阵乘法也得手写多层遍历。 现代机器学习、神经网络与 3D 图形都建立在向量和矩阵运算之上。MQL5 因此引入了 matrix 和 vector 专用类型,把常规数学操作封装掉,减少样板代码。 下面这段声明展示了老式数组的三种常见形态:50 个整数的向量、7×50 的二维矩阵、以及 100 个自定义结构的集合。 对于外汇与贵金属策略研发,这类底层类型差异直接影响回测脚本的维护成本,高杠杆品种下模型误差放大,风险显著。

MQL5 / C++
class="type">int    a[class="num">50];       class=class="str">"cmt">// One-dimensional array of class="num">50 integers
class="type">class="kw">double m[class="num">7][class="num">50];    class=class="str">"cmt">// Two-dimensional array of class="num">7 subarrays, each consisting of class="num">50 integers
MyTime t[class="num">100];      class=class="str">"cmt">// Array containing elements of MyTime type

MQL5 里的向量与矩阵到底是什么

在 MQL5 中,向量就是一维的 double 数组,矩阵则是二维的 double 数组。两者行、列索引均从 0 起算,和常规数组一致;矩阵可视为若干水平向量按行堆叠,第一维是行号、第二维是列号,水平或垂直向量在语法层面不作区分。 除基础 matrix、vector(双精度)外,语言还提供 matrixf、matrixc、vectorf、vectorc 四种变体,分别承载浮点与复数数据。截至本文撰写时,matrixc 与 vectorc 的内部实现尚未完工,尚不能在内置方法里直接调用。 模板写法 matrix<double>、vector<float> 可等价替代具名类型,方便泛型封装。下面这段演示了 vectorf 的初始化与模板向量互赋值,输出为 [0,1,2,3],可直接贴进 MT5 脚本验证。 外汇与贵金属行情波动剧烈,用矩阵批量处理多品种信号时须自行做好越界与精度校验,相关计算结论仅具概率意义。

MQL5 / C++
  vectorf       v_f1= {class="num">0, class="num">1, class="num">2, class="num">3,};  class=class="str">"cmt">// 声明浮点向量并用字面量初始化,含尾随逗号合法
  vector<class="type">class="kw">float> v_f2=v_f1;              class=class="str">"cmt">// 用模板写法声明同类型向量,直接拷贝 v_f1
  Print("v_f2 = ", v_f2);               class=class="str">"cmt">// 打印向量内容到日志
  class=class="str">"cmt">/*
  v_f2 = [class="num">0,class="num">1,class="num">2,class="num">3]
  */

◍ MT5里矩阵与向量的几种声明姿势

在 MT5 的 MQL5 环境里,矩阵(matrix)和向量(vector)按用途分了九大类,但创建方式主要就几类。最轻量的做法是不指定大小直接声明,只写类型和变量名,此时不分配内存,后续再改尺寸并填数,也能直接丢进内置矩阵方法里参与计算。 也可以按指定大小声明,括号里写行数和列数(向量写长度),这时内存分配了但元素不初始化,值是脏的。第三种是声明同时用大括号初始化,尺寸由花括号内的数字序列自动推断,比如双精度矩阵 {{0.1,0.2,0.3},{0.4,0.5,0.6}} 就是 2×3。 除了直接声明,还有静态方法能造出特定填充规律的矩阵:Eye(4,5,1) 生成 4 行 5 列、主对角为 1 的矩阵,Full(3,4,M_PI) 把 3×4 全填成 π,Identity 和 Ones、Zeros 也类似。非静态的 Init 和 Fill 则能在对象已存在后按给定值重填,Init 还能传初始化函数按规则生成元素,例如重设尺寸后用随机值铺满。 下面这段代码把上述几种写法都列了出来,注意 float 和 double 类型不能混用赋值,且向量长度参数可以是变量或表达式(如 InpSize+16)。开 MT5 新建脚本把这些贴进去编译,就能直观看到每种对象占用的内存和默认排布。

MQL5 / C++
  matrix        matrix_a;    class=class="str">"cmt">// class="type">class="kw">double type matrix
  matrix<class="type">class="kw">double> matrix_a1;   class=class="str">"cmt">// another way to declare a class="type">class="kw">double matrix, suitable for use in templates
  matrix<class="type">class="kw">float>  matrix_a3;   class=class="str">"cmt">// class="type">class="kw">float type matrix
  vector        vector_a;     class=class="str">"cmt">// class="type">class="kw">double type vector
  vector<class="type">class="kw">double> vector_a1;   class=class="str">"cmt">// another notation to create a class="type">class="kw">double vector
  vector<class="type">class="kw">float>  vector_a3;   class=class="str">"cmt">// class="type">class="kw">float type vector
  matrix        matrix_a(class="num">128,class="num">128);                    class=class="str">"cmt">// the parameters can be either constants
  matrix<class="type">class="kw">double> matrix_a1(InpRows,InpCols);          class=class="str">"cmt">// or variables
  matrix<class="type">class="kw">float>  matrix_a3(InpRows,class="num">1);                class=class="str">"cmt">// analogue of a vertical vector
  vector        vector_a(class="num">256);
  vector<class="type">class="kw">double> vector_a1(InpSize);
  vector<class="type">class="kw">float>  vector_a3(InpSize+class="num">16);               class=class="str">"cmt">// expression can be used as a parameter
  matrix        matrix_a={{class="num">0.1,class="num">0.2,class="num">0.3},{class="num">0.4,class="num">0.5,class="num">0.6}};
  matrix<class="type">class="kw">double> matrix_a1=matrix_a;                  class=class="str">"cmt">// the matrices must be of the same type
  matrix<class="type">class="kw">float>  matrix_a3={{class="num">1,class="num">2},{class="num">3,class="num">4}};
  vector        vector_a={-class="num">5,-class="num">4,-class="num">3,-class="num">2,-class="num">1,class="num">0,class="num">1,class="num">2,class="num">3,class="num">4,class="num">5};
  vector<class="type">class="kw">double> vector_a1={class="num">1,class="num">5,class="num">2.4,class="num">3.3};
  vector<class="type">class="kw">float>  vector_a3=vector_a2;                 class=class="str">"cmt">// the vectors must be of the same type
  matrix        matrix_a =matrix::Eye(class="num">4,class="num">5,class="num">1);
  matrix<class="type">class="kw">double> matrix_a1=matrix::Full(class="num">3,class="num">4,M_PI);
  matrixf       matrix_a2=matrixf::Identity(class="num">5,class="num">5);
  matrixf<class="type">class="kw">float> matrix_a3=matrixf::Ones(class="num">5,class="num">5);
  matrix        matrix_a4=matrix::Tri(class="num">4,class="num">5,-class="num">1);
  vector        vector_a =vector::Ones(class="num">256);
  vectorf       vector_a1=vector<class="type">class="kw">float>::Zeros(class="num">16);

「矩阵扩容后旧值残留的坑」

MT5 里 matrix 的 Fill 只覆盖已有维度,一旦用 Init 改尺寸,新扩出的区域不会自动清零。上面那段代码里,2×2 的矩阵 Fill(10) 后全是 10;Init(4,6) 扩成 4 行 6 列,打印出来第 0 行前 4 个还是 10,后面却混进了 0.0078125 和 32.00000762939453 这种脏数据,其余三行全是 0。 开 MT5 把下面代码贴进脚本跑一遍,就能复现这个现象。做向量化信号计算前,若依赖 Init 动态调整矩阵大小,得手动重填,否则后续点积或归一化会带进不可控偏差。 自定义初始化回调能绕开这个问题。例子里的 MatrixSetValues 以初始值 1 按行填 2 的倍数,3×6 矩阵跑出来就是 1 到 131072 的等比数列,旧内存不会被误用。外汇和贵金属行情高频刷新,矩阵维度常随品种数变动,这种写法在实盘里更稳,但杠杆品种波动剧烈,仍属高风险操作。

MQL5 / C++
  vector<class="type">class="kw">float>  vector_a2=vectorf::Full(class="num">128,float_value);
  matrix m(class="num">2, class="num">2);
  m.Fill(class="num">10);
  Print("matrix m \n", m);
  class=class="str">"cmt">/*
  matrix m
  [[class="num">10,class="num">10]
  [class="num">10,class="num">10]]
  */
  m.Init(class="num">4, class="num">6);
  Print("matrix m \n", m);
  class=class="str">"cmt">/*
  matrix m
  [[class="num">10,class="num">10,class="num">10,class="num">10,class="num">0.0078125,class="num">32.00000762939453]
  [class="num">0,class="num">0,class="num">0,class="num">0,class="num">0,class="num">0]
  [class="num">0,class="num">0,class="num">0,class="num">0,class="num">0,class="num">0]
  [class="num">0,class="num">0,class="num">0,class="num">0,class="num">0,class="num">0]]
  */
class="type">void OnStart()
{
class=class="str">"cmt">//---
  matrix init(class="num">3, class="num">6, MatrixSetValues);  
  Print("init = \n", init);
  class=class="str">"cmt">/*
  Execution result
  init =
  [[class="num">1,class="num">2,class="num">4,class="num">8,class="num">16,class="num">32]
   [class="num">64,class="num">128,class="num">256,class="num">512,class="num">1024,class="num">2048]
   [class="num">4096,class="num">8192,class="num">16384,class="num">32768,class="num">65536,class="num">131072]]
  */  
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Fills the matrix with powers of a number                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void MatrixSetValues(matrix& m, class="type">class="kw">double initial=class="num">1)
{
  class="type">class="kw">double value=initial;
  for(class="type">class="kw">ulong r=class="num">0; r<m.Rows(); r++)
   {
    for(class="type">class="kw">ulong c=class="num">0; c<m.Cols(); c++)
     {
      m[r][c]=value;
      value*=class="num">2;
     }
   }
}

数组灌进矩阵时尺寸与类型怎么变

在 MT5 里做向量化计算,经常要把一个普通数组直接塞进矩阵。除了 Copy 方法,更顺手的是赋值号 '=' 和 Assign 调用;区别是 Assign 在数组和矩阵两边都能用,不挑类型。 从 int 数组转到 double 矩阵,Assign 会顺手做两件事:按源数组的行列自动改目标矩阵大小,并把整型升成双精度。下面这段实测里,原矩阵是 2 行 10 列的 3.14 填充阵,Assign 一个 5×5 的 int 数组后,直接变成了 5 行 5 列、未赋值位补 0 的矩阵。 注意那个 5×5 的 int_arr 只写了前 6 个有效数,其余 19 个格子默认是 0,灌进去后双精度矩阵对应位置也是 0.0。外汇和贵金属策略里拿历史 tick 数组去初始化特征矩阵时,这种自动补零可能掩盖样本边界,建议先 Print 出来核对维度。 开 MT5 把代码贴进脚本跑一遍,重点看 double_matrix 前后维度从 2×10 变成 5×5——这一行就能确认 Assign 的隐式 reshape 行为,比看文档直观。

MQL5 / C++
class=class="str">"cmt">//--- copying matrices
  matrix a= {{class="num">2, class="num">2}, {class="num">3, class="num">3}, {class="num">4, class="num">4}};
  matrix b=a+class="num">2;
  matrix c;
  Print("matrix a \n", a);
  Print("matrix b \n", b);
  c.Assign(b);
  Print("matrix c \n", c);
  class=class="str">"cmt">/*
  matrix a
  [[class="num">2,class="num">2]
   [class="num">3,class="num">3]
   [class="num">4,class="num">4]]
  matrix b
  [[class="num">4,class="num">4]
   [class="num">5,class="num">5]
   [class="num">6,class="num">6]]
  matrix c
  [[class="num">4,class="num">4]
   [class="num">5,class="num">5]
   [class="num">6,class="num">6]]
  */
class=class="str">"cmt">//--- copying an array to a matrix
  matrix double_matrix=matrix::Full(class="num">2,class="num">10,class="num">3.14);
  Print("double_matrix before Assign() \n", double_matrix);
  class="type">int int_arr[class="num">5][class="num">5]= {{class="num">1, class="num">2}, {class="num">3, class="num">4}, {class="num">5, class="num">6}};
  Print("int_arr: ");
  ArrayPrint(int_arr);
  double_matrix.Assign(int_arr);
  Print("double_matrix after Assign(int_arr) \n", double_matrix);
  class=class="str">"cmt">/*
  double_matrix before Assign() 
  [[class="num">3.14,class="num">3.14,class="num">3.14,class="num">3.14,class="num">3.14,class="num">3.14,class="num">3.14,class="num">3.14,class="num">3.14,class="num">3.14]
   [class="num">3.14,class="num">3.14,class="num">3.14,class="num">3.14,class="num">3.14,class="num">3.14,class="num">3.14,class="num">3.14,class="num">3.14,class="num">3.14]]
  
  int_arr: 
       [,class="num">0][,class="num">1][,class="num">2][,class="num">3][,class="num">4]
  [class="num">0,]   class="num">1   class="num">2   class="num">0   class="num">0   class="num">0
  [class="num">1,]   class="num">3   class="num">4   class="num">0   class="num">0   class="num">0
  [class="num">2,]   class="num">5   class="num">6   class="num">0   class="num">0   class="num">0
  [class="num">3,]   class="num">0   class="num">0   class="num">0   class="num">0   class="num">0
  [class="num">4,]   class="num">0   class="num">0   class="num">0   class="num">0   class="num">0
  
  double_matrix after Assign(int_arr) 
  [[class="num">1,class="num">2,class="num">0,class="num">0,class="num">0]
   [class="num">3,class="num">4,class="num">0,class="num">0,class="num">0]
   [class="num">5,class="num">6,class="num">0,class="num">0,class="num">0]
   [class="num">0,class="num">0,class="num">0,class="num">0,class="num">0]
   [class="num">0,class="num">0,class="num">0,class="num">0,class="num">0]]
  */

常见问题

向量和矩阵内置了点积、转置等运算,多品种价格序列可直接做批量计算,不用手写循环,效率更高。
声明矩阵时需指定行数列数,数组元素数不够会补零,超出则截断,灌入前先确认长度避免静默出错。
小布可读取你的品种页脚本,提示矩阵扩容后未初始化区域可能残留历史数值,并给出重分配写法建议。
会残留。MQL5 矩阵重新分配内存后旧位置数据不自动清空,扩容后务必显式赋初值或整体重置。
矩阵类型由语言层优化且支持线性代数接口,运算速度和代码可读性都优于手搓二维数组,适合量化计算。