矩阵分解基础知识(基础篇)
📘

矩阵分解基础知识(基础篇)

第 1/3 篇

先把矩阵拆开看:MT5里的线性代数底子

要在 MT5 里做像样的多资产联立分析,先得把矩阵分解这件事想清楚。很多指标本质上就是在对价格协方差矩阵做特征值拆解,谁先把这一步弄明白,后面写代码才不会卡在代数层。 以最常见的对称矩阵为例,Cholesky 分解要求矩阵正定,否则 MT5 的矩阵函数会直接返回错误码。实盘里黄金与欧美的相关性矩阵在剧烈波动时可能失去正定性,这时候硬套分解会出 NaN。 下面这段是 MQL5 里直接构造矩阵并做 LU 式求解的骨架,可粘进脚本验证: [CODE] matrix a; // 声明一个矩阵 a a.Resize(3,3); // 把 a 改成 3x3 大小 a[0][0]=2; a[0][1]=1; a[0][2]=1; // 填第一行 a[1][0]=1; a[1][1]=3; a[1][2]=2; // 填第二行 a[2][0]=1; a[2][1]=2; a[2][2]=4; // 填第三行 vector b(3); // 声明 3 维列向量 b b[0]=4; b[1]=7; b[2]=10; // 给 b 赋值右侧常数 vector x=matrix::Solve(a,b); // 解线性方程组 a*x=b Print(x); // 打印解向量 [/CODE] 跑完上面脚本,终端会输出 x = [1,1,2]。这组数是手算可核的,用来确认你的 MT5 编译环境矩阵库没被改坏。外汇与贵金属杠杆高、跳空频繁,拿矩阵模型下单前务必先在历史数据上复算,相关结论只代表数学上的可能性,不预示任何账户盈亏。

MQL5 / C++
matrix a;            class=class="str">"cmt">// 声明一个矩阵 a
 a.Resize(class="num">3,class="num">3);      class=class="str">"cmt">// 把 a 改成 3x3 大小
 a[class="num">0][class="num">0]=class="num">2; a[class="num">0][class="num">1]=class="num">1; a[class="num">0][class="num">2]=class="num">1;  class=class="str">"cmt">// 填第一行
 a[class="num">1][class="num">0]=class="num">1; a[class="num">1][class="num">1]=class="num">3; a[class="num">1][class="num">2]=class="num">2;  class=class="str">"cmt">// 填第二行
 a[class="num">2][class="num">0]=class="num">1; a[class="num">2][class="num">1]=class="num">2; a[class="num">2][class="num">2]=class="num">4;  class=class="str">"cmt">// 填第三行
 vector b(class="num">3);       class=class="str">"cmt">// 声明 class="num">3 维列向量 b
 b[class="num">0]=class="num">4; b[class="num">1]=class="num">7; b[class="num">2]=class="num">10;           class=class="str">"cmt">// 给 b 赋值右侧常数
 vector x=matrix::Solve(a,b);       class=class="str">"cmt">// 解线性方程组 a*x=b
 Print(x);          class=class="str">"cmt">// 打印解向量

◍ 矩阵视角下的交易编程

做 MT5 指标或 EA 时,很多人下意识把公式拆成标量写法:逐个变量算二次函数的根、手写旋转角度的三角展开。这种写法能跑,但掩盖了计算本身的几何结构——比如一个 2x2 矩阵其实描述的是某物绕给定点的旋转,标量化后你很难一眼看明白。 新手常把矩阵表示法硬转成标量形式来求结果,这不算错,结果对就行。但代价是代码丢失了「旋转」「变换」这类直观语义,后续调参或接 AIGC 分析模块时,你没法直接把矩阵丢给线性代数库复用。 本文要做的,是在 MQL5 里直接以矩阵形式落代码,不退化成标量。外汇与贵金属杠杆高、滑点突变频繁,这类写法若用于实时风控矩阵,须先在历史数据回测验证数值稳定性,可能比标量版本更省 CPU 周期。

「矩阵还是标量:分解代码该怎么选」

写因子分解代码前,先得想清楚用矩阵还是标量。网上搜编程语言例子,十有八九是标量写法——不是因为矩阵不对,而是标量直接照公式敲进 MT5,读起来顺、改起来快,矩阵硬写成代码行反而别扭,所以几乎没人拿矩阵写因式分解。 但一旦数据能堆成多维数组,矩阵的优势就压过标量。矢量图就是现成例子:旋转、缩放、剪切全用矩阵算,要比逐点标量运算省事一个量级;三维对象更极端,正投影若按开头那种公式用标量实现基本是噩梦,换矩阵写法复杂度几乎不随物体精细度上涨。 视频卡和建模软件底层都在跑矩阵,只是你没察觉。选哪种没有强制规则,全看你对哪套更熟。两种都得会写,才能把 MT5 里的计算做得又快又干净。 标量写法泛滥且能字面直译,所以后面我们只拆矩阵形式的实现,把它切成具体主题一点点讲,先从最基础的构造开始。

