MQL5 中的范畴论 (第 15 部分):函子与图论(基础篇)
📘

MQL5 中的范畴论 (第 15 部分):函子与图论(基础篇)

第 1/3 篇

「用函子把图结构搬进 MQL5」

在 MQL5 里谈范畴论,第 15 部分落地到函子与图论的交叉点:函子本质是一类保持结构的映射,能把一个范畴的对象和态射整块迁到另一个范畴,而图论里的顶点与边正好可视为某个范畴的骨架。 MT5 内置的图容器适合表达顶点集合与邻接关系,若用函子视角封装,就能把行情状态图(如高低点拓扑)映射到指标计算范畴,而不破坏原有的连接语义。外汇与贵金属市场高波动,这类抽象仅辅助建模,不预示任何方向。 下面这段给出极简函子式包装:把图顶点数组映射到另一数组并保持索引对应,验证时可在 MT5 新建脚本粘贴,观察 ArraySize 输出是否为 740(原文示例数据规模)。 实际用处在于,当你要把多周期拓扑合并进同一分析空间,函子保证子图映射后仍能追溯原顶点,避免手动拷贝导致的关系断裂。

MQL5 / C++
class=class="str">"cmt">// 极简函子式映射:保持顶点索引结构
class="type">int GraphFunctor(const class="type">int &src[], class="type">int &dst[])
  {
   class="type">int n = ArraySize(src);          class=class="str">"cmt">// 取源图顶点数
   ArrayResize(dst, n);             class=class="str">"cmt">// 目标数组同尺寸
   for(class="type">int i=class="num">0; i<n; i++)
     dst[i] = src[i];               class=class="str">"cmt">// 保持索引对应(结构映射)
   class="kw">return n;                        class=class="str">"cmt">// 返回映射后顶点数
  }

把日历数据铺成图再谈函子

上一节我们把线性序当成范畴看,态射跨范畴时就成了函子。这一节换一套工具:用图论把 MQL5 经济日历数据重构成一张图,让它也变成一个范畴。 域范畴从线性序换成图之后,协域仍是一条预排序的时间序列,但装的不是 VIX,而是标准普尔 500 指数的波动率值。这样做的意图和前几篇一致——检验两个范畴之间的函子是否携带可用来预测波动的讯息。 关键动作很具体:打开 MT5 里的经济日历,把事件节点和先后关系导成图结构,再对接标普波动率序列。能在 MT5 里复现这张图,才算接上了后面要讲的函子映射。外汇与贵金属受宏观事件冲击大,这类重构仅作概率层面的波动参考,高风险品种务必轻仓验证。

◍ 用图论把财经日历重构成节点网

把财经日历灌进 MT5 不是为了复述宏观逻辑,而是把它变成一张可计算的图:顶点是新闻项,边线是其间的派生关系。先挑子集,若只取零售销售、制造业 PMI、CPI、国债竞售四个点,并假设零售销售驱动 PMI、PMI 派生 CPI、CPI 来自国债业绩、国债业绩又回咬零售销售,就得到一个四节点的循环图。这种假设的准确度不是重点,重点是它示范了如何从日历数据勾出可能的图结构。 图论用两张表就够:一张存顶点配对(边),一张存顶点索引。顶点当范畴里的对象,边就是态射。它比上一篇的线性序更松——线性序每个对象只能连下一个,图里一个顶点能同时连多个,适合表达新闻间的网状耦合。 配对时不像线性序那样把单点对标普波动率范畴的单点一一对应,而是把「顶点对」的整行(多行)捆起来,去配标普里的单个时间数据点。所以两者不可能同构:标普按时间展开,我们的域对象由循环中四个项的各自最新值构成,维度根本对不上。

「函子如何把两个范畴连成交易映射」

很多做 MQL5 的交易者习惯直接上神经网络,毕竟那套研究积累比范畴论深得多。但范畴之间的函子其实是更底层的态射:它不只把两个范畴里的对象对应起来,还把范畴各自内部的同态结构也一并连上,这对构建非常规策略有意义。 我们在前一篇跑过两类连接:一类只做对象对对象的映射,另一类在同范畴内走态射连接。函子在数学上同时覆盖这两种对数关系,实操里我们分开测,以便看差异。 测试窗口取 2020 年 1 月 1 日到 2020 年 3 月 15 日,样本偏短,没法断言哪种映射更优。但两边结果差异明显,说明映射方式对纳斯达克波动率预测高度敏感,某一种连接倾向压过另一种,值得在 MT5 里自己复跑确认。外汇与贵金属杠杆高,这类非常规映射实盘前务必小仓位验证。

把标普波动塞进协域对象里

标普500的波动率处理思路和纳斯达克那套类似,但独立看VIX会漏掉价格本身的振幅信息,所以这里用每根新K线的 (high-low)/Point() 作为原始波动读数,在每根柱线刷新时重算。这套读数被装进一个协域:域里是单根波动值的集合,态射捕捉相邻读数间的相对变化。 只要能把带滞后的域映射进这个协域,就具备了对标普500波动做概率性前瞻的条件。测试时沿用上一篇文章的信号与资金管理设置,分别跑对象函子和态射函子,看哪类映射对波动跳变更敏感。 下面这段MQL5是把单根波动值写进协域的核心。逐行看:先声明 _float_value 并归零;注释提示 R 是 MqlRates 实例;用 (R.high-R.low)/Point() 算出以最小报价单位计的振幅。SetSP500 函数里先清空态射容器,再算同样振幅,把值包成基数1的元素塞进域,最后挂到协域的域序列上。 _float_value=(R.high-R.low)/Point() 这一行就是波动读数的唯一数据源,改 Point() 为自定义刻度可切换波动率口径。外汇与贵金属波动结构不同,直接套用标普口径前需在MT5上验证点值映射,杠杆品种高风险,回测结论仅代表历史概率。

MQL5 / C++
      class="type">class="kw">double _float_value=class="num">0.0;
      class=class="str">"cmt">//where R is an instance of class="type">MqlRates...
      _float_value=(R.high-R.low)/Point();
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  Get SP500-class="num">100 data from symbol(NDX100 for this broker).        | 
class=class="str">"cmt">//|  Load it into SP500 category.                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void SetSP500(class="type">MqlRates &R)
  {
      _hmorph_sp500.Let();
      
      class="type">class="kw">double _float_value=class="num">0.0;
      
      _float_value=(R.high-R.low)/Point();
      
      _element_value.Let();_element_value.Cardinality(class="num">1);_element_value.Set(class="num">0,DoubleToString(_float_value));
      _domain_sp500.Cardinality(class="num">1);_domain_sp500.Set(class="num">0,_element_value);
      
      class=class="str">"cmt">//_category_sp500.Domains(_category_sp500.Domains()+class="num">1);
      _category_sp500.SetDomain(_category_sp500.Domains(),_domain_sp500);  
  }

常见问题

先把事件和受影响品种分别定义为节点,用有向边表示「事件→品种波动」关系,在代码里建邻接表即可可视化,比看表格直观得多。
用函子思维把各周期价格序列映射到同一协域对象(如归一化向量),只比映射后的结构差异,不用叠主图指标也能看偏离。
可以,小布盯盘的 AIGC 已内置日历到节点网的重构,打开对应品种页就能直接看事件—波动关系图,不用自己写映射代码。
两者量纲和波动幅度不同,直接叠是范畴错配;应先通过函子把各自塞进统一协域对象(如 z-score 空间)再比较。
节点网只做盘后归因和盘前预案,不用于 tick 级触发;高频场景另走轻量信号通道,图结构偏中低频诊断。