MQL5 范畴论中的幺半群:用结构化代数给交易决策建模(基础篇)
(1/3)· 多数交易者凭直觉组合入场参数,却从没想过它们能套进一个带幺元和结合律的代数结构
◍ 用幺半群看 MQL5 里的数据拼接
在 MQL5 语境下,幺半群(Monoid)可理解成一种带「单位元」且支持结合律的运算结构。最直观的例子是字符串拼接:空串 "" 是单位元,任意字符串 s 满足 "" + s == s,且 (a+b)+c == a+(b+c)。 这种抽象对写指标或 EA 的日志模块有用。把多次状态变更先攒进一个序列,再统一折叠(fold)成一条可读消息,比边算边 Print 更可控,也方便后续接小布盯盘的知识库做结构化解析。 下面这段演示了用字符串幺半群累积信号描述,外汇与贵金属波动剧烈,此类拼接仅作信息组织,不预示任何方向。
class="type">class="kw">string signalLog = ""; class=class="str">"cmt">// 单位元:空串 signalLog += "MA_cross "; class=class="str">"cmt">// 左结合拼接 signalLog += "RSI_over70 "; class=class="str">"cmt">// 继续拼接 signalLog = signalLog + "Vol_spike"; class=class="str">"cmt">// 等价 (signalLog+"RSI_over70")+"Vol_spike" Print(signalLog); class=class="str">"cmt">// 输出累积后的信号链
「用幺半群给交易数据建模」
把范畴论里多集、相对集那套放下,下一步要落地的是幺半群(monoid)。它在算法交易里不是数学装饰,而是给一串价格或信号做结构化聚合的底座。 一个幺半群由三样东西构成:一个元素集合、一个二元运算、以及一个幺元。二元运算吃进集合里任意两个元素,吐出来的还得是同一个集合里的成员;幺元 special 在——它和任何元素做运算,结果都是那个元素本身。 这个运算还得满足结合律,也就是 (a*b)*c 和 a*(b*c) 是一回事。对 MT5 玩家来说,这意味着你把 tick 流按时间窗切片再合并,顺序不同不应改变最终统计量,否则你的指标在重算时会漂移。 形式上,设幺半群 M 含元素 a、b、c,幺元 e,二元运算 *,则约束写为:e*a=a*e=a,且 (a*b)*c=a*(b*c)。拿这个去核对你自己的累加器类,若不满足,回测里的样本外结果可能只是巧合。
二元运算里的结合律到底在管什么
在群结构里,等式 1 约束的是任意两个成员做配对运算,结果仍落回集合内部,这是封闭性的底线。等式 2 与 3 把幺元拎出来单独说:只要参与运算的元素不是幺元,输出就还是那个二元运算里的普通元素,幺元自身不改变别的元素的状态。 最后这条被标作 4 的关系式,盯的是运算符号 * 的结合律——也就是 a*(b*c) 和 (a*b)*c 在群里面必须等价。 对写 EA 的人而言,这意味着你若把订单操作、指标状态封装成某种代数结构,嵌套调用的顺序不能悄悄改变结果;在 MT5 里验证时,可故意构造三层嵌套计算看浮点误差是否破坏该等价性。外汇与贵金属杠杆高,模型假设失真会放大实盘风险。
◍ 把交易决策拆成五个幺半群
用范畴论的幺半群视角看交易,核心是把每次下单前的判断切成可组合的离散决策。这里先列 5 个大多数交易者绕不开的点:回溯期长度、图表时间帧、应用价格类型、选用的指标,以及拿到上述信息后按区间还是趋势交易。不同人关键决策点会有出入,但这 5 个足以跑通一套最小原型,并非标准流程。 对每个决策点,都要配套三样东西:一个可取数值的集合、一个只在两个元素间二选一的二元运算、以及该集合里幺元所在的数组下标。二元运算在 MQL5 里可写成一个方法,由上层方法迭代调用,扫完集合做出唯一选择。 二选一的运算本身有四种倾向:取较小值、取较大值、取最接近两值中点的那个、取离中点最远的那个。实际写的时候,用数组索引定位幺元比用对象引用更省事,也方便回测时直接打印集合状态。 外汇与贵金属波动剧烈、杠杆风险高,这类结构只解决‘决策怎么编排’,不预示任何方向。开 MT5 把下面代码塞进脚本,改 SELECT_MODE 就能看不同二元运算在回溯期集合上的取舍。
class="type">int SelectBinary(const class="type">class="kw">double &arr[], class="type">int i, class="type">int j, class="type">int mode) { if(mode==class="num">0) class="kw">return (arr[i]<arr[j])? i : j; class=class="str">"cmt">// 取较小元素索引 if(mode==class="num">1) class="kw">return (arr[i]>arr[j])? i : j; class=class="str">"cmt">// 取较大元素索引 class="type">class="kw">double mid=(arr[i]+arr[j])/class="num">2.0; if(mode==class="num">2) class=class="str">"cmt">// 取离中点最近 class="kw">return (MathAbs(arr[i]-mid)<=MathAbs(arr[j]-mid))? i : j; class="kw">return (MathAbs(arr[i]-mid)>=MathAbs(arr[j]-mid))? i : j; class=class="str">"cmt">// 取离中点最远 }
「验证数据是否满足幺半群公理」
把行情序列拿来做代数建模前,先确认它构成定义明确的幺半群:必须存在幺元,且二元运算满足封闭与结合。否则后续分类或特征提取会得到自相矛盾的结果。 封闭性最容易踩坑。若运算结果跑出原集合,结构即被破坏——例如用自然数配减法,3−5=−2 已不在自然数内,这种“运算”严格说只是个返回同集合元素的映射规则,不能当幺半群运算用。 结合性缺失也会让分组结果漂移。以矩阵为元素、乘法为运算时,(a*b)*c 与 a*(b*c) 通常不等,三元组合顺序一变含义就乱。幺元缺失同样致命:实数配除法就没有幺元,因为除以零无定义,中性元素不存在。 可直接对照三个合规样例:整数配加法(幺元0,封闭且结合)、非零有理数配乘法(幺元1)、字符串配串联(幺元空串"")。这三类在 MT5 里做状态聚合或符号序列编码时,能安全当作幺半群使用。外汇与贵金属波动具高风险,任何代数结构仅辅助刻画价格行为,不预示方向。
别让聚合结果变成黑箱
把若干片段聚合成一个分类结论时,真正决定结论有没有用的,是你在聚合前给每个维度赋予的语义。幺半群运算本身只是求和、串联或取最值,但若「高频词=专注主题」「低频词=内容发散」这种映射没定义清楚,最终分出来的类别只是数字堆,读不出业务含义。 拿情绪打分举例:一条评价「产品不错。但是,客户服务不尽如人意。」若整句糊里糊涂给个传闻分数,会因为「不错」二字偏多被判正面。拆成两句分别赋分——前句 0.8、后句 -0.7,幺半群求和得 0.1,按「[-1,1] 区间、靠近 0 为中性」的语义才该归为「中性或略微正面」。这一步拆句和映射,就是可解释性的地基。 图像分类也能平移到盘面上:把「浅色简单」映射为猫、「深色复杂」映射为狗,等价于把「实体短小+回撤浅」判为看涨头肩、「实体粗长+回撤深」判为看跌结构。组合特征若不加语义注解,模型只会吐出「浅色简单」字符串,交易者无从下单。 时间序列上,把价格、成交量、波动率用幺半群拼起来分趋势,语义怎么定直接牵动决策。同样一个「价升+量缩+波平」的聚合结果,定义成「蓄势」还是「背离」会导出完全相反的仓位倾向。外汇与贵金属波动剧烈、滑点频繁,这类语义误读可能引发高风险敞口,定义映射时必须留人工复核余地。 客户细分里「高价值」「易流失」的标签也一样:聚合交易笔数或属性后,标签词义不写清,细分就只是数据库里的代号。五个例子串起来只有一句话——运算能自动化,语义必须人拍板,否则分类只是看起来合理。