MQL5中的范畴论(第18部分):自然性四边形·进阶篇
📐

MQL5中的范畴论(第18部分):自然性四边形·进阶篇

(2/3)· 当ATR与布林带变化被放进同一个态射网格,预测波动不再靠肉眼比对

新手友好 第 2/3 篇
把自然变换当成函子间的简单映射,很多人会漏掉它真正链接的是共域对象而非起点。只用指标当前值判断波动,容易在拐点前丢掉结构信息。本文用自然性四边形把这种变化显式铺开。

「用两个列表函子看清自然变换的交换性」

把范畴论里的自然变换先放下抽象定义,直接用一组具体对象跑一遍更容易抓感觉。设范畴 C 有两个对象 X={5,6,7} 与 Y={Q,R,S},态射 f: X→Y 规定 f(5)=S、f(6)=R、f(7)=R。 取两个函子 F、G 把 C 映到 D:F 把对象变成平铺列表,F(X)=[5,6,5,7,5,6,7,7];G 把对象变成列表的列表,G(X)=[[5,6],[5,7,5,6,7],[7]]。对 Y 同样作用会得到 D 中另外两组对象,这样共域里一共有四个对象可看。 自然变换 η 对每个 C 中对象给一个箭头 η_X: F(X)→G(X) 与 η_Y: F(Y)→G(Y)。把它们和函子作用在 f 上得到的两条竖箭头摆在一起,就构成自然性四边形;箭头能 commute 表示无论先走 η 再走函子,还是先走 f 再走 η,结果一致。 这背后关键不是符号游戏:NT 强制保存了结构与关系。比如两位厨师 A、B 用同套食材做同一道菜,A 的菜到 B 的菜之间的 NT 不只记录差异,还保留 A 的食谱与手法;据此还能查厨师 C 的菜离 B 差多少变换,或按饮食限制反推现有配方要改哪一步。外汇与贵金属市场里这类“结构保持”思维可用来校验多周期信号映射是否一致,但市场高风险,映射一致只代表数学关系成立,不预示方向。

◍ 用自然变换框住波动预测

把价格柱区间的下一步变化预测出来,最直接的用途是判断持仓的跟踪止损要不要动、动多少。外汇与贵金属杠杆高、跳空频繁,这类预测只作概率参考,不能当成确定性信号。 具体做法是把多层感知器(MLP)架在「平方换相」结构上:正方形的四个角 A、B、C、D 各自对应未来不同时点的区间变化预测。A 角只投射下一根价格柱的区间变动,越往 D 角走,视野越往前推;若用近期区间变化去训练连通四角的 MLP,就有机会在单根柱之外给出更远的推演。 下面的 CTransformation 类就是自然变换的载体,它不存两个函子实例,只抓函子映射到的两个域(domain 与 codomain),再挂一个 MLP 做变换。开 MT5 把这段代码塞进 EA,改 hidden_size 就能试探网络容量对预测偏移的影响。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| NATURAL TRANSFORMATION CLASS                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CTransformation
  {
   class="kw">protected:
   
   class="kw">public:
   
   CDomain<class="type">class="kw">string>                domain;   class=class="str">"cmt">//codomain object of first functor
   CDomain<class="type">class="kw">string>                codomain; class=class="str">"cmt">//codomain object of second functor
   
   class="type">uint                           hidden_size;
   CMultilayerPerceptron          transformer;
   CMLPBase                       init;
   
   class="type">void                           Transform(CDomain<class="type">class="kw">string> &D,CDomain<class="type">class="kw">string> &C)
                                     {
                                      domain=D;
                                      codomain=C;
                                      
                                      class="type">int _inputs=D.Cardinality(),_outputs=C.Cardinality();
                                      
                                      if(_inputs>class="num">0 && _outputs>class="num">0)
                                       {
                                        init.MLPCreate1(_inputs,hidden_size+fmax(_inputs,_outputs),_outputs,transformer);
                                       }
                                     }
   
   class=class="str">"cmt">//
   class="type">void                           Let()

用自然性方块把变换串成闭环

上面这段类定义把范畴论里的自然性方块(naturality square)落到了 MT5 的 C++ 风格代码里。核心思路是:四条对象 A、B、C、D 用 CDomain<string> 承载,上下两条边 AB、CD 是 CTransformation,左右两条边 BD、AC 则交给 CMultilayerPerceptron 去拟合,hidden_size_bd 与 hidden_size_ac 分别控制两边隐藏层规模。 CTransformation 的构造函数里写死了 hidden_size=1,说明这条边本身不做复杂映射,只是把 domain 和 codomain 的 Let() 依次释放,相当于把输入输出域的占用交还系统。 实际在 MT5 里验证时,你可以把 CNaturalitySquare 的 AB、CD 换成自己的指标映射,再让 BD、AC 两个感知机跑不同品种——外汇与贵金属杠杆高、跳空频繁,两边网络权重发散的概率会明显大于股票。开 MT5 新建 EA 把这段粘进去编译,先确认 hidden_size=1 的变换不会在策略 tester 里报空指针,再决定要不要扩 hidden_size。

MQL5 / C++
            {
               this.codomain.Let();
               this.domain.Let();
            };

            CTransformation(class="type">void){ hidden_size=class="num">1; };
            ~CTransformation(class="type">void){};
   };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| NATURALITY CLASS                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CNaturalitySquare
   {
   class="kw">protected:

   class="kw">public:

   CDomain<class="type">class="kw">string>             A,B,C,D;

   CTransformation             AB;

   class="type">uint                        hidden_size_bd;
   CMultilayerPerceptron       BD;

   class="type">uint                        hidden_size_ac;
   CMultilayerPerceptron       AC;

   CTransformation             CD;

   CMLPBase                    init;


            CNaturalitySquare(class="type">void){};
            ~CNaturalitySquare(class="type">void){};
   };

