将概率论应用于缺口交易·综合运用
(3/3)·从思路验证到风险测算,把概率框架真正跑通在单一资产 EA 上
◍ 缺口对象的破位判定与容器维护
这段 MQL5 实现把「价格缺口」封装成 SGap 类,再用 CGaps 管理一组活跃缺口。核心破位函数 brkn() 用三根参考价 p0、p1、p2 判定:当 p0 在 p1 上方且 p2 回落至 p1 或以下,或 p0 在 p1 下方且 p2 升破 p1 或以上,返回 1 表示缺口被回填;若进度函数 Q() 达到阈值 q0 则返回 -1,否则 0 表示仍在挂单区。 Q() 的计算式为 (p2-p0)/(p2-p1),当 p2 等于 p1 时直接返回 0.0,避免除零。a() 则用 gap() 绝对值和滑点 s 推导挂单比例,k0 固定为 q0/(1-q0),这套映射决定了每跳价格对应的仓位权重。 CGaps::add() 在新增缺口时按 p+(p-p_1)*q0/(1-q0) 推演出参考挂单价 ps,并调用 pp2v() 换算成手数;容器用 ArrayResize 以 ND 为步长动态扩容。change() 遍历活跃缺口,根据多空方向用 bid 或 ask 更新 p2,一旦 brkn() 非 0 就把索引置 -1 标记失效。 实盘里外汇与贵金属跳空后回填概率随品种波动率变化,建议把这段代码挂到 MT5 策略测试器,用不同 q0(如 0.5 / 0.618)跑 EURUSD 与 XAUUSD 的 tick 数据,观察 ng0 失效节奏再调参。
else { request.volume=-vi*dv; request.type=ORDER_TYPE_SELL; } if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); } class="type">int SGap :: brkn() { if(((p0>p1)&&(p2<=p1))||((p0<p1)&&(p2>=p1))) class="kw">return class="num">1; if(Q()>=q0) class="kw">return -class="num">1; class="kw">return class="num">0; } class="type">bool SGap :: up() { class="kw">return p0>p1; } class="type">void SGap :: change(class="type">class="kw">double p) { if(brkn()==class="num">0) p2=p; } class="type">class="kw">double SGap :: gap() { class="kw">return MathAbs(p0-p1); } class="type">class="kw">double SGap :: Q() { if(p2==p1) class="kw">return class="num">0.0; class="kw">return (p2-p0)/(p2-p1); } class="type">class="kw">double SGap :: a() { class="type">class="kw">double g=gap(), k0=q0/(class="num">1-q0); class="kw">return (g-s)/(k0*g+s); } class="type">void CGaps :: init() { ngs=ngo=class="num">0; } class="type">void CGaps :: add(class="type">class="kw">double p_1,class="type">class="kw">double p) { ++ngs; if(ArraySize(gs)<ngs) ArrayResize(gs,ngs,ND); gs[ngs-class="num">1].p0=gs[ngs-class="num">1].p2=p; gs[ngs-class="num">1].p1=p_1; class="type">class="kw">double ps=p+(p-p_1)*q0/(class="num">1-q0); gs[ngs-class="num">1].v=pp2v(ps,p); class="type">int i=class="num">0; for(; i<ngo; ++i) if(go[i]<class="num">0) class="kw">break; if(i==ngo) { ++ngo; if(ArraySize(go)<ngo) ArrayResize(go,ngo,ND); } go[i]=ngs-class="num">1; } class="type">bool CGaps :: change(class="type">class="kw">double pbid,class="type">class="kw">double pask) { class="type">bool ch=class="kw">false; for(class="type">int i=class="num">0; i<ngo; ++i) { if(go[i]<class="num">0) class="kw">continue; if(gs[go[i]].up()) gs[go[i]].change(pbid); else gs[go[i]].change(pask); if(gs[go[i]].brkn()!=class="num">0) {go[i]=-class="num">1; ch=true;} } class="kw">return ch; } class="type">class="kw">double CGaps :: v(class="type">void) { class="type">class="kw">double v=class="num">0; for(class="type">int i=class="num">0; i<ngo; ++i) if(go[i]>=class="num">0) v+=gs[go[i]].v; class="kw">return v; } class="type">void CGaps :: gs2f(class="type">class="kw">string fn) { class="type">int f=FileOpen(fn, FILE_WRITE|FILE_COMMON|FILE_ANSI|FILE_TXT); class="type">int na=class="num">0, np=class="num">0, bk; class="type">class="kw">double kt=class="num">0.0, pk=class="num">0.0; for(class="type">int i=class="num">0;i<ngs;++i) { bk=gs[i].brkn(); if(bk==class="num">0) class="kw">continue;
「把回测统计量写进文件再喂给优化器」
上面这段逻辑干了两件事:先按样本数 na 求平均触发价 kt,再算盈利交易占比 pk,最后用 FileWriteString 把 na、kt、pk 三个量落盘。注意 kt 只有在 na>0 时才做除法,避免空样本除零;pk 是 np/na 的 double 强转,直接反映胜率倾向。 落盘之后,代码用 input 把上一次统计结果硬编码成新轮优化的起点:na=26 表示取 26 笔交易做样本,kt=1.1627 是止盈/止损比,pk=0.5769 即约 57.7% 的盈利概率。外汇与贵金属波动剧烈,这组数只是某段历史样本,换周期可能漂移。 G0、D0、dlt 三个常量是下一轮筛选门槛:G0=0 表示不卡最低平均盈利,D0=0.9 要求增量至少 0.9,dlt=0.17 是显著性阈值。你开 MT5 把这段贴进 EA 的收尾模块,改 na 或 kt 初始值,就能看输出文件里的 pk 是否随样本放大而收敛。
++na; if(bk>class="num">0) ++np; kt+=gs[i].a(); } if(na>class="num">0) { kt/=na; pk=((class="type">class="kw">double)np)/na; } FileWriteString(f,"na = "+(class="type">class="kw">string)na+"\n"); FileWriteString(f,"kt = "+(class="type">class="kw">string)kt+"\n"); FileWriteString(f,"pk = "+(class="type">class="kw">string)pk); FileClose(f); } input class="type">uint na=class="num">26; class=class="str">"cmt">// 该系列中的交易数量 input class="type">class="kw">double kt=class="num">1.162698185452029 class=class="str">"cmt">// 止盈/止损比率 input class="type">class="kw">double pk=class="num">0.5769230769230769class=class="str">"cmt">// 盈利能力 class="type">class="kw">double G0=class="num">0.0; class=class="str">"cmt">// 最低平均盈利能力 class="type">class="kw">double D0=class="num">0.9; class=class="str">"cmt">// 最小增量 class="type">class="kw">double dlt=class="num">0.17; class=class="str">"cmt">// 显著级别
概率思路能补你的现有策略
概率方法很适合用来开发和配置 EA,但它不排他。 实际中它往往能补上你已有的技术路线,或逼你重新想一遍进出场逻辑。 本文没碰常规 EA 参数优化,只顺带提了一句。 这块和概率方法(统计方案理论)其实绑得很紧,后续有机会再拆。 外汇和贵金属波动大、杠杆高,任何统计结论都只是概率倾向,上 MT5 跑一遍自己的品种再定参数。
◍ 别急着下结论
随文附带三个 EA 源码:gaps_reg_stat.mq5(2.47 KB)抓连续逐笔报价间的缺口分布,gaps_ses_stat.mq5(3.16 KB)按交易时段收口统计缺口,gaps_ses_test.mq5(4.93 KB)直接拿时段缺口跑回测。把它们丢进 MT5 的 MQL5/Experts 目录编译,先跑 stat 两个收集样本,再换 test 验信号,比对着看才有意义。 外汇和贵金属缺口本身低频,俄文原帖评论里也提到周间隙之外还能叠新闻间隙,但罕见不代表能裸做。用这几只 EA 把你自己品种的缺口频率跑出来之前,别预设它「够用」或「没用」——样本量不到几千次跳动,统计结论都只是倾向。 下载包里文件版权归原作者,部分复制转载被禁;MT5 里实跑验证时,注意实盘外汇/贵金属杠杆风险,回测漂亮不等于活盘能复现。