MQL5 中的矩阵和向量·综合运用
🧮

MQL5 中的矩阵和向量·综合运用

(3/3)· 前两层铺垫完类型与基础接口,这篇把乘积累加、维度陷阱和复数矩阵一次打通

进阶 第 3/3 篇
把矩阵当普通二维数组来回套循环,是 MQL5 线性代数代码里最隐蔽的性能黑洞。不少人还在手写嵌套 for 做向量点积,调试时连索引越界都难查。本篇收尾直接给你原生类型的正确打开方式。

「MT5 矩阵运算能直接搬哪些 NumPy 套路」

MQL5 的 matrix 类把一堆线性代数原语塞进了原生环境,做多资产协方差、因子加权或回归滤噪时不用自己写循环。换位、逐元素加减乘除、标量与矩阵混合运算、MatMul 积,以及 Inner / Outer / Kron 这些都在列,基本对应 NumPy 的同名语义。 解线性方程组有 Solve(),非方阵或退化矩阵走 LstSq() 拿最小二乘解,PInv() 给伪逆。Inv() 求逆但要先确认条件数别炸,外汇分钟级回归矩阵常接近奇异,直接逆容易数值溢出。 分解方法对照 NumPy 很直白:Cholesky 对应 cholesky,QR 对应 qr,SVD 对应 svd,Eig / EigVals 对应 eig / eigvals。LU 和带行置换的 LUP(PA=LU)也都内置,回测里做状态空间递推时能省不少事。 贵金属跨期价差建模若用 Cholesky 分解降维,注意样本外窗口要滚动重算,历史相关在高波动期会快速失效,杠杆品种回撤风险被放大。

◍ MatMul 里向量和矩阵的排列陷阱

MT5 的 matrix/vector 体系里,MatMul() 做矩阵积时有两套方向规则:左水平向量乘右矩阵,要求向量长度等于矩阵列数;左矩阵乘右垂直向量,要求矩阵列数等于向量长度。若向量长度与矩阵列数对不上,运行时直接抛严重错误,不是返回空值那么简单。 两个矩阵相乘的维度约束是 A[M,N]*B[N,K]=C[M,K],左矩阵列数必须等右矩阵行数,否则得到空矩阵。示例里用 m35(3x5) 乘 m52(5x2) 得到 3x2,维度链断一处就全废。 向量在底层就是特殊矩阵:水平向量是 1xN,垂直向量是 Nx1。代码里 v3.MatMul(m35) 得到长度 5 的水平向量,等价于 m13(1x3).MatMul(m35);m35.MatMul(v5) 得到长度 3 的垂直向量,等价于 m35.MatMul(m51(5x1))。开 MT5 把下面代码跑一遍,对照 Print 输出就能确认向量方向。 别把 Outer 当 MatMul 用。v5.Outer(v3) 得到 5x3 矩阵,和 m15(1x5).Outer(m31(3x1)) 结果一致,但它是外积不是矩阵积,维度逻辑完全不同。外汇/贵金属策略里拿矩阵做多资产权重计算时,乘错方向可能让信号整体反转,属高风险操作。