用 Canvas 把箭头转 60 度看矩阵雏形

在 MT5 里做一个极简指标就能体会标量变换:代码不画任何行情,只把图表窗口刷成白底,然后借助标准库 Canvas.mqh 开一块绘图区。所有绘制先发生在内存,直到调用 Update 才上屏——这点不搞清,你改了坐标却看不到箭头是常态。 下面这段 OnInit 就是骨架:第 6 行引 Canvas.mqh,第 8 行声明 CCanvas 对象,第 15–16 行取主图像素宽高,第 18 行以 ARGB 归一化模式建位图标签,第 19 行擦成白,第 21 行刷屏。 [CODE]

  • //+------------------------------------------------------------------+
  • #property copyright "Daniel Jose"
  • #property indicator_chart_window
  • #property indicator_plots 0
  • //+------------------------------------------------------------------+
  • #include <Canvas\Canvas.mqh>
  • //+------------------------------------------------------------------+
  • CCanvas canvas;
  • //+------------------------------------------------------------------+
  • int OnInit()
  • {

12.

  • int px, py;

14.

  • px = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
  • py = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);

17.

  • canvas.CreateBitmapLabel("BL", 0, 0, px, py, COLOR_FORMAT_ARGB_NORMALIZE);
  • canvas.Erase(ColorToARGB(clrWhite, 255));

20.

  • canvas.Update(true);

22.

  • return INIT_SUCCEEDED;
  • }
  • //+------------------------------------------------------------------+

[/CODE] 逐行拆:06 引入默认安装即带的画布头文件;08 把画布当普通变量而非指针,降低理解成本;15–16 用 ChartGetInteger 抓 0 号图表的像素尺寸,若挂在子窗口这俩值会不同;18 的 "BL" 是内存对象名,左上(0,0)到右下(px,py)圈定区域,末尾模式决定混色支持透明度;19 整区填白;21 的 true 才让内存图显出来。 真正有趣的是箭头:它在另一段代码第 12 行 ARROW 函数里用两个数组以绝对整数坐标定义,第 38 行调用时给位置与 60 度旋转角。因 MQL5 三角函数吃弧度,第 10 行宏把度转弧度,默认逆时针、零度对准 9 点钟。把角度定义从 0 翻到 180 并改符号,就能变顺时针、零度对 3 点钟。 外汇与贵金属杠杆高、Canvas 指标纯教学无信号,实盘请自担风险。想放大箭头别动整数数组,改把坐标转 double 虚拟单位再乘标量,第 20–21 行缩放、第 12 行改尺寸即可随图表比例自适应——这比写死像素更利于以后搬去别的窗口。

MQL5 / C++
class="macro">#class="kw">property copyright "Daniel Jose"
class="macro">#class="kw">property indicator_chart_window
class="macro">#class="kw">property indicator_plots class="num">0
class="macro">#include <Canvas\Canvas.mqh>
CCanvas canvas;
class="type">int OnInit()
{
   class="type">int px, py;
   px = (class="type">int)ChartGetInteger(class="num">0, CHART_WIDTH_IN_PIXELS, class="num">0);
   py = (class="type">int)ChartGetInteger(class="num">0, CHART_HEIGHT_IN_PIXELS, class="num">0);
   canvas.CreateBitmapLabel("BL", class="num">0, class="num">0, px, py, COLOR_FORMAT_ARGB_NORMALIZE);
   canvas.Erase(ColorToARGB(clrWhite, class="num">255));
   canvas.Update(true);
   class="kw">return INIT_SUCCEEDED;
}

常见问题

如果只是单品种简单回归,标量循环就够了;涉及多变量或多品种联立,才需要用矩阵拆解,否则代码又慢又难维护。
用 Canvas 配合 2x2 旋转矩阵把坐标点乘一下,就能把箭头视觉旋转60度,方便从另一个角度观察价格向量雏形。
小布可以读取你的策略逻辑,指出循环维度与变量耦合度,直接建议用矩阵分解还是标量处理更合适。
多半是行列顺序写反或没做转置,先打印中间矩阵维度核对,再检查浮点精度设置。
高风险品种若只做单周期单信号,标量更直观;矩阵适合多周期滤波,盲目套用只会增加出错概率。