「MT5里怎么跑这套临时训练」

把MLP塞进MT5,最棘手的不是前向推理,而是训练和权重落盘的方式。本篇和前两轮不同:每个新K线到来时,四个MLP全部重新实例化并用随机权重初始化,不读取任何历史训练结果,也不把训好的权重写回文件。 驱动这件事的是下面这段Refresh函数。它只在最近一根价格柱上,对随机初始化的网络做一轮训练。对要拿去实盘或共享代码的系统来说这显然不够稳,但作为验证「自然性方块」假设的测试手段,够用。 输入参数 m_extra_training 默认是0,也就是只训当前柱。把它往上调,预测前会多跑几轮样本外训练,EA负载会明显上升,但也更逼近真实泛化表现。这正好解释了为什么本文刻意避开了权重的读写——额外训练一开,存盘反而成了累赘。 外汇与贵金属市场高杠杆、高波动,这类临训结构在切换品种或周期时可能失效,请在策略测试器里先跑样本内再上模拟盘。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Refresh function for naturality square.                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CTrailingCT::Refresh()
  {
   class="type">class="kw">double _refresh=class="num">0.0;
   
   m_high.Refresh(-class="num">1);
   m_low.Refresh(-class="num">1);
   
   class="type">int _x=StartIndex();
   
   class=class="str">"cmt">// atr domains capture class="num">1 bar ranges   
   class=class="str">"cmt">// bands&class="macro">#x27; domains capture class="num">2 bar ranges  
   class=class="str">"cmt">// class="num">1 functors capture ranges after class="num">1 bar  
   class=class="str">"cmt">// class="num">2 functors capture ranges after class="num">2 bars 
   
   class="type">int _info_ab=class="num">0,_info_bd=class="num">0,_info_ac=class="num">0,_info_cd=class="num">0;
   CMLPReport _report_ab,_report_bd,_report_ac,_report_cd;
   CMatrixDouble _xy_ab;_xy_ab.Resize(m_extra_training+class="num">1,class="num">1+class="num">1);
   CMatrixDouble _xy_bd;_xy_bd.Resize(m_extra_training+class="num">1,class="num">1+class="num">1);
   CMatrixDouble _xy_ac;_xy_ac.Resize(m_extra_training+class="num">1,class="num">1+class="num">1);
   CMatrixDouble _xy_cd;_xy_cd.Resize(m_extra_training+class="num">1,class="num">1+class="num">1);
   
   CElement<class="type">class="kw">string> _e;
   for(class="type">int i=class="num">0;i<m_extra_training+class="num">1;i++)
   {
     ...
     
     if(i<m_extra_training+class="num">1)
     {
       _xy_ab[i].Set(class="num">0,_a);class=class="str">"cmt">//in
       _xy_ab[i].Set(class="num">1,_b);class=class="str">"cmt">//out
       
       _xy_bd[i].Set(class="num">0,_b);class=class="str">"cmt">//in
       _xy_bd[i].Set(class="num">1,_d);class=class="str">"cmt">//out
       
       _xy_ac[i].Set(class="num">0,_a);class=class="str">"cmt">//in
       _xy_ac[i].Set(class="num">1,_c);class=class="str">"cmt">//out
       
       _xy_cd[i].Set(class="num">0,_c);class=class="str">"cmt">//in
       _xy_cd[i].Set(class="num">1,_d);class=class="str">"cmt">//out
     }
   }
   
   m_train.MLPTrainLM(m_naturality_square.AB.transformer,_xy_ab,m_extra_training+class="num">1,m_decay,m_restarts,_info_ab,_report_ab);
 
   ...
   class=class="str">"cmt">//
   if(_info_ab>class="num">0 && _info_bd>class="num">0 && _info_ac>class="num">0 && _info_cd>class="num">0)
   {
     ...
   }
   
   class="kw">return(_refresh);
  }
让小布替你跑这套
把指标归一化和态射配对的重复计算交给小布盯盘,你专注决策。这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到波动结构的实时四边形。

常见问题

因为自然变换衡量的是两个函子把同一个域对象送出去之后,在共域里如何对应,链接的是函子映射到的共域对象,而非原指标对象本身。
可以。小布盯盘的品种页内置了波动结构诊断,会把ATR与布林带归一化变化按本文的态射逻辑实时呈现,省去手写EA跟踪类的麻烦。
变化按10%步长从负100%到正100%记录,包含0共21个离散值,因此每个对象基数固定为21,方便做确定性配对。
不一定,但本文给出的实例是EA交易跟踪类,实际也可以写成指标脚本,重点在于把两个函子的映射结果放进同一个 commutative square 检验。