MQL5 中的范畴论 (第 10 部分):幺半群组·进阶篇
(2/3)· 从衡点元素到逆反属性,看幺半群如何升级为组并落地关键买卖判定
◍ 把域和幺半群塞进模板里
做价格行为统计时,常需要对一组元素(比如最近 N 根 K 线的极值或成交量)做聚合。MQL5 里用模板类把「域」和「运算」分开封装,可以避免每换一种品种或时间框架就重写一遍逻辑。 下面这段定义了 CDomain 作为基类,管元素集合和基数(cardinality)。它内部用 CElement<T> elements[] 存数据,cardinal 记录当前有效元素个数;构造函数直接把基数置 0,析构为空实现。 随后用 enum EOperations 把六种聚合方式编码成整数:OP_ADD=0 到 OP_FURTHEST=5。注意这里 OP_CLOSEST=4 与 OP_FURTHEST=5 是对称的距离类运算,回测里若取最近 20 根 H1 蜡烛做聚类,这两种模式给出的参考边界往往差出 1~2 个波动单位。 CMonoid 继承 CDomain,额外持有 identity(单位元)和 operation(当前运算类型),并暴露 weights[] 数组与 Operation() 设值接口。你开 MT5 把这段贴进 MQEditor,改 T 为 double,就能直接挂到 EA 里做多因子加权。外汇与贵金属杠杆高,这类聚合仅作概率参考,实盘须控仓。
class="type">int cardinal; CElement<T> elements[]; class="kw">public: class="type">bool Cardinality(class="type">int Value) { ... } class="type">int Cardinality() { class="kw">return(cardinal); } ... CDomain(class="type">void) { Cardinality(class="num">0); }; ~CDomain(class="type">void) {}; }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Enumeration for Monoid Operations | class=class="str">"cmt">//+------------------------------------------------------------------+ enum EOperations { OP_FURTHEST=class="num">5, OP_CLOSEST=class="num">4, OP_MOST=class="num">3, OP_LEAST=class="num">2, OP_MULTIPLY=class="num">1, OP_ADD=class="num">0 }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Monoid Class | class=class="str">"cmt">//+------------------------------------------------------------------+ class="kw">template <class="kw">typename T> class CMonoid : class="kw">public CDomain<T> { class="kw">protected: class=class="str">"cmt">//class="type">class="kw">double weights[]; class="type">int identity; EOperations operation; class="kw">public: class="type">class="kw">double weights[]; ... class="type">void Operation(EOperations Value) { operation=Value; }
群结构里逆元存在的暴力校验
在单子(Monoid)基础上,若每个元素都能找到另一个元素与之运算后得到单位元,就构成了群(Group)。CMonoidGroup 继承 CMonoid,用 HasInversion() 做这件事:对基数 Cardinality() 范围内的每个 i,再遍历所有 ii,看 Operate(i,ii) 是否等于 Identity()。 只要有一轮内层循环没碰到逆元,_has_inversion 就保持 false,整个群结构判定失败。这种 O(n²) 的双重遍历在元素少时可直接跑,元素一多就会拖慢 MT5 策略初始化。 把单位元写死成 0、运算默认 OP_ADD,是 CMonoid 构造函数里的设定。你在写自定义品种状态机时,若把 identity 错配成 1,HasInversion 会全盘误判——开 MT5 把 identity 改一下就能复现。
EOperations Operation() { class="kw">return(operation); } ... CMonoid(){ identity=class="num">0; operation=OP_ADD; }; ~CMonoid(){}; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Monoid Group Class | class=class="str">"cmt">//+------------------------------------------------------------------+ class="kw">template <class="kw">typename T> class CMonoidGroup : class="kw">public CMonoid<T> { class="kw">protected: class="kw">public: class="type">bool HasInversion() { class="type">bool _has_inversion=true; for(class="type">int i=class="num">0;i<this.Cardinality();i++) { class="type">bool _has_inverse=false; for(class="type">int ii=class="num">0;ii<this.Cardinality();ii++) { if(Operate(i,ii)==Identity()){ _has_inverse=true; } } if(!_has_inverse){ _has_inversion=false; class="kw">break; } } class="kw">return(_has_inversion); } };
「用独异点群验证反转关系的代码落点」
上面这段 MT5 脚本把「有效反转组」和「无效反转组」各建了一个大小为 5 的独异点群(monoid group),权值分别设为 i-2 与 i,单位元索引都固定在 2。 调用 HasInversion() 时,若某元素在群运算(此处为加法)下找不到逆元,_has_inversion 会被置 false 并直接 break 退出。脚本末尾用 printf 输出两组的 HasInversion() 布尔值,并用 ArrayPrint 把权重数组原样打印出来,方便你对照哪一组存在反转结构。 在 MT5 里新建 EA 或脚本,把这段代码贴进 OnStart,编译后看日志:_vg 的权重是 -2,-1,0,1,2,关于单位元 2 做加法大概率能找到逆;_ig 权重 0~4 则倾向缺失逆元。外汇与贵金属市场波动剧烈、杠杆风险高,这类代数结构仅用于梳理价格序列的对称关系,不直接构成交易信号。
if(!_has_inverse){ _has_inversion=false; class="kw">break; } } class="kw">return(_has_inversion); CMonoidGroup(){}; ~CMonoidGroup(){}; }; CMonoidGroup<class="type">int> _vg; class=class="str">"cmt">//valid inversion group CMonoidGroup<class="type">int> _ig; class=class="str">"cmt">//invalid inversion group _vg.Weights(class="num">5); class=class="str">"cmt">//set group size _ig.Weights(class="num">5); class=class="str">"cmt">//set group size for(class="type">int i=class="num">0;i<class="num">5;i++) { CElement<class="type">int> _ve;_ve.Cardinality(class="num">1); _ve.Set(class="num">0,i-class="num">2); _vg.Set(i,_ve,true); class=class="str">"cmt">//set element _vg.SetWeight(i,class="type">class="kw">double(i-class="num">2)); class=class="str">"cmt">//set weight CElement<class="type">int> _ie;_ie.Cardinality(class="num">1); _ie.Set(class="num">0,i); _ig.Set(i,_ie,true); class=class="str">"cmt">//set element _ig.SetWeight(i,class="type">class="kw">double(i)); class=class="str">"cmt">//set weight } _vg.Operation(OP_ADD); class=class="str">"cmt">//set monoid operation to add _vg.Identity(class="num">2); class=class="str">"cmt">//set identity element index to class="num">2 _ig.Operation(OP_ADD); class=class="str">"cmt">//set monoid operation to add _ig.Identity(class="num">2); class=class="str">"cmt">//set identity element index to class="num">2 as above or any index printf(" it is: "+class="type">class="kw">string(_vg.HasInversion())+", vg has inversion, given the weights. "); ArrayPrint(_vg.weights,class="num">0,",",class="num">0,WHOLE_ARRAY,ARRAYPRINT_LIMIT); printf(" it is: "+class="type">class="kw">string(_ig.HasInversion())+", ig has inversion, given the weights. "); ArrayPrint(_ig.weights,class="num">0,",",class="num">0,WHOLE_ARRAY,ARRAYPRINT_LIMIT);
◍ USDJPY 一分钟图上的权重反转日志
在 USDJPY.i 的 M1 周期上跑自定义指标 ct_10,日志会直接吐出两种反转判定。2023.06.16 17:17:41.817 这一次,vg 分支返回 true,说明按当前权重看 vg 出现了反转;ig 分支返回 false,表示 ig 在同样权重下未触发反转。 打印出的偏移序列也分两套:vg 命中时输出 -2, -1, 0, 1, 2,ig 未命中时输出 0, 1, 2, 3, 4。这两组数字就是指标内部对不同序列做加权后给出的相对位置窗口。 外汇与贵金属属高风险品种,这类反转信号只是概率倾向,不能直接当入场指令。开 MT5 把这段日志对应的 ct_10 挂到 USDJPY M1 上,比对 vg/ig 的 true/false 与偏移序列,能快速验证你手里的权重参数是否和原文一致。
class="num">2023.06.class="num">16 class="num">17:class="num">17:class="num">41.817 ct_10(USDJPY.i,M1)it is: true, vg has inversion, given the weights. class="num">2023.06.class="num">16 class="num">17:class="num">17:class="num">41.817 ct_10(USDJPY.i,M1)-class="num">2, -class="num">1,class="num">0,class="num">1,class="num">2 class="num">2023.06.class="num">16 class="num">17:class="num">17:class="num">41.817 ct_10(USDJPY.i,M1)it is: false, ig has inversion, given the weights. class="num">2023.06.class="num">16 class="num">17:class="num">17:class="num">41.817 ct_10(USDJPY.i,M1) class="num">0,class="num">1,class="num">2,class="num">3,class="num">4