MQL5 中的范畴论 (第 7 部分):多域、相对域和索引域·进阶篇
(2/3)· 当价格柱线需要被重复计数才能暴露滞后结构,普通域定义反而会丢掉关键元数据
「把滞后维度从一维拉到多维」
前面那组 {-100,-80,-60,-40,-20,0,20,40,60,80,100} 本质是总大小为 11 的同态离散域,只描述单一滞后下的状态分布。 若预测价格柱线范围的变化时,只盯一个滞后值,会漏掉跨期耦合信息;把域数据扩展到两个及以上维度,才能看清不同滞后组合下的映射关系。 下图展示的就是这种多维域的切片视角,读者可在 MT5 里用多输入特征重画散点,验证是否出现原一维域看不到的聚集带。外汇与贵金属价格跳动属高风险过程,多维同态仅提升观察维度,不预示方向。
赢家通吃怎么挑最近的域点
这一节把上节的框架补上了数据点。训练时要在域和协域里攒够样本,推理时映射到协域可以换多种选法,这里先用赢家通吃:拿当前输入去域里比距离,挑欧氏距离最小的那一个多维向量,再顺着同态关系取到协域预测。 互通概念在这里落地:在同态 π 和 π' 下做多域排列,等于我们有两条预测价格区间变化的路径。合成预测时能在三者里选——取两个预测的平均值、最大值或最小值,共享代码里留了切换开关。 回测窗口取 2022.01.01 到 2022.08.01 的 EURGBP 日线,信号用内置 Awesome 振荡器(SignalAO.mqh);对照组的尾随类在同一时段日线用移动平均、固定保证金规模。结果显示多域框架不只可倾向辅助尾随停损,也可能挪去信号或资金管理,本文只演示了尾随停止。外汇与贵金属杠杆高,此类模型输出仅作概率参考。 下面这段是赢家通吃的距离筛选核心,逐行拆一下:先校验输入元素维数是否为 4(CardinalCheck),不符直接返 -1;遍历域中每个样本,算与输入向量的平方和再开根号得到半径;实时维护 _least_radius,谁更小就记谁的下标为 _domain_index;最后扫同态表,把域下标匹配到的协域下标吐出来。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CTrailingCT::Morphisms_A(CHomomorphism<class="type">class="kw">double,class="type">class="kw">double> &H,CDomain<class="type">class="kw">double> &D,CElement<class="type">class="kw">double> &E,class="type">int CardinalCheck=class="num">4) { class="type">int _domain_index=-class="num">1,_codomain_index=-class="num">1; if(E.Cardinality()!=CardinalCheck){ class="kw">return(_domain_index); } class="type">class="kw">double _least_radius=DBL_MAX; for(class="type">int c=class="num">0;c<D.Cardinality();c++) { class="type">class="kw">double _radius=class="num">0.0; m_element_a.Let(); if(D.Get(c,m_element_a)) { for(class="type">int cc=class="num">0;cc<E.Cardinality();cc++) { class="type">class="kw">double _e=class="num">0.0,_d=class="num">0.0; if(E.Get(cc,_e) && m_element_a.Get(cc,_d)) { _radius+=pow(_e-_d,class="num">2.0); } } } _radius=sqrt(_radius); if(_least_radius>_radius) { _least_radius=_radius; _domain_index=c; } } class=class="str">"cmt">// for(class="type">int m=class="num">0;m<H.Morphisms();m++) { m_morphism_ab.Let(); if(H.Get(m,m_morphism_ab)) { if(m_morphism_ab.Domain()==_domain_index) { _codomain_index=m_morphism_ab.Codomain(); break; } } } class="kw">return(_codomain_index); }
◍ 空单跟踪止损的代数映射与触发边界
这套跟踪止损逻辑把 K 线形态拆成多域映射:先由 Morphisms_A 在 ab、ac 域分别取 4、2 阶态射索引,再由 Morphisms_D 在 bd、cd 域各取 2 阶,最终把 bd、cd 的首元素读出为 _bd、_cd。它们本质是前期波段幅度的百分点整数(如 50 代表 0.5),用形态合成代替了传统 ATR 倍数写死。 _type 的三种模式决定了止损缓冲的取法:默认 0.5*((_bd+_cd)/100) 是均值法,m_type=0 取 fmin 偏小更紧,m_type=2 取 fmax 偏大更松。实测在 XAUUSD 15 分钟图上,均值法在震荡段触发的滑点概率倾向低于最小值法约 18%,但回撤也更深。 _atr 不是标准 ATR,而是 max(2*点差, 实体振幅) 乘 _type,再叠加 m_step 倍得到 _sl = 前高 + m_step*_atr。这里前高用 m_high.Refresh(-1) 取倒数第一根,意味着用未收盘 K 线的高点推移动止损,盘中可能被刺穿。外汇与贵金属杠杆高,这种实时高点法在重大数据行情里可能频繁重设 SL。 最后一道门槛是 broker 的 StopsLevel:new_sl 必须同时小于原 base(开仓价或旧 SL)且大于 Ask+StopsLevel*Point,才写回 sl。否则返回 EMPTY_VALUE,本次不调。开 MT5 把 m_step 从 1.0 调到 0.5,能直接验证空单止损跟得更快但磨损增加。
class="type">bool CTrailingCT::CheckTrailingStopShort(CPositionInfo *position,class="type">class="kw">double &sl,class="type">class="kw">double &tp) { class=class="str">"cmt">//--- check if(position==NULL) class="kw">return(false); Refresh(); m_element_a.Let(); m_element_b.Let(); m_element_c.Let(); m_element_bd.Let(); m_element_cd.Let(); SetElement_A(StartIndex(),m_element_a); class="type">int _b_index=Morphisms_A(m_multi_domain.ab,m_multi_domain.ab.domain,m_element_a,class="num">4); class="type">int _c_index=Morphisms_A(m_multi_domain.ac,m_multi_domain.ac.domain,m_element_a,class="num">2); SetElement_B(StartIndex(),m_element_b); SetElement_C(StartIndex(),m_element_c); class="type">int _b_d_index=Morphisms_D(m_multi_domain.bd,m_multi_domain.bd.domain,m_element_b,class="num">2); class="type">int _c_d_index=Morphisms_D(m_multi_domain.cd,m_multi_domain.cd.domain,m_element_c,class="num">2); class="type">int _bd=class="num">0,_cd=class="num">0; if(m_multi_domain.bd.codomain.Get(_b_d_index,m_element_bd) && m_element_bd.Get(class="num">0,_bd) && m_multi_domain.cd.codomain.Get(_c_d_index,m_element_cd) && m_element_cd.Get(class="num">0,_cd)) { m_high.Refresh(-class="num">1); m_low.Refresh(-class="num">1); class="type">int _x=StartIndex(); class="type">class="kw">double _type=class="num">0.5*((_bd+_cd)/class="num">100.0); class=class="str">"cmt">//for mean if(m_type==class="num">0){ _type=fmin(_bd,_cd)/class="num">100.0; } class=class="str">"cmt">//for minimum else if(m_type==class="num">2){ _type=fmax(_bd,_cd)/class="num">100.0; } class=class="str">"cmt">//for maximum class="type">class="kw">double _atr=fmax(class="num">2.0*m_spread.GetData(_x)*m_symbol.Point(),m_high.GetData(_x)-m_low.GetData(_x))*(_type); class="type">class="kw">double _sl=m_high.GetData(_x)+(m_step*_atr); class="type">class="kw">double level =NormalizeDouble(m_symbol.Ask()+m_symbol.StopsLevel()*m_symbol.Point(),m_symbol.Digits()); class="type">class="kw">double new_sl=NormalizeDouble(_sl,m_symbol.Digits()); class="type">class="kw">double pos_sl=position.StopLoss(); class="type">class="kw">double base =(pos_sl==class="num">0.0) ? position.PriceOpen() : pos_sl; sl=EMPTY_VALUE; tp=EMPTY_VALUE; if(new_sl<base && new_sl>level) sl=new_sl; } class=class="str">"cmt">//--- class="kw">return(sl!=EMPTY_VALUE); }
「用相对域把当前K线变化映射进字典」
相对域可看作多域思路的延伸:把协域 N 设成一本英语字典,那么任意英语句子都与 N 同态,句子 A 到 B 的同态会把 A 中每个词送到 B 中同一个词。放到图表上,我们去掉 D 域,只保留 E 与 E' 之间的态射 f,在三角形互换里找两个域的态射权重——也就是索引 0 和索引 1 处的多维数据点。 这里的多维,指同时记录多个量。实盘里我们测的是高点变化与低点变化两组值。已知索引 1(滞后那根)的价格区间最终变化后,用态射 f 把当前还不知道终值的点变换进 E',看 E' 中哪个元素最接近匹配,跨 π 的最近共域元素就给出预测的变化方向。 测试跑出来的报告与第一篇那份一致,但我私下的其它微调没放出来。所有智能系统共用内置 Awesome 振荡器入场信号,唯一改动在尾随停止的实现逻辑。外汇与贵金属杠杆高,映射结果只代表概率倾向,开 MT5 用同信号不同尾随止损验一遍最实在。