MQL5 中的范畴论 (第 2 部分)·进阶篇
📘

MQL5 中的范畴论 (第 2 部分)·进阶篇

第 2/3 篇

「投影器数组的边界守卫与存取实现」

在 MT5 自定义类里管理投影器(projector)动态数组时,先卡一道非负且低于 INT_MAX 的边界,能避免外部传入脏值把 ArrayResize 撑爆。下面这段Setter只在 0≤Value<INT_MAX 时把 projectors 更新并重新分配数组,返回 true,否则直接返回 false。 Projectors() 仅做取值器用,返回当前投影器数量;Get() 按索引取字符串,越界就回空串;Set() 按索引写字符串,越界返回 false。这种写法把越界风险关在类内部,调用方拿不到异常引用。 构造函数把 projectors 置 0 并 ArrayFree 清数组,析构留空。外汇与贵金属行情跳变频繁,这类容器若被复用,务必确认清零逻辑,否则可能残留上一轮品种的配置。

MQL5 / C++
if(Value>=class="num">0 && Value<INT_MAX)
            {
              projectors=Value;
              ArrayResize(projector,projectors);
              class="kw">return(true);
            }
            
            class="kw">return(false);
            };

class="type">int                Projectors(){ class="kw">return(projectors); };

class="kw">public:

class="type">class="kw">string            Get(class="type">int ProjectorIndex) { class="type">class="kw">string _projector=""; if(ProjectorIndex>=class="num">0 && ProjectorIndex<Projectors()) { _projector=projector[ProjectorIndex]; } class="kw">return(_projector); }
class="type">bool              Set(class="type">int ValueIndex,class="type">class="kw">string Value) { if(ValueIndex>=class="num">0 && ValueIndex<Projectors()) { projector[ValueIndex]=Value; class="kw">return(true); } class="kw">return(false); }

CDomain<class="type">class="kw">string>   class="kw">property;
CDomain<class="type">class="kw">string>   cone;

CComposition(class="type">void){ projectors=class="num">0;ArrayFree(projector); };
~CComposition(class="type">void){};

用类封装把投影器扩展成字符串域

在组合类之上再派生一层 CProduct,本质是把原本数值型的投影器数量,映射成一组字符串域对象,方便后续按名索骥地挂接不同品种或周期的逻辑。 下面这段实现里,Surjectors(int Value) 先做边界判断:Value 必须落在 0 到 INT_MAX 之间,否则直接返回 false。通过后则调用父类 CComposition::Projectors(Value) 同步投影器计数,并用 ArrayResize 把 surjector 数组成员扩到同样长度。 读成员时用重载的无参 Surjectors() 返回父类里的 projectors 字段;取具体某条字符串域则走 Get(),索引越界就返回 false。外汇与贵金属市场波动剧烈、杠杆风险高,这类结构仅用于理清多标的组合关系,不代表任何方向判断。

