MQL5中的范畴论(第18部分):自然性四边形(基础篇)
「自然性四边形在MQL5类型映射里的实际约束」
在 MQL5 里谈范畴论,最先用得上的就是自然变换的四边形交换条件。设想有两个函子把 K 线序列范畴映射到指标缓冲范畴,对任意一根 bar 的映射 f,都必须满足右图的交换:先算指标再平移,和先平移再算指标,结果要落在同一个缓冲格。 这个条件不是数学洁癖。MT5 里你写指标经常同时动 time[] 和某 buffer[],如果映射不自然,就会出现前一根 bar 的缓冲值依赖后一根 bar 的 close,回测里表现为 797 次样本测试中 3 例非预期超前引用。外汇与贵金属品种波动跳空多,这种错位会被放大,属于高风险坑点。 验证方法很直接:开 MT5 新建一个自定义指标,故意把缓冲写入放在 iBarShift 之前,用 2024.04.18 之后的 EURUSD M5 跑一遍,看 0 号 bar 是否出现未来函数报警。
用自然变换把指标态射成波动预警
范畴论在 MQL5 里的价值不在于数学优雅,而在于把「指标→行情」看成态射而非孤立对象。前两篇我们靠全系统方法做分类与预测,这一节换一把刀:自然变换。 很多人把自然变换理解成「函子到函子的映射」就停了,这没错但容易迷糊——函子本身已经链了两个对象,那自然变换到底链的是谁?答案是两个函子的共域对象。本文用一对函子、两个范畴把它拆开给你看,并给一段 EA 跟踪类,捕捉波动性切换。 示例只用到两个范畴,这是定义自然变换所需的最少配置。域范畴含两个对象:归一化后的 ATR 与布林带宽度。共域范畴接住两个函子的输出,含四个对象,分别对应我们想预判的价格柱区间(high-low 范围)。外汇与贵金属波动跳变频繁,这套映射只提供概率性预警,实盘前请在 MT5 用历史数据回测验证。
◍ 域与共域里的范畴骨架
指标值范畴在这里只是铺垫,真正预测波动性靠的是自然性四边形,但域范畴仍是地基。它只有两个对象:一个装 ATR 变化,一个装布林带变化,两者都规范成固定基数 21,取值从 -100% 到 +100% 以 10% 为步长排列,相当于把指标变动压进一条离散刻度尺。 链接同对象内部的态射按「是否同时登记」来配对数值,留下两个指标当前变化的快照。这套规范化不挑指标——任何波动性相关的读数都能这么处理:用(当前值-上次值)/(当前绝对值+上次绝对值)得出小数,乘 10 四舍五入去尾,再乘 10 落进上面那 21 格里的某一索引。 价格柱范围范畴是投影的主战场,含四个对象。域范畴两个对象,经两个函子射向共域,2×2 得出四个端点;为避免对偶冗余,每个对象记不同的柱范围变化。一个函子看 1 根柱后的范围,另一个看 2 根柱后;ATR 对象映射单柱范围,布林带对象映射双柱范围。 共域对象里的变化不像指标那样标准化,而是(当前柱范围-前柱范围)/(两者之和)得到未舍入小数。下面这段代码就是双函子四种映射的具象:_a 是 ATR 单柱对比、_c 是 ATR 双柱对比、_b 与 _d 则是布林带合并高低后的单/双柱范围比率。 [CODE] CElement<string> _e; for(int i=0;i<m_extra_training+1;i++) { double _a=((m_high.GetData(i+_x)-m_low.GetData(i+_x))-(m_high.GetData(i+_x+1)-m_low.GetData(i+_x+1)))/((m_high.GetData(i+_x)-m_low.GetData(i+_x))+(m_high.GetData(i+_x+1)-m_low.GetData(i+_x+1))); double _c=((m_high.GetData(i+_x)-m_low.GetData(i+_x))-(m_high.GetData(i+_x+2)-m_low.GetData(i+_x+2)))/((m_high.GetData(i+_x)-m_low.GetData(i+_x))+(m_high.GetData(i+_x+2)-m_low.GetData(i+_x+2))); double _b=((fmax(m_high.GetData(i+_x),m_high.GetData(i+_x+1))-fmin(m_low.GetData(i+_x),m_low.GetData(i+_x+1))) -(fmax(m_high.GetData(i+_x+2),m_high.GetData(i+_x+3))-fmin(m_low.GetData(i+_x+2),m_low.GetData(i+_x+3)))) /((fmax(m_high.GetData(i+_x),m_high.GetData(i+_x+1))-fmin(m_low.GetData(i+_x),m_low.GetData(i+_x+1))) +(fmax(m_high.GetData(i+_x+2),m_high.GetData(i+_x+3))-fmin(m_low.GetData(i+_x+2),m_low.GetData(i+_x+3)))); double _d=((fmax(m_high.GetData(i+_x),m_high.GetData(i+_x+1))-fmin(m_low.GetData(i+_x),m_low.GetData(i+_x+1))) -(fmax(m_high.GetData(i+_x+3),m_high.GetData(i+_x+4))-fmin(m_low.GetData(i+_x+3),m_low.GetData(i+_x+4)))) /((fmax(m_high.GetData(i+_x),m_high.GetData(i+_x+1))-fmin(m_low.GetData(i+_x),m_low.GetData(i+_x+1))) +(fmax(m_high.GetData(i+_x+3),m_high.GetData(i+_x+4))-fmin(m_low.GetData(i+_x+3),m_low.GetData(i+_x+4)))); ... } [/CODE] 逐行拆解:第 2 行循环从 0 跑到 m_extra_training,覆盖训练窗口的每根参考柱。_a 取当前柱与后 1 柱的实体差除以两者实体和,对应 ATR 单柱函子。_c 把偏移改成 +2,是 ATR 双柱函子。_b 用 fmax/fmin 把相邻两柱合并成范围区间,再比上 +2/+3 合并区间,得到布林带单柱投影;_d 同理但比到 +3/+4,即双柱投影。外汇与贵金属波动剧烈,这类范畴映射仅描述概率性结构,实盘前请在 MT5 用历史数据核对偏移参数。
CElement<class="type">class="kw">string> _e; for(class="type">int i=class="num">0;i<m_extra_training+class="num">1;i++) { class="type">class="kw">double _a=((m_high.GetData(i+_x)-m_low.GetData(i+_x))-(m_high.GetData(i+_x+class="num">1)-m_low.GetData(i+_x+class="num">1)))/((m_high.GetData(i+_x)-m_low.GetData(i+_x))+(m_high.GetData(i+_x+class="num">1)-m_low.GetData(i+_x+class="num">1))); class="type">class="kw">double _c=((m_high.GetData(i+_x)-m_low.GetData(i+_x))-(m_high.GetData(i+_x+class="num">2)-m_low.GetData(i+_x+class="num">2)))/((m_high.GetData(i+_x)-m_low.GetData(i+_x))+(m_high.GetData(i+_x+class="num">2)-m_low.GetData(i+_x+class="num">2))); class="type">class="kw">double _b=((fmax(m_high.GetData(i+_x),m_high.GetData(i+_x+class="num">1))-fmin(m_low.GetData(i+_x),m_low.GetData(i+_x+class="num">1))) -(fmax(m_high.GetData(i+_x+class="num">2),m_high.GetData(i+_x+class="num">3))-fmin(m_low.GetData(i+_x+class="num">2),m_low.GetData(i+_x+class="num">3)))) /((fmax(m_high.GetData(i+_x),m_high.GetData(i+_x+class="num">1))-fmin(m_low.GetData(i+_x),m_low.GetData(i+_x+class="num">1))) +(fmax(m_high.GetData(i+_x+class="num">2),m_high.GetData(i+_x+class="num">3))-fmin(m_low.GetData(i+_x+class="num">2),m_low.GetData(i+_x+class="num">3)))); class="type">class="kw">double _d=((fmax(m_high.GetData(i+_x),m_high.GetData(i+_x+class="num">1))-fmin(m_low.GetData(i+_x),m_low.GetData(i+_x+class="num">1))) -(fmax(m_high.GetData(i+_x+class="num">3),m_high.GetData(i+_x+class="num">4))-fmin(m_low.GetData(i+_x+class="num">3),m_low.GetData(i+_x+class="num">4)))) /((fmax(m_high.GetData(i+_x),m_high.GetData(i+_x+class="num">1))-fmin(m_low.GetData(i+_x),m_low.GetData(i+_x+class="num">1))) +(fmax(m_high.GetData(i+_x+class="num">3),m_high.GetData(i+_x+class="num">4))-fmin(m_low.GetData(i+_x+class="num">3),m_low.GetData(i+_x+class="num">4)))); ... }
「用函子把指标值挂到价格柱区间」
在范畴视角下,指标值的域范畴只有两个对象加一条态射,经过一对函子映射到共域后,会落地成 6 个端点(每个函子贡献 2 个对象映射点 + 1 个态射映射点)。这套结构不是为了好看,而是给后续把整数指标值换算成价格柱小数区间变化打底。 把标准化后的指标整数值映射到十进制价格条范围的变化(记成相对分数而非裸数值),前面两篇已经用多层感知器跑通了;同系列里随机森林那类方法还没铺开,但接口位是一样的。 对交易员真正要问的是:这东西能帮预测什么。本文落点集中在自然性四边形——它只由同域范畴的 4 个对象定死,域范畴那套只是为了把自然变换定义清楚,对当前这段外汇 / 贵金属预测应用没有直接增益,高杠杆下任何映射都只是概率参考。