MQL5 / C++
class="type">void OnStart()
  {
class=class="str">"cmt">//--- initialize matrices
   matrix m35, m52;
   m35.Init(class="num">3,class="num">5,Arange);
   m52.Init(class="num">5,class="num">2,Arange);
class=class="str">"cmt">//---
   Print("class="num">1. Product of horizontal vector v[class="num">3] and matrix m[class="num">3,class="num">5]");
   vector v3 = {class="num">1,class="num">2,class="num">3};
   Print("On the left v3 = ",v3);
   Print("On the right m35 = \n",m35);
   Print("v3.MatMul(m35) = horizontal vector v[class="num">5] \n",v3.MatMul(m35));
class=class="str">"cmt">//--- show that this is really a horizontal vector
   Print("\n2. Product of matrix m[class="num">1,class="num">3] and matrix m[class="num">3,class="num">5]");
   matrix m13;
   m13.Init(class="num">1,class="num">3,Arange,class="num">1);
   Print("On the left m13 = \n",m13);
   Print("On the right m35 = \n",m35);
   Print("m13.MatMul(m35) = matrix m[class="num">1,class="num">5] \n",m13.MatMul(m35));
   Print("\n3. Product of matrix m[class="num">3,class="num">5] and vertical vector v[class="num">5]");
   vector v5 = {class="num">1,class="num">2,class="num">3,class="num">4,class="num">5};
   Print("On the left m35 = \n",m35);
   Print("On the right v5 = ",v5);
   Print("m35.MatMul(v5) = vertical vector v[class="num">3] \n",m35.MatMul(v5));
class=class="str">"cmt">//--- show that this is really a vertical vector
   Print("\n4. Product of matrix m[class="num">3,class="num">5] and matrix m[class="num">5,class="num">1]");
   matrix m51;
   m51.Init(class="num">5,class="num">1,Arange,class="num">1);
   Print("On the left m35 = \n",m35);
   Print("On the right m51 = \n",m51);
   Print("m35.MatMul(m51) = matrix v[class="num">3] \n",m35.MatMul(m51));
   Print("\n5. Product of matrix m[class="num">3,class="num">5] and matrix m[class="num">5,class="num">2]");
   Print("On the left m35 = \n",m35);
   Print("On the right m52 = \n",m52);
   Print("m35.MatMul(m52) = matrix m[class="num">3,class="num">2] \n",m35.MatMul(m52));
   Print("\n6. Product of horizontal vector v[class="num">5] and matrix m[class="num">5,class="num">2]");
   Print("On the left v5 = \n",v5);
   Print("On the right m52 = \n",m52);
   Print("v5.MatMul(m52) = horizontal vector v[class="num">2] \n",v5.MatMul(m52));
   Print("\n7. Outer() product of horizontal vector v[class="num">5] and vertical vector v[class="num">3]");
   Print("On the left v5 = \n",v5);
   Print("On the right v3 = \n",v3);
   Print("v5.Outer(v3) = matrix m[class="num">5,class="num">3] \n",v5.Outer(v3));
class=class="str">"cmt">//--- show that the product of matrices generates the same result
   Print("\n8. Outer() product of the matrix m[class="num">1,class="num">5] and matrix m[class="num">3,class="num">1]");
   matrix m15,m31;
   m15.Init(class="num">1,class="num">5,Arange,class="num">1);
   m31.Init(class="num">3,class="num">1,Arange,class="num">1);
   Print("On the left m[class="num">1,class="num">5] = \n",m15);
   Print("On the right m31 = \n",m31);
   Print("m15.Outer(m31) = matrix m[class="num">5,class="num">3] \n",m15.Outer(m31));
  }

用 Arange 填矩阵看 MatMul 外积怎么算

下面这段函数给任意 matrix 按行优先、从 start 起以 step 递增填值,是验证矩阵乘法结果的手工构造器。把它丢进 MT5 脚本,先造出规则矩阵,再对照 MatMul / Outer 的输出,能直接看清维度对齐规则。 void Arange(matrix &m, double start=0, double step=1) 接收矩阵引用与起止步长;内部取 m.Cols()、m.Rows() 拿到列数与行数,用双层 for 把 value 依次写入 m[r][c] 并累加 step。跑完一个 3×5 矩阵会从 0 填到 14。 实测几组乘积:横向向量 v3=[1,2,3] 乘 m35(3×5)得 [40,46,52,58,64];m35 乘纵向向量 v5=[1,2,3,4,5] 得 [40,115,190]。Outer 外积更直白,v5(1×5)与 v3(3×1)外积出 5×3 矩阵,首行就是 [1,2,3] 的倍数排开。 外汇与贵金属市场杠杆高、跳空频繁,这类线性代数工具只帮你算信号矩阵,不预示行情方向,结论都只是概率层面的参考。