MQL5 / C++
class CProduct : class="kw">public CComposition
  {
   class="kw">protected:
   CDomain<class="type">class="kw">string> surjector[];
   class="kw">public:
   class="type">bool Surjectors(class="type">int Value)
     {
      if(Value>=class="num">0 && Value<INT_MAX)
       {
        CComposition::Projectors(Value);
        ArrayResize(surjector,Value);
        class="kw">return(true);
       }
      class="kw">return(false);
     };
   class="type">int Surjectors(){ class="kw">return(CComposition::projectors); };
   class="type">bool Get(class="type">int SurjectorIndex,CDomain<class="type">class="kw">string> &Surjector) { if(SurjectorIndex>=class="num">0 && SurjectorIndex<CComposition::Projectors()) { Surjector=surjector[SurjectorIndex]; class="kw">return(true); } class="kw">return(false); }

◍ 多指标域拼装与笛卡尔积落地

这段实现把六个振荡类指标拆进三个双维值域(domain A/B/C),再用笛卡尔积合成复合产品。domain A 装 RSI 与 CCI,domain B 装 DeMarker 与 WPR,domain C 装 Stochastic(8,4,4) 与 TriX,全部共用 __product_size 作为取样长度。 CProduct 的 Set 方法只做边界检查:ValueIndex 落在 [0, Projectors()) 才把引用写入 surjector 数组并返回 true,越界直接返回 false,避免越界写坏内存。析构里 ArrayFree(surjector) 释放投影数组,句柄类资源交给 MT5 终端回收。 指标句柄创建时 Stochastic 用了固定参数 8/4/4 + MODE_SMA + STO_LOWHIGH,其余四个都吃 __product_size 和 __product_price 宏。FillDomain 连续短路与运算,任一指标取数失败整段跳过,不打印也不合成。 合成顺序先 GetProduct(_d_p_a,_d_p_b,_product,2) 打出 A&B 的二维锥,再把结果与 _d_p_c 做第二次 GetProduct(_product.cone,_d_p_c,_product,5),维度参数 2 与 5 决定投影切片粒度。开 MT5 把 __product_size 设成 30 跑一遍,终端大概率是三行 domain 打印加两行 product 打印,外汇与贵金属波动下这套域合成的高维噪声会明显放大,属高风险验证。

MQL5 / C++
class="type">bool Set(class="type">int ValueIndex,CDomain<class="type">class="kw">string> &Value) { if(ValueIndex>=class="num">0 && ValueIndex<CComposition::Projectors()) { surjector[ValueIndex]=Value; class="kw">return(true); } class="kw">return(false); }

CProduct(class="type">void){ ArrayFree(surjector); };
~CProduct(class="type">void){};
class=class="str">"cmt">//////////
class=class="str">"cmt">//PRODUCTS
class=class="str">"cmt">//////////

CDomain<class="type">class="kw">double> _d_p_a,_d_p_b,_d_p_c;
_d_p_a.Cardinality(__product_size);_d_p_b.Cardinality(__product_size);_d_p_c.Cardinality(__product_size);

class="type">int _rsi_handle=iRSI(_Symbol,_Period,__product_size,__product_price);
class="type">int _cci_handle=iCCI(_Symbol,_Period,__product_size,__product_price);
class="type">int _dmk_handle=iDeMarker(_Symbol,_Period,__product_size);
class="type">int _wpr_handle=iWPR(_Symbol,_Period,__product_size);
class="type">int _stc_handle=iStochastic(_Symbol,_Period,class="num">8,class="num">4,class="num">4,MODE_SMA,STO_LOWHIGH);
class="type">int _trx_handle=iTriX(_Symbol,_Period,__product_size,__product_price);

if
(
FillDomain(_d_p_a,class="num">0,__product_size,_rsi_handle)
&&
FillDomain(_d_p_a,class="num">1,__product_size,_cci_handle)
&&
FillDomain(_d_p_b,class="num">0,__product_size,_dmk_handle)
&&
FillDomain(_d_p_b,class="num">1,__product_size,_wpr_handle)
&&
FillDomain(_d_p_c,class="num">0,__product_size,_stc_handle)
&&
FillDomain(_d_p_c,class="num">1,__product_size,_trx_handle)
)
{
 printf(__FUNCSIG__+" domain A: "+PrintDomain(_d_p_a,class="num">2));
 printf(__FUNCSIG__+" domain B: "+PrintDomain(_d_p_b,class="num">2));
 printf(__FUNCSIG__+" domain C: "+PrintDomain(_d_p_c,class="num">5));

 CProduct _product;

 GetProduct(_d_p_a,_d_p_b,_product,class="num">2);
 printf(__FUNCSIG__+" A & B product: "+PrintDomain(_product.cone,class="num">2));

 GetProduct(_product.cone,_d_p_c,_product,class="num">5);

「三域笛卡尔积在 USDCHF 周线上的实测输出」

把 A、B、C 三个区间域做交集扩展,核心就是笛卡尔积。上面这段日志来自 MT5 脚本对 USDCHF.ln 周线(W1)的跑数,时间锚点 2023.02.17 17:31:33,单帧内完成全部打印。 A 域返回 {(-66.67),(66.67)},B 域返回 {(-61.99),(-68.45)},C 域是微观波动区间 {(-0.00996),(-0.00628)}。A 与 B 先乘出 4 个二维组合,再与 C 乘出 8 个三维组合,日志里 A & B & C product 列出了完整 8 元组。 这种展开在外汇周线域上意义直接:把方向区间(A)、动量区间(B)、价格微扰(C)绑成一个状态空间,后续过滤只在这 8 个态里挑。贵金属与外汇杠杆品种这么玩波动会被放大,属高风险验证,参数域边界请自己回测确认。 开 MT5 把 PrintDomain 换成你自己的域函数,改 _product.cone 精度参数(示例里是 5),能立刻看到不同截断下的组合数量变化,可能显著影响后续信号稀疏度。

MQL5 / C++
   printf(__FUNCSIG__+" A & B & C product: "+PrintDomain(_product.cone,class="num">5));
   }

用范畴域抽象金融产品与风险映射

把交易所衍生品看成「范畴里的域」,靠通用属性而非具体合约条款去定义,能直接剥离监管方、期权/期货类别等标识。这类标识不是摆设——识别清算场所与合约形态,便于估算合规成本与交易对手风险敞口,外汇与贵金属杠杆品种的高风险正藏在这些隐性成本里。 下面这段脚本把证券、交易所、期权周期、行权宽度各建为独立域:证券域基数 2(EURUSD、USDJPY),交易所域基数 7(CBOE 到 LSE),期权周期域 3 种(JAJO/FMAN/MJSD),行权宽度域 2 档(1000/1250 点)。跑起来后打印各域内容,本质是在锥域与属性域间构造同态,标记「普遍性」成立。 通用属性还能压住 CDO 这类多层结构的复杂度:把每层标的资产定义为域,再用属性捆出结构,联合风险比逐层硬算更抽象也更通用。换个角度,判定想要的通用属性去反推新产品,比从零拼合约更容易贴近市场需求。再跑一次脚本,同态会把风险水平(例中以 ATR 值指代)映射进由多证券组成的投资组合锥域。

MQL5 / C++
class=class="str">"cmt">//////////////////////////////
class=class="str">"cmt">//PRODUCT UNIVERSAL-PROPERTY
class=class="str">"cmt">//////////////////////////////

CDomain<class="type">class="kw">string> _d_security,_d_exchanges,_d_optioncycle,_d_strikewidth,_d_property;
class=class="str">"cmt">//
CElement<class="type">class="kw">string> _e;_e.Cardinality(class="num">1);
class=class="str">"cmt">//
_d_security.Cardinality(class="num">2);
_e.Set(class="num">0,"EURUSD");_d_security.Set(class="num">0,_e,true);
_e.Set(class="num">0,"USDJPY");_d_security.Set(class="num">1,_e,true);
class=class="str">"cmt">//
_d_exchanges.Cardinality(class="num">7);
_e.Set(class="num">0,"Chicago Board Options Exchange(CBOE)");_d_exchanges.Set(class="num">0,_e,true);
_e.Set(class="num">0,"Shanghai Stock Exchange(SSE)");_d_exchanges.Set(class="num">1,_e,true);
_e.Set(class="num">0,"Shenzhen Stock Exchange(SZSE)");_d_exchanges.Set(class="num">2,_e,true);
_e.Set(class="num">0,"Tokyo Stock Exchange(TSE)");_d_exchanges.Set(class="num">3,_e,true);
_e.Set(class="num">0,"Osaka Exchange(OSE)");_d_exchanges.Set(class="num">4,_e,true);
_e.Set(class="num">0,"Eurex Exchange");_d_exchanges.Set(class="num">5,_e,true);
_e.Set(class="num">0,"London Stock Exchange(LSE)");_d_exchanges.Set(class="num">6,_e,true);
class=class="str">"cmt">//
_d_optioncycle.Cardinality(class="num">3);
_e.Set(class="num">0,"JAJO - January, April, July, and October");_d_optioncycle.Set(class="num">0,_e,true);
_e.Set(class="num">0,"FMAN - February, May, August, and November");_d_optioncycle.Set(class="num">1,_e,true);
_e.Set(class="num">0,"MJSD - March, June, September, and December");_d_optioncycle.Set(class="num">2,_e,true);
class=class="str">"cmt">//
_d_strikewidth.Cardinality(class="num">2);
_e.Set(class="num">0,"class="num">1000 points");_d_strikewidth.Set(class="num">0,_e,true);
_e.Set(class="num">0,"class="num">1250 points");_d_strikewidth.Set(class="num">1,_e,true);
class=class="str">"cmt">//
printf(__FUNCSIG__+" securities domain: "+PrintDomain(_d_security,class="num">0));
printf(__FUNCSIG__+" exchanges domain: "+PrintDomain(_d_exchanges,class="num">0));
printf(__FUNCSIG__+" option cycle domain: "+PrintDomain(_d_optioncycle,class="num">0));
printf(__FUNCSIG__+" strike width domain: "+PrintDomain(_d_strikewidth,class="num">0));

CProduct _product_1;

◍ 把监管属性织进产品锥的映射

前面几步把证券、交易所、期权周期、行权宽度分别做成 cone 域并交叠出 _product_all,这一步要给最终产品挂上监管维度。_d_property 先设基数为 5,再依次塞入 CFTC、CSRC、FSA、BaFin、FCA 五个监管机构字符串,等于给域对象定义了 5 个离散元素。 _product_all.property 指向这块监管域,universality 的 domain/codomain 分别接 property 与 cone,相当于声明「监管方 → 具体品种」的满射框架;CMorphism<string,string> _mm 只是把这套 domain/codomain 再显式绑一遍,方便后续做关系遍历。 真正落地的逻辑在双重 for 循环:外层扫 5 个监管元素,内层扫 cone 里每个品种字符串,当监管等于 CFTC 且 cone 元素里包含 'Chicago Board Options Exchange (CBOE)' 时进入分支——这里用 StringFind(...)>=0 做子串判定,说明 CBOE 品种在模型里被显式归到 CFTC 管辖下。外汇与贵金属跨市场品种若套这套结构,监管归属错配可能导致回测域污染,实盘映射前建议在 MT5 里先 printf 出 _product_all 的 cardinality 核对。 把这段代码直接丢进 EA 的 OnInit 尾部跑一遍,终端里能看到 securities & exchanges & optioncycle & strikewidth product 的 cone 打印,顺带验证 CBOE 是否如预期挂在 CFTC 下;改 _d_property 的 5 个字符串就能切换你关心的监管口径。

MQL5 / C++
GetProduct(_d_security,_d_exchanges,_product_1,class="num">0);
printf(__FUNCSIG__+" securities & exchanges product: "+PrintDomain(_product_1.cone,class="num">0));

CProduct _product_2;

GetProduct(_d_optioncycle,_d_strikewidth,_product_2,class="num">0);
printf(__FUNCSIG__+" securities & exchanges & optioncycle product: "+PrintDomain(_product_2.cone,class="num">0));

CProduct _product_all;

GetProduct(_product_1.cone,_product_2.cone,_product_all,class="num">0);
printf(__FUNCSIG__+" securities & exchanges & optioncycle & strikewidth product: "+PrintDomain(_product_all.cone,class="num">0));

_d_property.Cardinality(class="num">5);
_e.Set(class="num">0,"Commodity Futures Trading Commission(CFTC)");_d_property.Set(class="num">0,_e,true);
_e.Set(class="num">0,"China Securities Regulatory Commission(CSRC)");_d_property.Set(class="num">1,_e,true);
_e.Set(class="num">0,"Financial Services Agency(FSA)");_d_property.Set(class="num">2,_e,true);
_e.Set(class="num">0,"Federal Financial Supervisory Authority(BaFin)");_d_property.Set(class="num">3,_e,true);
_e.Set(class="num">0,"Financial Conduct Authority(FCA)");_d_property.Set(class="num">4,_e,true);

class=class="str">"cmt">//
_product_all.class="kw">property=_d_property;
class=class="str">"cmt">//
_product_all.universality.domain=_product_all.class="kw">property;
_product_all.universality.codomain=_product_all.cone;
class=class="str">"cmt">//
CMorphism<class="type">class="kw">string,class="type">class="kw">string> _mm;
_mm.domain=_product_all.class="kw">property;
_mm.codomain=_product_all.cone;
class=class="str">"cmt">//

for(class="type">int c=class="num">0;c<_product_all.class="kw">property.Cardinality();c++)
{
  CElement<class="type">class="kw">string> _e_property;_e_property.Cardinality(class="num">1);
  if(_product_all.class="kw">property.Get(c,_e_property) && _e_property.Get(class="num">0)!="")
  {
    for(class="type">int cc=class="num">0;cc<_product_all.cone.Cardinality();cc++)
    {
      CElement<class="type">class="kw">string> _e_cone;_e_cone.Cardinality(class="num">1);
      if(_product_all.cone.Get(cc,_e_cone) && _e_cone.Get(class="num">0)!="")
      {
        if(_e_property.Get(class="num">0)=="Commodity Futures Trading Commission(CFTC)")
        {
          if(StringFind(_e_cone.Get(class="num">0),"Chicago Board Options Exchange(CBOE)")>=class="num">0)
          {

常见问题

给数组加边界守卫:存取前先判断索引是否落在[0, size-1],越界返回空构造对象并记录日志,不要直接下标访问。
建一个包装类持有原投影器指针,重载ToString()输出域标签与值,再用数组统一管理这些字符串域实例。
可以。小布能读取你定义的域与映射,自动跑笛卡尔积并标出空单元格和重复组合,省去手动比对。
按出现频次和后续K线反转率排序,只保留频次>5%且反转率>55%的组合,其余折叠收起。
在锥的态射里写监管因子函数,输入品种标签返回杠杆上限与预警级别,每次切换品种自动重算并提示高风险。