MQL5 中的矩阵和向量操作·进阶篇
🧮

MQL5 中的矩阵和向量操作·进阶篇

(2/3)·从时间序列灌入到矩阵运算,用原生类型替掉三层嵌套循环

案例拆解 第 2/3 篇

接上篇铺垫的类型概念,我们继续深挖 MQL5 里矩阵和向量真正落地的几处关键操作。很多人在 EA 里仍用老式数组硬算协方差和回归,代码又长又难改,其实语言层已经给了更直的接口。

◍ 用 CopyRates 一次拉满多品种收盘价矩阵

做跨品种相关性分析时,传统写法要逐个调时间序列函数再把数组转矩阵,步骤碎、容易漏。MQL5 里 vector.CopyRates 能直接把 MqlRates 历史序列塞进向量,再用 matrix.Col 插列,一次调用就拿到了干净的数值矩阵。 下面这段脚本挂了 5 个直盘:EURUSD、GBPUSD、USDJPY、USDCAD、USDCHF,周期 H1、取 100 根 K 线的收盘价。实测日志里每个品种都回了 '100 Close prices were added to the matrix',说明向量尺寸对齐没问题,矩阵 rates 是 100×5。 拿到矩阵后就能直接算相关系数:可以两两向量调 CorrCoef,也能把整矩阵丢进 CorrCoef 出相关阵,再用 TriU 取上三角、Compare 比两种算法差异。外汇和贵金属跨品种相关受利率和避险情绪驱动,H1 窗口只有 100 根,相关值随时变,结论只能当概率参考,杠杆品种高风险。 开 MT5 把代码贴进脚本跑一遍,重点看 PrintFormat 出来的每列 Size 是否都是 100,若某个符号返回 0 多半是市场报价未开启。