MQL5 / C++
class="type">void Arange(matrix &m, class="type">class="kw">double start = class="num">0, class="type">class="kw">double step = class="num">1) class=class="str">"cmt">// the function has three parameters
  {
class=class="str">"cmt">//---
   class="type">class="kw">ulong cols = m.Cols();
   class="type">class="kw">ulong rows = m.Rows();
   class="type">class="kw">double value = start;
   for(class="type">class="kw">ulong r = class="num">0; r < rows; r++)
     {
       for(class="type">class="kw">ulong c = class="num">0; c < cols; c++)
         {
          m[r][c] = value;
          value += step;
         }
     }
class=class="str">"cmt">//---    
  }
class=class="str">"cmt">/*
Execution result
class="num">1. Product of horizontal vector v[class="num">3] and matrix m[class="num">3,class="num">5]
On the left v3 = [class="num">1,class="num">2,class="num">3]
On the right m35 =
[[class="num">0,class="num">1,class="num">2,class="num">3,class="num">4]
[class="num">5,class="num">6,class="num">7,class="num">8,class="num">9]
[class="num">10,class="num">11,class="num">12,class="num">13,class="num">14]]
v3.MatMul(m35) = horizontal vector v[class="num">5]
[class="num">40,class="num">46,class="num">52,class="num">58,class="num">64]
class="num">2. Product of matrix m[class="num">1,class="num">3] and matrix m[class="num">3,class="num">5]
On the left m13 =
[[class="num">1,class="num">2,class="num">3]]
On the right m35 =
[[class="num">0,class="num">1,class="num">2,class="num">3,class="num">4]
[class="num">5,class="num">6,class="num">7,class="num">8,class="num">9]
[class="num">10,class="num">11,class="num">12,class="num">13,class="num">14]]
m13.MatMul(m35) = matrix m[class="num">1,class="num">5]
[[class="num">40,class="num">46,class="num">52,class="num">58,class="num">64]]
class="num">3. Product of matrix m[class="num">3,class="num">5] and vertical vector v[class="num">5]
On the left m35 =
[[class="num">0,class="num">1,class="num">2,class="num">3,class="num">4]
[class="num">5,class="num">6,class="num">7,class="num">8,class="num">9]
[class="num">10,class="num">11,class="num">12,class="num">13,class="num">14]]
On the right v5 = [class="num">1,class="num">2,class="num">3,class="num">4,class="num">5]
m35.MatMul(v5) = vertical vector v[class="num">3]
[class="num">40,class="num">115,class="num">190]
class="num">4. Product of matrix m[class="num">3,class="num">5] and matrix m[class="num">5,class="num">1]
On the left m35 =
[[class="num">0,class="num">1,class="num">2,class="num">3,class="num">4]
[class="num">5,class="num">6,class="num">7,class="num">8,class="num">9]
[class="num">10,class="num">11,class="num">12,class="num">13,class="num">14]]
On the right m51 =
[[class="num">1]
[class="num">2]
[class="num">3]
[class="num">4]
[class="num">5]]
m35.MatMul(m51) = matrix v[class="num">3]
[[class="num">40]
[class="num">115]
[class="num">190]]
class="num">5. Product of matrix m[class="num">3,class="num">5] and matrix m[class="num">5,class="num">2]
On the left m35 =
[[class="num">0,class="num">1,class="num">2,class="num">3,class="num">4]
[class="num">5,class="num">6,class="num">7,class="num">8,class="num">9]
[class="num">10,class="num">11,class="num">12,class="num">13,class="num">14]]
On the right m52 =
[[class="num">0,class="num">1]
[class="num">2,class="num">3]
[class="num">4,class="num">5]
[class="num">6,class="num">7]
[class="num">8,class="num">9]]
m35.MatMul(m52) = matrix m[class="num">3,class="num">2]
[[class="num">60,class="num">70]
[class="num">160,class="num">195]
[class="num">260,class="num">320]]
class="num">6. The product of horizontal vector v[class="num">5] and matrix m[class="num">5,class="num">2]
On the left v5 =
[class="num">1,class="num">2,class="num">3,class="num">4,class="num">5]
On the right m52 =
[[class="num">0,class="num">1]
[class="num">2,class="num">3]
[class="num">4,class="num">5]
[class="num">6,class="num">7]
[class="num">8,class="num">9]]
v5.MatMul(m52) = horizontal vector v[class="num">2]
[class="num">80,class="num">95]
class="num">7. Outer() product of horizontal vector v[class="num">5] and vertical vector v[class="num">3]
On the left v5 =
[class="num">1,class="num">2,class="num">3,class="num">4,class="num">5]
On the right v3 =
[class="num">1,class="num">2,class="num">3]
v5.Outer(v3) = matrix m[class="num">5,class="num">3]
[[class="num">1,class="num">2,class="num">3]
[class="num">2,class="num">4,class="num">6]
[class="num">3,class="num">6,class="num">9]
[class="num">4,class="num">8,class="num">12]
[class="num">5,class="num">10,class="num">15]]
class="num">8. Outer() product of the matrix m[class="num">1,class="num">5] and matrix m[class="num">3,class="num">1]
On the left m[class="num">1,class="num">5] =
[[class="num">1,class="num">2,class="num">3,class="num">4,class="num">5]]
On the right m31 =
[[class="num">1]
[class="num">2]
[class="num">3]]
m15.Outer(m31) = matrix m[class="num">5,class="num">3]
[[class="num">1,class="num">2,class="num">3]
[class="num">2,class="num">4,class="num">6]
[class="num">3,class="num">6,class="num">9]
[class="num">4,class="num">8,class="num">12]
[class="num">5,class="num">10,class="num">15]]
*/

