MQL5中的范畴论(第19部分):自然性四边形归纳法·进阶篇
(2/3)· 当 USDJPY、EURJPY、EURUSD 被装进同一个函子网络,转通验证能替你预判信号边界
不少交易者把套利三件套当成静态价差表,忽略了价格点在时间序里的态射链。一旦把域范畴里的每个报价都看作对象,传统的相关性肉眼就很难追了。本文接着上篇的自然变换,直接把四边形拿来做归纳。
◍ 用脚本搭起四边形归纳的数据管道
在 MT5 的 IDE 里除了 EA 和指标,脚本也能跑通整套自然性四边形归纳;本文选脚本而非多货币信号类,是因为多货币信号虽能写,但在非优化环境里不够顺手。我们直接打印 EURUSD 收盘变化的预测值与实际值,用结果去支持或反驳「四边形归纳对预测有用」这个假设,外汇与贵金属市场波动剧烈,结论仅代表概率倾向。 脚本先建一个域范畴装 USDJPY 收盘变化,再建共域范畴装 EURJPY 与 EURUSD 两个序列,分别叫 b、c,每个价位是一个对象,必须按顺序塞进去才能保留序列结构。n 次归纳会把共域里的对象框成角 A、B、C、D 的四边形,AB 和 CD 是变换、AC 和 BD 是态射,映射成 RDF 时可用矩阵填数。 训练前先填四个 RDF 的矩阵(AB/CD/AC/BD 各一),声明模型后逐个训练,重点看 info 整数参数——和 MLP 一样必须为正,四个 info 全正才进前瞻测试。脚本输入含训练起止与测试止三个日期,训练止日兼作测试起日,使用前务必确认三日期递增,否则逻辑会错。 前瞻测试里我们盯的是正方形角 D(即 EURUSD 变化):严格同向、涉零可能同向、未命中三类分别计数。整套跑完,你开 MT5 把 USDJPY/EURJPY/EURUSD 三个对灌进去,调 __n_inductions 就能复现这套非优化预测。
class=class="str">"cmt">//create domain series(USDJPY-rates) in first category for training CCategory _c_a_train;_c_a_train.Let(); class="type">int _a_size=GetObject(_c_a_train,__currency_a,__training_start,__training_stop); class=class="str">"cmt">//create class="num">2 series(EURJPY and EURUSD rates) in second category for training CCategory _c_bc_train;_c_bc_train.Let(); class="type">int _b_trains=GetObject(_c_bc_train,__currency_b,__training_start,__training_stop); class="type">int _c_trains=GetObject(_c_bc_train,__currency_c,__training_start,__training_stop); class=class="str">"cmt">//create natural transformation, by induction across, n squares..., cpi to pmi class=class="str">"cmt">//mapping by random forests class="type">int _training_size=fmin(_c_trains,_b_trains); class="type">int _info_ab=class="num">0,_info_bd=class="num">0,_info_ac=class="num">0,_info_cd=class="num">0; CDFReport _report_ab,_report_bd,_report_ac,_report_cd; CMatrixDouble _xy_ab;_xy_ab.Resize(_training_size,class="num">1+class="num">1); CMatrixDouble _xy_bd;_xy_bd.Resize(_training_size,class="num">1+class="num">1); CMatrixDouble _xy_ac;_xy_ac.Resize(_training_size,class="num">1+class="num">1); CMatrixDouble _xy_cd;_xy_cd.Resize(_training_size,class="num">1+class="num">1); class="type">class="kw">double _a=class="num">0.0,_b=class="num">0.0,_c=class="num">0.0,_d=class="num">0.0; CElement<class="type">class="kw">string> _e_a,_e_b,_e_c,_e_d; class="type">class="kw">string _s_a="",_s_b="",_s_c="",_s_d=""; for(class="type">int i=class="num">0;i<_training_size-__n_inductions;i++) { _s_a="";_e_a.Let();_c_bc_train.domain[i].Get(class="num">0,_e_a);_e_a.Get(class="num">1,_s_a);_a=StringToDouble(_s_a); _s_b="";_e_b.Let();_c_bc_train.domain[i+_b_trains].Get(class="num">0,_e_b);_e_b.Get(class="num">1,_s_b);_b=StringToDouble(_s_b); _s_c="";_e_c.Let();_c_bc_train.domain[i+__n_inductions].Get(class="num">0,_e_c);_e_c.Get(class="num">1,_s_c);_c=StringToDouble(_s_c); _s_d="";_e_d.Let();_c_bc_train.domain[i+_b_trains+__n_inductions].Get(class="num">0,_e_d);_e_d.Get(class="num">1,_s_d);_d=StringToDouble(_s_d); if(i<_training_size-__n_inductions) { _xy_ab[i].Set(class="num">0,_a); _xy_ab[i].Set(class="num">1,_b);
「随机森林训练与样本外推演的接线方式」
上面这段把四组货币对特征向量(ab/bd/ac/cd)塞进随机森林,用的是 MT5 自带的 CForest 接口,不是自己手搓决策树。每一组都调用 DFBuildRandomDecisionForest,训练样本数由 _training_size - __n_inductions 决定,特征维度和目标维度都写死为 1,树的数量与抽样比例由 __training_trees 和 __training_r 控制。
四个森林对象 _rdf_ab、_rdf_bd、_rdf_ac、_rdf_cd 是并行建出来的,建完用 _info_* 大于 0 做健康检查——只要有一个森林返回码非正,后面测试分支就不会进。这一步能挡掉大部分训练数据不足的静默失败。
推演阶段用 CopyRates 拉 __currency_c 的 K 线,要求返回不少于 _testing_size + __n_inductions + 1 根;循环从 i = __n_inductions + _testing_size 倒序到 __n_inductions + 1,把域里取出的字符串转成 double 喂给 _x_ab[0],再调 DFProcess 拿 _y_ab 输出。外汇与贵金属杠杆高,这套流程只是特征映射实验,实盘信号概率性成立,不代表方向必现。
别把 _info 检查当装饰。很多自己改代码的人删掉 if(_info_ab>0 && ...) 直接跑,结果某组森林空模型,DFProcess 吐出来全是 0,还以为是行情失效。
_xy_bd[i].Set(class="num">0,_b); _xy_bd[i].Set(class="num">1,_d); _xy_ac[i].Set(class="num">0,_a); _xy_ac[i].Set(class="num">1,_c); _xy_cd[i].Set(class="num">0,_c); _xy_cd[i].Set(class="num">1,_d); } } CDForest _forest; CDecisionForest _rdf_ab,_rdf_cd; CDecisionForest _rdf_ac,_rdf_bd; _forest.DFBuildRandomDecisionForest(_xy_ab,_training_size-__n_inductions,class="num">1,class="num">1,__training_trees,__training_r,_info_ab,_rdf_ab,_report_ab); _forest.DFBuildRandomDecisionForest(_xy_bd,_training_size-__n_inductions,class="num">1,class="num">1,__training_trees,__training_r,_info_bd,_rdf_bd,_report_bd); _forest.DFBuildRandomDecisionForest(_xy_ac,_training_size-__n_inductions,class="num">1,class="num">1,__training_trees,__training_r,_info_ac,_rdf_ac,_report_ac); _forest.DFBuildRandomDecisionForest(_xy_cd,_training_size-__n_inductions,class="num">1,class="num">1,__training_trees,__training_r,_info_cd,_rdf_cd,_report_cd); class=class="str">"cmt">// if(_info_ab>class="num">0 && _info_bd>class="num">0 && _info_ac>class="num">0 && _info_cd>class="num">0) { class=class="str">"cmt">//create class="num">2 objects(cpi and pmi) in second category for testing CCategory _c_cp_test;_c_cp_test.Let(); class="type">int _b_test=GetObject(_c_cp_test,__currency_b,__training_stop,__testing_stop); ... ... class="type">MqlRates _rates[]; ArraySetAsSeries(_rates,true); if(CopyRates(__currency_c,Period(), class="num">0, _testing_size+__n_inductions+class="num">1, _rates)>=_testing_size+__n_inductions+class="num">1) { ArraySetAsSeries(_rates,true); for(class="type">int i=__n_inductions+_testing_size;i>__n_inductions;i--) { _s_a="";_e_a.Let();_c_cp_test.domain[i].Get(class="num">0,_e_a);_e_a.Get(class="num">1,_s_a);_a=StringToDouble(_s_a); class="type">class="kw">double _x_ab[],_y_ab[]; ArrayResize(_x_ab,class="num">1); ArrayResize(_y_ab,class="num">1); ArrayInitialize(_x_ab,class="num">0.0); ArrayInitialize(_y_ab,class="num">0.0); class=class="str">"cmt">// _x_ab[class="num">0]=_a; _forest.DFProcess(_rdf_ab,_x_ab,_y_ab); ...
用双模型一致信号做方向校验
这段逻辑把 ABD 与 ACD 两个随机森林子模型的输出做交叉验证,只有当两者对货币对 c 的变化方向同号时才生成合并预测:_c_forecast 取两者均值的一半加权(0.5 系数),若异号则预测值保持 0.0 不参与计数。 实际落地时,预测值会和历史 K 线真实波动 _c_actual 比对:_c_actual 取 i-__n_inductions 根 K 线的 close 与当前 close 之差。若预测与真实同号计 _strict_match,仅零界同侧计 _generic_match,否则 _miss。 在 MT5 里把 __n_inductions 设为 50、回测 EURUSD 日线时,可能出现 _strict_match 占比约 58% 的现象,说明双模型一致信号对趋势延续有一定倾向,但外汇高风险下仍可能失效。
class="type">class="kw">double _x_cd[],_y_cd[]; ArrayResize(_x_cd,class="num">1); ArrayResize(_y_cd,class="num">1); ArrayInitialize(_x_cd,class="num">0.0); ArrayInitialize(_y_cd,class="num">0.0); class=class="str">"cmt">// _x_cd[class="num">0]=_y_ac[class="num">0]; _forest.DFProcess(_rdf_cd,_x_cd,_y_cd); class="type">class="kw">double _c_forecast=class="num">0.0; if((_y_bd[class="num">0]>class="num">0.0 && _y_cd[class="num">0]>class="num">0.0)||(_y_bd[class="num">0]<class="num">0.0 && _y_cd[class="num">0]<class="num">0.0))class=class="str">"cmt">//abd agrees with acd on currency c change { _c_forecast=class="num">0.5*(_y_bd[class="num">0]+_y_cd[class="num">0]); } class="type">class="kw">double _c_actual=_rates[i-__n_inductions].close-_rates[i].close; if((_c_forecast>class="num">0.0 && _c_actual>class="num">0.0)||(_c_forecast<class="num">0.0 && _c_actual<class="num">0.0)){ _strict_match++; } else if((_c_forecast>=class="num">0.0 && _c_actual>=class="num">0.0)||(_c_forecast<=class="num">0.0 && _c_actual<=class="num">0.0)){ _generic_match++; } else { _miss++; } class=class="str">"cmt">// ...
◍ 归纳次数调高后匹配率怎么走
在 USDJPY 日线图上跑那段共享脚本,归纳数设为 1 时日志给出:misses 45、strict matches 61、generic matches 166,严格匹配占比 0.58,含泛化匹配占比 0.83。 把归纳数加到 2,misses 升到 56,strict matches 微涨到 63,generic matches 降到 153;严格占比跌到 0.53,泛化占比 0.79。整体仍是严格匹配多于未命中,但边际收益在收敛。 这种预测和记录下来的匹配准确率,得放进实盘逻辑里、用不同归纳滞后去跑才可能确认。外汇和贵金属波动剧烈、杠杆风险高,光看回测日志不能断定某组参数就理想。 原生的难点是:MQL5 向导生成的 EA 信号类默认只加载当前图表的品种缓冲,要多品种得从 CExpert 继承写自定义类改装配。篇幅所限不展开,你可以自己接这个类验证 5~8 滞后区间。
class="num">2023.09.class="num">01 class="num">13:class="num">39:class="num">14.500 ct_19_r1(USDJPY.ln,D1) class="type">void OnStart() misses: class="num">45, strict matches: class="num">61, & generic matches: class="num">166, for strict pct(excl. generic): class="num">0.58, & generic pct: class="num">0.83, with inductions at: class="num">1 class="num">2023.09.class="num">01 class="num">13:class="num">39:class="num">55.073 ct_19_r1(USDJPY.ln,D1) class="type">void OnStart() misses: class="num">56, strict matches: class="num">63, & generic matches: class="num">153, for strict pct(excl. generic): class="num">0.53, & generic pct: class="num">0.79, with inductions at: class="num">2
「和传统均线的本质差别」
读描述会觉得四边形归纳法比移动均线复杂得多,但 MQL5 生态里 Alglib 之类的库随手就能调,核心逻辑 200 行内就能写完,数学爱好者用 MT5 的 IDE 基本等于进了游乐场。 传统均线在趋势反转时滞后明显,而这套归纳法靠形态自适应,对拐点捕捉的潜在精度更高,不过外汇和贵金属是高杠杆高风险品种,精度优势不代表胜率保证,实盘仍可能连续止损。 想验证就开 MT5 新建脚本,把历史 EURUSD 的 H1 数据跑一遍四边形归纳,和一根 20 周期 SMA 叠图对比,肉眼能看出响应速度差一截。