MQL5 / C++
class="kw">input class="type">int                InBars=class="num">100;
class="kw">input ENUM_TIMEFRAMES InTF=PERIOD_H1;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Script program start function                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart()
{
class=class="str">"cmt">//--- list of symbols for calculation
  class="type">class="kw">string symbols[]= {"EURUSD", "GBPUSD", "USDJPY", "USDCAD", "USDCHF"};
  class="type">int size=ArraySize(symbols);
class=class="str">"cmt">//--- matrix and vector to receive Close prices
  matrix rates(InBars, size);
  vector close;
  for(class="type">int i=class="num">0; i<size; i++)
   {
   class=class="str">"cmt">//--- get Close prices to a vector
   if(close.CopyRates(symbols[i], InTF, COPY_RATES_CLOSE, class="num">1, InBars))
    {
    class=class="str">"cmt">//--- insert the vector to the timeseries matrix
    rates.Col(close, i);
    PrintFormat("%d. %s: %d Close prices were added to matrix", i+class="num">1, symbols[i], close.Size());
    class=class="str">"cmt">//--- output the first class="num">20 vector values for debugging
    class="type">int  digits=(class="type">int)SymbolInfoInteger(symbols[i], SYMBOL_DIGITS);
    Print(VectorToString(close, class="num">20, digits));
    }
   else
    {
    Print("vector.CopyRates(%d,COPY_RATES_CLOSE) failed. Error ", symbols[i], GetLastError());
    class="kw">return;
    }
   }
  class=class="str">"cmt">/*
  class="num">1. EURUSD: class="num">100 Close prices were added to the matrix
  class="num">0.99561 class="num">0.99550 class="num">0.99674 class="num">0.99855 class="num">0.99695 class="num">0.99555 class="num">0.99732 class="num">1.00305 class="num">1.00121  class="num">1.069 class="num">0.99936  class="num">1.027 class="num">1.00130 class="num">1.00129 class="num">1.00123 class="num">1.00201 class="num">1.00222 class="num">1.00111  class="num">1.079  class="num">1.030  ...
  class="num">2. GBPUSD: class="num">100 Close prices were added to the matrix
  class="num">1.13733 class="num">1.13708 class="num">1.13777 class="num">1.14045 class="num">1.13985 class="num">1.13783 class="num">1.13945 class="num">1.14315 class="num">1.14172 class="num">1.13974 class="num">1.13868 class="num">1.14116 class="num">1.14239 class="num">1.14230 class="num">1.14160 class="num">1.14281 class="num">1.14338 class="num">1.14242 class="num">1.14147 class="num">1.14069  ...
  class="num">3. USDJPY: class="num">100 Close prices were added to the matrix
  class="num">143.451 class="num">143.356 class="num">143.310 class="num">143.202 class="num">143.079 class="num">143.294 class="num">143.146 class="num">142.963 class="num">143.039 class="num">143.032 class="num">143.039 class="num">142.957 class="num">142.904 class="num">142.956 class="num">142.920 class="num">142.837 class="num">142.756 class="num">142.928 class="num">143.130 class="num">143.069  ...
  class="num">4. USDCAD: class="num">100 Close prices were added to the matrix
  class="num">1.32840 class="num">1.32877 class="num">1.32838 class="num">1.32660 class="num">1.32780 class="num">1.33068 class="num">1.33001 class="num">1.32798 class="num">1.32730 class="num">1.32782 class="num">1.32951 class="num">1.32868 class="num">1.32716 class="num">1.32663 class="num">1.32629 class="num">1.32614 class="num">1.32586 class="num">1.32578 class="num">1.32650 class="num">1.32789  ...
  class="num">5. USDCHF: class="num">100 Close prices were added to the matrix
  class="num">0.96395 class="num">0.96440 class="num">0.96315 class="num">0.96161 class="num">0.96197 class="num">0.96337 class="num">0.96358 class="num">0.96228 class="num">0.96474 class="num">0.96529 class="num">0.96529 class="num">0.96502 class="num">0.96463 class="num">0.96429 class="num">0.96378 class="num">0.96377 class="num">0.96314 class="num">0.96428 class="num">0.96483 class="num">0.96509  ...
  */
class=class="str">"cmt">//--- prepare a matrix of correlations between symbols
  matrix corr_from_vector=matrix::Zeros(size, size);
  Print("Compute pairwise correlation coefficients");
  for(class="type">int i=class="num">0; i<size; i++)
   {
   for(class="type">int k=i; k<size; k++)
    {
    vector v1=rates.Col(i);
    vector v2=rates.Col(k);

向量循环与单行矩阵算相关性哪个更稳

手动遍历两个价格向量调用 CorrCoef 算两两相关系数,再把结果塞进二维数组,是多数老脚本的写法。上面那段循环跑出的矩阵里,EURUSD 对 GBPUSD 相关系数 0.974,对 USDCAD 是 -0.950,对 USDCHF 仅 -0.397,正负分化很明显,做多币种组合时得留意同向爆仓风险。 MT5 也支持直接拿整个 rates 矩阵调 CorrCoef(false) 一行出结果,参数 false 表示向量按列排。把两种算法用 Compare 以 1e-12 精度比对,误差计数返回 0,说明手工循环和内置矩阵函数在浮点层面一致,没必要怀疑哪边算错。 最后那段把矩阵用表头符号名打印出来,EURUSD 行读数是 1.0 / 0.974 / -0.713 / -0.950 / -0.397,肉眼核对比看原始浮点数组轻松得多。外汇与贵金属杠杆高,相关性会随利率周期跳变,这类矩阵建议每次换周线重算,别拿三个月前的数硬套。

MQL5 / C++
class="type">class="kw">double coeff = v1.CorrCoef(v2);
PrintFormat("corr(%s,%s) = %.3f", symbols[i], symbols[k], coeff);
corr_from_vector[i][k]=coeff;
}
}
Print("Correlation matrix on vectors: \n", corr_from_vector);
class=class="str">"cmt">/*
Calculate pairwise correlation coefficients
corr(EURUSD,EURUSD) = class="num">1.000
corr(EURUSD,GBPUSD) = class="num">0.974
corr(EURUSD,USDJPY) = -class="num">0.713
corr(EURUSD,USDCAD) = -class="num">0.950
corr(EURUSD,USDCHF) = -class="num">0.397
corr(GBPUSD,GBPUSD) = class="num">1.000
corr(GBPUSD,USDJPY) = -class="num">0.744
corr(GBPUSD,USDCAD) = -class="num">0.953
corr(GBPUSD,USDCHF) = -class="num">0.362
corr(USDJPY,USDJPY) = class="num">1.000
corr(USDJPY,USDCAD) = class="num">0.736
corr(USDJPY,USDCHF) = class="num">0.083
corr(USDCAD,USDCAD) = class="num">1.000
corr(USDCAD,USDCHF) = class="num">0.425
corr(USDCHF,USDCHF) = class="num">1.000
Correlation matrix on vectors:
[[class="num">1,class="num">0.9736363791537366,-class="num">0.7126365191640618,-class="num">0.9503129578410202,-class="num">0.3968181226230434]
 [class="num">0,class="num">1,-class="num">0.7440448047501974,-class="num">0.9525190338388175,-class="num">0.3617774666815978]
 [class="num">0,class="num">0,class="num">1,class="num">0.7360546499847362,class="num">0.08314381248168941]
 [class="num">0,class="num">0,class="num">0,class="num">0.9999999999999999,class="num">0.4247042496841555]
 [class="num">0,class="num">0,class="num">0,class="num">0,class="num">1]]
*/
class=class="str">"cmt">//--- now let&class="macro">#x27;s see how a correlation matrix can be calculated in one line
matrix corr_from_matrix=rates.CorrCoef(false);   class=class="str">"cmt">// false means that the vectors are in the matrix columns
Print("Correlation matrix rates.CorrCoef(false): \n", corr_from_matrix.TriU());
class=class="str">"cmt">//--- compare the resulting matrices to find discrepancies
Print("How many discrepancy errors between result matrices?");
class="type">class="kw">ulong errors=corr_from_vector.Compare(corr_from_matrix.TriU(), (class="type">class="kw">float)class="num">1e-12);
Print("corr_from_vector.Compare(corr_from_matrix,class="num">1e-12)=", errors);
class=class="str">"cmt">/*
Correlation matrix rates.CorrCoef(false):
[[class="num">1,class="num">0.9736363791537366,-class="num">0.7126365191640618,-class="num">0.9503129578410202,-class="num">0.3968181226230434]
 [class="num">0,class="num">1,-class="num">0.7440448047501974,-class="num">0.9525190338388175,-class="num">0.3617774666815978]
 [class="num">0,class="num">0,class="num">1,class="num">0.7360546499847362,class="num">0.08314381248168941]
 [class="num">0,class="num">0,class="num">0,class="num">1,class="num">0.4247042496841555]
 [class="num">0,class="num">0,class="num">0,class="num">0,class="num">1]]
How many discrepancy errors between result matrices?
corr_from_vector.Compare(corr_from_matrix,class="num">1e-12)=class="num">0
*/
class=class="str">"cmt">//--- create a nice output of the correlation matrix
Print("Output the correlation matrix with headers");
class="type">class="kw">string header="       ";   class=class="str">"cmt">// header
for(class="type">int i=class="num">0; i<size; i++)
   header+="  "+symbols[i];
Print(header);
class=class="str">"cmt">//--- now rows
for(class="type">int i=class="num">0; i<size; i++)
  {
   class="type">class="kw">string line=symbols[i]+"  ";
   line+=VectorToString(corr_from_vector.Row(i), size, class="num">3, class="num">8);
   Print(line);
  }
class=class="str">"cmt">/*
Output the correlation matrix with headers
         EURUSD  GBPUSD  USDJPY  USDCAD  USDCHF
EURUSD     class="num">1.0   class="num">0.974  -class="num">0.713  -class="num">0.950  -class="num">0.397
GBPUSD     class="num">0.0     class="num">1.0  -class="num">0.744  -class="num">0.953  -class="num">0.362
USDJPY     class="num">0.0     class="num">0.0     class="num">1.0   class="num">0.736   class="num">0.083
USDCAD     class="num">0.0     class="num">0.0     class="num">0.0     class="num">1.0   class="num">0.425
USDCHF     class="num">0.0     class="num">0.0     class="num">0.0     class="num">0.0     class="num">1.0
*/

「把向量打印成对齐文本的两个辅助函数」

在 MT5 里做向量调试时,直接 Print(v) 出来的内容常常挤成一团,肉眼很难比对前后元素。下面这两个函数能把 vector 转成定宽对齐的字符串,方便在专家日志里快速扫读。 VectorToString 默认截取前 20 个元素(用 MathMin(20, v.Size()) 控制),每个数值保留 5 位小数、占 8 字符宽度;若向量实际长度超过 20,行尾补 ' ...' 提示截断。DoubleToString 之后用 StringReplace 把 '.000' 替换成 '.0',能少看几个零。 Indent 是个纯体力活函数:传入需要多少个空格,就拼出对应长度的空格串。VectorToString 里用 width-StringLen(value) 算出差值再调 Indent,保证每列右对齐。 复制进 EA 的 mq5 文件,调用 Print(VectorToString(my_vec)) 即可在日志看到整齐的向量快照;若你的向量维度常大于 50,可把函数里的 20 改成更大值,但日志行宽超过 200 字符在 MT5 里可能折行,建议控制在 80 以内。外汇与贵金属行情高波动,日志仅作离线核对,不构成任何方向判断。

MQL5 / C++
class="type">class="kw">string VectorToString(const vector &v, class="type">int length=class="num">20, class="type">int digits=class="num">5, class="type">int width=class="num">8)
{
  class="type">class="kw">ulong size=(class="type">class="kw">ulong)MathMin(class="num">20, v.Size());
class=class="str">"cmt">//--- compose a class="type">class="kw">string
  class="type">class="kw">string line="";
  for(class="type">class="kw">ulong i=class="num">0; i<size; i++)
   {
    class="type">class="kw">string value=DoubleToString(v[i], digits);
    StringReplace(value, ".class="num">000", ".class="num">0");
    line+=Indent(width-StringLen(value))+value;
   }
class=class="str">"cmt">//--- add a tail if the vector length exceeds the specified size
  if(v.Size()>size)
    line+="  ...";
class=class="str">"cmt">//---
  class="kw">return(line);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  Returns a class="type">class="kw">string with the specified number of spaces            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">string Indent(class="type">int number)
{
  class="type">class="kw">string indent="";
  for(class="type">int i=class="num">0; i<number; i++)
    indent+=" ";
  class="kw">return(indent);
}

◍ 矩阵向量的逐元素运算规则

在 MT5 里,matrix 和 vector 的加、减、乘、除默认都是逐元素(element-wise)操作,不是线性代数里的矩阵乘法。两个操作对象必须同类型且同维度,比如 2×3 的矩阵只能和另一个 2×3 的矩阵做对应位运算;若用标量当第二项,则矩阵每个成员都套用这个标量。 大多数数学函数也能直接吃矩阵或向量,做的是逐元素处理。MathAbs、MathSin、MathSqrt 这类都能直接传 matrix,例如对 [[1,4],[9,16]] 跑 MathSqrt,结果就是 [[1,2],[3,4]],你可以原样贴进 MT5 验证。 MathMod 和 MathPow 特殊一点:第二个参数既可以是标量,也可以是同尺寸的矩阵/向量。代码里用 128×128 的 mat1 对比 MathPow(mat1,1.9) 和循环逐元素 MathPow 的结果,误差计数 errors 为 0 时才说明内置函数与手算一致;vector 维度拉到 16384 时同理。外汇与贵金属波动剧烈,用这类批量运算做指标预处理时须留意浮点误差累积带来的高风险。 原地运算(+=\/=)也支持,matrix_a+=matrix_b 后 matrix_a 直接改写,不另开内存。写高频因子计算时,用原地操作能少一次拷贝,128×128 规模下体感明显。

MQL5 / C++
  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 matrix_b={{class="num">1,class="num">2,class="num">3},{class="num">4,class="num">5,class="num">6}};
  matrix matrix_c1=matrix_a+matrix_b;
  matrix matrix_c2=matrix_b-matrix_a;
  matrix matrix_c3=matrix_a*matrix_b;   class=class="str">"cmt">// Hadamard product
  matrix matrix_c4=matrix_b/matrix_a;
  matrix_c1=matrix_a+class="num">1;
  matrix_c2=matrix_b-double_value;
  matrix_c3=matrix_a*M_PI;
  matrix_c4=matrix_b/class="num">0.1;
class=class="str">"cmt">//--- operations in place are possible
  matrix_a+=matrix_b;
  matrix_a/=class="num">2;
class=class="str">"cmt">//---
  matrix a= {{class="num">1, class="num">4}, {class="num">9, class="num">16}};
  Print("matrix a=\n",a);
  a=MathSqrt(a);
  Print("MatrSqrt(a)=\n",a);
  class=class="str">"cmt">/*
   matrix a=
   [[class="num">1,class="num">4]
    [class="num">9,class="num">16]]
   MatrSqrt(a)=
   [[class="num">1,class="num">2]
    [class="num">3,class="num">4]]
   */
   matrix<T> mat1(class="num">128,class="num">128);
   matrix<T> mat3(mat1.Rows(),mat1.Cols());
   class="type">class="kw">ulong     n,size=mat1.Rows()*mat1.Cols();
...
   mat2=MathPow(mat1,(T)class="num">1.9);
   for(n=class="num">0; n<size; n++)
     {
       T res=MathPow(mat1.Flat(n),(T)class="num">1.9);
       if(res!=mat2.Flat(n))
         errors++;
     }
   mat2=MathPow(mat1,mat3);
   for(n=class="num">0; n<size; n++)
     {
       T res=MathPow(mat1.Flat(n),mat3.Flat(n));
       if(res!=mat2.Flat(n))
         errors++;
     }
...
   vector<T> vec1(class="num">16384);
   vector<T> vec3(vec1.Size());
   class="type">class="kw">ulong     n,size=vec1.Size();
...
   vec2=MathPow(vec1,(T)class="num">1.9);
   for(n=class="num">0; n<size; n++)
     {
       T res=MathPow(vec1[n],(T)class="num">1.9);
       if(res!=vec2[n])
         errors++;
     }
   vec2=MathPow(vec1,vec3);
   for(n=class="num">0; n<size; n++)
     {
       T res=MathPow(vec1[n],vec3[n]);
       if(res!=vec2[n])
         errors++;
     }

矩阵向量免计算的结构操纵

MT5 的 matrix 和 vector 原生支持一批不触发数值运算的结构操纵:转置、提取行列与对角线、改尺寸重塑、行列置换、复制、比较、拆分子矩阵和排序。这些操作在写指标或批量处理 K 线矩阵时,省掉手写循环,直接调方法即可。 转置把 2 行 3 列的 [[0,1,2],[3,4,5]] 变成 3 行 2 列的 [[0,3],[1,4],[2,5]],行变列。Diag 方法既能用 vector 生成对角阵,也能从已有矩阵里抽指定偏移的对角线——比如偏移 1 抽出来是 [1,2,3,9],偏移 -1 抽出 [9,9,9]。 Reshape 不改变元素总顺序只改视图形状:4 行 3 列拉成 2 行 6 列时元素顺排,但重建成 3 行 5 列时因容量不够会补 0,像 [11,12,0,3,0] 里就出现了两个 0。Vsplit 按给定行数把矩阵竖切为多个子矩阵,parts 数组写 {2,3} 即前 2 行和后 3 行各成一块。 Col 和 Row 方法还能往未分配大小的空矩阵里插元素,适合边收数据边扩结构的场景。开 MT5 把下面代码丢进脚本跑一遍,重点看 Reshape 三次后末尾补 0 的现象,比你读文档直观。

MQL5 / C++
  matrix a= {{class="num">0, class="num">1, class="num">2}, {class="num">3, class="num">4, class="num">5}};
  Print("matrix a \n", a);
  Print("a.Transpose() \n", a.Transpose());
  class=class="str">"cmt">/*
  matrix a
  [[class="num">0,class="num">1,class="num">2]
   [class="num">3,class="num">4,class="num">5]]
  a.Transpose()
  [[class="num">0,class="num">3]
   [class="num">1,class="num">4]
   [class="num">2,class="num">5]]
  */
   vector v1={class="num">1,class="num">2,class="num">3};
   matrix m1;
   m1.Diag(v1);
   Print("m1\n",m1);
  class=class="str">"cmt">/*
  m1
  [[class="num">1,class="num">0,class="num">0]
  [class="num">0,class="num">2,class="num">0]
  [class="num">0,class="num">0,class="num">3]]
  m2
  */
   matrix m2;
   m2.Diag(v1,-class="num">1);
   Print("m2\n",m2);
  class=class="str">"cmt">/*
  m2
  [[class="num">0,class="num">0,class="num">0]
  [class="num">1,class="num">0,class="num">0]
  [class="num">0,class="num">2,class="num">0]
  [class="num">0,class="num">0,class="num">3]]
  */
   matrix m3;
   m3.Diag(v1,class="num">1);
   Print("m3\n",m3);
  class=class="str">"cmt">/*
  m3
  [[class="num">0,class="num">1,class="num">0,class="num">0]
  [class="num">0,class="num">0,class="num">2,class="num">0]
  [class="num">0,class="num">0,class="num">0,class="num">3]]
  */
   matrix m4=matrix::Full(class="num">4,class="num">5,class="num">9);
   m4.Diag(v1,class="num">1);
   Print("m4\n",m4);
   