「画得少,看得清」

MQL5 里的 complex 类型本质是个含 real 与 imag 双精度字段的结构,但它跳出了普通结构的限制:能作为函数参数按值传递,只有从 DLL 导入时才强制走引用。写常数时后缀 i 直接标虚部,比如 1+2i 就是实 1 虚 2。 目前复数只开放了 =、+、-、*、/ 及对应复合赋值和相等判断这几组基础运算,官方提到后续会补绝对值、正弦、余弦等数学函数,但当下若要做频域滤波还得自己扩。 把上面那段代码丢进 MT5 脚本跑,OnStart 里 Print(square(1+2i)) 会输出 "(-3,4)"——实算 (1+2i)²=1+4i²+4i=-3+4i,验证按值传递和运算符重载都没坑。外汇与贵金属杠杆高、波动剧烈,用复数做信号变换前先在小周期回测,别直接挂实盘。

MQL5 / C++
class="kw">struct complex
  {
   class="type">class="kw">double             real;   class=class="str">"cmt">// real part
   class="type">class="kw">double             imag;   class=class="str">"cmt">// imaginary part
  };
complex  square(complex c)
  {
   class="kw">return(c*c);
  }
  
class="type">void OnStart()
  {
   Print(square(class="num">1+2i));   class=class="str">"cmt">// a constant is passed as a parameter
  }
class=class="str">"cmt">// will print "(-class="num">3,class="num">4)" - a class="type">class="kw">string representation of a complex number
把重复劳动交给小布
这些矩阵诊断和小布盯盘的 AIGC 已内置,打开对应品种页即可看到向量维度告警,你专注策略而非底层索引。

常见问题

运算前用 Size 和位数核对形状,矩阵乘向量时确认外层维一致;建议在封装函数里加断言打印实际维度,避免静默算出错误长度的结果。
原生类型在底层走优化过的线性代数路径,省去解释层循环开销,且避免索引记错导致的隐性 bug,倾向在高频计算里优先用内置方法。
可以,小布盯盘的 AIGC 模块对品种页加载的自定义指标会做基础形状校验,维度不匹配会在日志侧提示,减少你手动排查时间。
复数矩阵需走对应复数方法,不能套用双精度向量范数;具体接口见末节说明,实盘运用涉及外汇贵金属高风险,信号仅作概率参考。