   Print("diag -class="num">1 - ",m4.Diag(-class="num">1));
   Print("diag class="num">0 - ",m4.Diag());
   Print("diag class="num">1 - ",m4.Diag(class="num">1));
  class=class="str">"cmt">/*
  m4
  [[class="num">9,class="num">1,class="num">9,class="num">9,class="num">9]
  [class="num">9,class="num">9,class="num">2,class="num">9,class="num">9]
  [class="num">9,class="num">9,class="num">9,class="num">3,class="num">9]
  [class="num">9,class="num">9,class="num">9,class="num">9,class="num">9]]
  diag -class="num">1 - [class="num">9,class="num">9,class="num">9]
  diag class="num">0 - [class="num">9,class="num">9,class="num">9,class="num">9]
  diag class="num">1 - [class="num">1,class="num">2,class="num">3,class="num">9]
  */
   matrix matrix_a={{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}};
   Print("matrix_a\n",matrix_a);
  class=class="str">"cmt">/*
  matrix_a
  [[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]]
  */
   matrix_a.Reshape(class="num">2,class="num">6);
   Print("Reshape(class="num">2,class="num">6)\n",matrix_a);
  class=class="str">"cmt">/*
  Reshape(class="num">2,class="num">6)
  [[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]]
  */
   matrix_a.Reshape(class="num">3,class="num">5);
   Print("Reshape(class="num">3,class="num">5)\n",matrix_a);
  class=class="str">"cmt">/*
  Reshape(class="num">3,class="num">5)
  [[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">0,class="num">3,class="num">0]]
  */
   matrix_a.Reshape(class="num">2,class="num">4);
   Print("Reshape(class="num">2,class="num">4)\n",matrix_a);
  class=class="str">"cmt">/*
  Reshape(class="num">2,class="num">4)
  [[class="num">1,class="num">2,class="num">3,class="num">4]
   [class="num">5,class="num">6,class="num">7,class="num">8]]
  */
   matrix matrix_a={{ 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,class="num">15,class="num">16,class="num">17,class="num">18}};
   matrix splitted[];
   class="type">class="kw">ulong  parts[]={class="num">2,class="num">3};
   matrix_a.Vsplit(class="num">2,splitted);
   for(class="type">uint i=class="num">0; i<splitted.Size(); i++)

「竖向切分与按列写入的实测输出」

Vsplit 的两种调用方式在 3×6 矩阵上差异明显:直接给行数 3,会均匀切成两块,每块 3 列;给数组 parts={3,4,5} 时,则按指定列数切成三块,末块只剩 1 列。从打印看,原矩阵 [[1..18]] 被拆成 [[1,2][7,8][13,14]]、[[3,4,5][9,10,11][15,16,17]]、[[6][12][18]],列维度严格按 parts 累加。 Col 方法写列时不会自动扩维。m1 初始为空,Col(v1,1) 把 [1,2,3] 写到第 1 列(0 基),矩阵变成 3×2,未写列全是 0;而 m2 用 Full(4,5,8) 预填后 Col(v1,2),第 2 列被覆盖为 [1,2,3,8],其余列保持 8。 回测这类矩阵操作别靠脑补,开 MT5 把上面代码贴进脚本,看 Print 输出是否与注释一致;外汇与贵金属行情矩阵化处理时杠杆波动大,实盘前先用历史数据验证切分逻辑。

MQL5 / C++
  Print("splitted ",i,"\n",splitted[i]);
  class=class="str">"cmt">/*
    splitted class="num">0
    [[class="num">1,class="num">2,class="num">3]
      [class="num">7,class="num">8,class="num">9]
      [class="num">13,class="num">14,class="num">15]]
    splitted class="num">1
    [[class="num">4,class="num">5,class="num">6]
      [class="num">10,class="num">11,class="num">12]
      [class="num">16,class="num">17,class="num">18]]
  */
   matrix_a.Vsplit(class="num">3,splitted);
   for(class="type">uint i=class="num">0; i<splitted.Size(); i++)
      Print("splitted ",i,"\n",splitted[i]);
  class=class="str">"cmt">/*
    splitted class="num">0
    [[class="num">1,class="num">2]
      [class="num">7,class="num">8]
      [class="num">13,class="num">14]]
    splitted class="num">1
    [[class="num">3,class="num">4]
      [class="num">9,class="num">10]
      [class="num">15,class="num">16]]
    splitted class="num">2
    [[class="num">5,class="num">6]
      [class="num">11,class="num">12]
      [class="num">17,class="num">18]]
*/
   matrix_a.Vsplit(parts,splitted);
   for(class="type">uint i=class="num">0; i<splitted.Size(); i++)
      Print("splitted ",i,"\n",splitted[i]);
  class=class="str">"cmt">/*
    splitted class="num">0
    [[class="num">1,class="num">2]
      [class="num">7,class="num">8]
      [class="num">13,class="num">14]]
    splitted class="num">1
    [[class="num">3,class="num">4,class="num">5]
      [class="num">9,class="num">10,class="num">11]
      [class="num">15,class="num">16,class="num">17]]
    splitted class="num">2
    [[class="num">6]
      [class="num">12]
      [class="num">18]]
  */
   vector v1={class="num">1,class="num">2,class="num">3};
   matrix m1;
   m1.Col(v1,class="num">1);
   Print("m1\n",m1);
  class=class="str">"cmt">/*
   m1
   [[class="num">0,class="num">1]
    [class="num">0,class="num">2]
    [class="num">0,class="num">3]]
  */
   matrix m2=matrix::Full(class="num">4,class="num">5,class="num">8);
   m2.Col(v1,class="num">2);
   Print("m2\n",m2);
  class=class="str">"cmt">/*
   m2
   [[class="num">8,class="num">8,class="num">1,class="num">8,class="num">8]
   [class="num">8,class="num">8,class="num">2,class="num">8,class="num">8]
   [class="num">8,class="num">8,class="num">3,class="num">8,class="num">8]
    [class="num">8,class="num">8,class="num">8,class="num">8,class="num">8]]   
  */
   Print("col class="num">1 - ",m2.Col(class="num">1));
  class=class="str">"cmt">/*
   col class="num">1 - [class="num">8,class="num">8,class="num">8,class="num">8]
  */
   Print("col class="num">2 - ",m2.Col(class="num">2));
  class=class="str">"cmt">/*
   col class="num">1 - [class="num">8,class="num">8,class="num">8,class="num">8]  col class="num">2 - [class="num">1,class="num">2,class="num">3,class="num">8]
  */
交给小布盯盘看盘口
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到向量化后的波动特征,你专注决策就好。

常见问题

可用 CopyClose 配合向量赋值接口直接灌入 double 向量,避免手写循环;具体封装见矩阵和向量方法里的序列拷贝函数。
浮点矩阵占用带宽更小,大规模机器学习推断时延迟可能更低,但精度损失在贵金属点差环境里要自行评估概率影响。
可以,小布盯盘的品种页支持粘贴向量化指标输出,自动标注异动区间,省去你手动比对历史分布。
撰写本文时相关内置方法尚未完工,实战中建议暂用 double 类型绕开,等官方补全再迁移。
先查条件数,近奇异矩阵会放大误差;可换伪逆或正则化,外汇高阶数据常出现共线需警惕高风险。