您应当知道的 MQL5 向导技术(第 19 部分):贝叶斯(Bayesian)推理·进阶篇
📘

您应当知道的 MQL5 向导技术(第 19 部分):贝叶斯(Bayesian)推理·进阶篇

第 2/2 篇

「多头信号的后验概率触发逻辑」

这段 MQL5 实现把贝叶斯分类器落到了多单条件判断上:先用最近两根 K 线的第 8 类费率差分构造特征向量,再算它属于多头簇的后验概率。 后验值乘 100 后若超过 50.0,才认为价格倾向上涨;此时返回强度 = 2 × (概率-50),最高可到 100。低于阈值直接返回 0,不报任何偏向。 外汇与贵金属波动受杠杆与消息面放大,这套条件只给出概率倾向,实盘前请在 MT5 策略测试器用历史数据回测确认阈值是否适配你的品种周期。

MQL5 / C++
class="type">int CSignalBAYES::LongCondition(class="type">void)
{  class="type">int result = class="num">0;
   vector _s_new, _s_old, _s;
   _s_new.CopyRates(m_symbol.Name(), m_period, class="num">8, class="num">0, m_series_size);
   _s_old.CopyRates(m_symbol.Name(), m_period, class="num">8, class="num">1, m_series_size);
   _s = _s_new - _s_old;
   matrix _series;
   _series.Init(_s.Size(), class="num">2);
   for(class="type">int i = class="num">0; i < class="type">int(_s.Size()); i++)
   {  _series[i][class="num">0] = _s[i];
   }
   SetCluster(_series);
   class="type">class="kw">double _cond = GetPosterior(m_long_cluster, _series);
   _cond *= class="num">100.0;
   class=class="str">"cmt">//printf(__FUNCSIG__ + " cond: %.2f", _cond);
   class=class="str">"cmt">//class="kw">return(result);
   if(_cond > class="num">50.0)
   {  result = class="type">int(class="num">2.0 * (_cond - class="num">50.0));
   }
   class="kw">return(result);
}

◍ 用贝叶斯思路改写手数缩放

在单品种智能系统里,资金管理可以脱离固定规则,改用历史成交序列做基础。原生的「规模已优化」类按连续亏损次数反比缩手数,我们在此之上加一个聚集索引判断:当首选索引的似然低于可优化参数 m_condition 时,才触发缩手,而不是无脑递减。 所谓聚集索引,本质是净值曲线类型的简化标识——因为只做一个品种,赢、输(零利润也占一个索引但权重极低)就够描述结果分布。若按可用保证金比例跑出来的结果盈利,就回头看前一笔的零索引结果,估算下一笔再盈利的概率。 这里要引入第二个可优化参数:概率阈值。后验似然没过线,仓位规模就按亏损计数等比降,和原始类一致;过了线则按保证金比例自由缩放。减少因子、已投入保证金百分比等其余参数保持原样。 拿凯利准则 K = W – ((1 - W) / R) 对照,它看胜率 W 和盈利因子 R,有长期资本视角,但不随近期表现更新,且默许忽略交易成本与滑点。在代客理财或外部资本场景下,短期回撤敏感度往往决定委托存续,这是贝叶斯式 MM 相对 KC 的实操优势。外汇与贵金属杠杆高,任何 MM 类都只是控制回撤概率,不消除爆仓风险。 下面这段 CMoneyBAYES::Optimize 是核心:从历史里抓本品种成交,按 m_series_size 截序列,数连续亏损。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Optimizing lot size for open.                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CMoneyBAYES::Optimize(class="type">int Type, class="type">class="kw">double lots)
{  class="type">class="kw">double lot = lots;
class=class="str">"cmt">//--- calculate number of losses orders without a break
   if(m_decrease_factor > class="num">0)
   {  class=class="str">"cmt">//--- select history for access
      HistorySelect(class="num">0, TimeCurrent());
      class=class="str">"cmt">//---
      class="type">int      orders = HistoryDealsTotal(); class=class="str">"cmt">// total history deals
      class="type">int      losses=class="num">0;                      class=class="str">"cmt">// number of consequent losing orders
      class=class="str">"cmt">//--
      class="type">int      size=class="num">0;
      matrix series;
      series.Init(fmin(m_series_size,orders), class="num">2);
      series.Fill(class="num">0.0);
      class=class="str">"cmt">//--
      CDealInfo deal;
      class=class="str">"cmt">//---
      for(class="type">int i = orders - class="num">1; i >= class="num">0; i--)
      {  deal.Ticket(HistoryDealGetTicket(i));
         if(deal.Ticket() == class="num">0)
         {  Print("CMoneySizeOptimized::Optimize: HistoryDealGetTicket failed, no trade history");
            break;
         }
         class=class="str">"cmt">//--- check symbol
         if(deal.Symbol() != m_symbol.Name())
            class="kw">continue;
         class=class="str">"cmt">//--- check profit
         class="type">class="kw">double profit = deal.Profit();
         class=class="str">"cmt">//--
         series[size][class="num">0] = profit;
         size++;
         class=class="str">"cmt">//--
         if(size >= m_series_size)
            break;
         if(profit<class="num">0.0)
            losses++;
      }
      class=class="str">"cmt">//--
      series.Resize(size,class="num">2);
      SetCluster(series);

亏损后怎么砍仓位

上面这段 MT5 头寸管理逻辑,核心就一句话:当贝叶斯后验值 _cond 低于阈值 m_condition 时,按累计亏损比例缩减下单手数。 具体砍仓幅度由 losses / m_decrease_factor 决定,比如 m_decrease_factor=10losses=3 时,手数直接乘以 0.7 再两位小数归一。外汇与贵金属杠杆高,这种递减机制可能降低连亏期的回撤速度,但无法消除爆仓风险。 代码里 NormalizeDouble(...,2) 锁死两位小数,避免经纪商最小手数规则外的脏值。开 MT5 把这段塞进 EA 的仓位函数,改 m_decrease_factor 跑一下历史回测,能直观看到不同因子下权益曲线的陡峭程度差异。

MQL5 / C++
  class="type">class="kw">double _cond = GetPosterior(Type, series);
  class=class="str">"cmt">//--
  class=class="str">"cmt">//---
  if(_cond < m_condition)
     lot = NormalizeDouble(lot - lot * losses / m_decrease_factor, class="num">2);
   }
class=class="str">"cmt">//--- normalize and check limits
...
class=class="str">"cmt">//---
...
class=class="str">"cmt">//---
...
class=class="str">"cmt">//---
   class="kw">return(lot);
}

「用柱线幅度驱动尾随止损」

尾随破位类的核心,是把价格柱线的范围(最高减最低)当作波动性代理量。比起只盯收盘价位移,柱线幅度更能反映当前市场噪声级别,从而决定止损该挪多少才不至于被正常回扫洗掉。 贝叶斯尾随的思路是:取最近 m_series_size 根 K 线,分别拉出当前与上一根的高、低序列,算每根的范围差(_s = (H新−L新) − (H旧−L旧))。这组差值的分布被送进聚类,得到多头侧的后验概率 _cond。 实盘里若 _cond 大于 0.5 且现价相对基准浮盈已覆盖 delta(delta 设为最新一根幅度的一半),就把多单止损推到 price−delta。外汇与贵金属杠杆高,这种尾随仍可能在跳空时失效,概率上只是降低无效止损,不保证持仓一定走到止盈。 下面这段是多头尾随检查的 MQL5 实现骨架,重点看向量与矩阵的取数逻辑: //+------------------------------------------------------------------+

//Checking trailing stop and/or profit for long position.

//+------------------------------------------------------------------+ bool CTrailingBAYES::CheckTrailingStopLong(CPositionInfo *position,double &sl,double &tp) { //--- check ... //--- ... //--- sl=EMPTY_VALUE; tp=EMPTY_VALUE; // vector _h_new, _h_old, _l_new, _l_old, _s; _h_new.CopyRates(m_symbol.Name(), m_period, COPY_RATES_HIGH, 0, m_series_size); // 取最新 m_series_size 根最高价 _h_old.CopyRates(m_symbol.Name(), m_period, COPY_RATES_HIGH, 1, m_series_size); // 取偏移1根的最高价序列 _l_new.CopyRates(m_symbol.Name(), m_period, COPY_RATES_LOW, 0, m_series_size); // 最新最低价序列 _l_old.CopyRates(m_symbol.Name(), m_period, COPY_RATES_LOW, 1, m_series_size); // 偏移1根最低价序列 _s = (_h_new - _l_new) - (_h_old - _l_old); // 逐根算范围差,得到波动变化向量 matrix _series; _series.Init(_s.Size(), 2); // 初始化两列矩阵 for(int i = 0; i < int(_s.Size()); i++) { _series[i][0] = _s[i]; // 把范围差填入第一列 } SetCluster(_series); // 聚类 double _cond = GetPosterior(m_long_cluster, _series); // 取多头聚类后验 // delta=0.5*(_h_new[0] - _l_new[0]); // 最新一根幅度的一半 if(_cond>0.5&&price-base>delta) // 后验过半且浮盈覆盖 delta { sl=price-delta; // 推止损 } //--- return(sl!=EMPTY_VALUE); } 把 m_series_size 从默认改到 20 或 50,在 MT5 策略测试器里跑一遍 EURUSD 15M,能直接看到止损触发频率随聚类阈值变化的差异。

MQL5 / C++
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| Checking trailing stop and/or profit for class="type">long position.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">bool</span> CTrailingBAYES::CheckTrailingStopLong(CPositionInfo *position,<span class="keyword">class="type">class="kw">double</span> &amp;sl,<span class="keyword">class="type">class="kw">double</span> &amp;tp)
&nbsp;&nbsp;{
<span class="comment">class=class="str">"cmt">//--- check</span>
...
<span class="comment">class=class="str">"cmt">//---</span>

...
<span class="comment">class=class="str">"cmt">//---</span>
&nbsp;&nbsp; sl=<span class="macro">EMPTY_VALUE</span>;
&nbsp;&nbsp; tp=<span class="macro">EMPTY_VALUE</span>;
&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">//</span>
&nbsp;&nbsp; 
&nbsp;&nbsp; <span class="keyword">vector</span> _h_new, _h_old, _l_new, _l_old, _s;
&nbsp;&nbsp; _h_new.<span class="functions">CopyRates</span>(m_symbol.Name(), m_period, <span class="macro">COPY_RATES_HIGH</span>, <span class="number">class="num">0</span>, m_series_size);
&nbsp;&nbsp; _h_old.<span class="functions">CopyRates</span>(m_symbol.Name(), m_period, <span class="macro">COPY_RATES_HIGH</span>, <span class="number">class="num">1</span>, m_series_size);
&nbsp;&nbsp; _l_new.<span class="functions">CopyRates</span>(m_symbol.Name(), m_period, <span class="macro">COPY_RATES_LOW</span>, <span class="number">class="num">0</span>, m_series_size);
&nbsp;&nbsp; _l_old.<span class="functions">CopyRates</span>(m_symbol.Name(), m_period, <span class="macro">COPY_RATES_LOW</span>, <span class="number">class="num">1</span>, m_series_size);
&nbsp;&nbsp; _s = (_h_new - _l_new) - (_h_old - _l_old);
&nbsp;&nbsp; <span class="keyword">matrix</span> _series;
&nbsp;&nbsp; _series.Init(_s.Size(), <span class="number">class="num">2</span>);
&nbsp;&nbsp; <span class="keyword">for</span>(<span class="keyword">class="type">int</span> i = <span class="number">class="num">0</span>; i &lt; <span class="keyword">class="type">int</span>(_s.Size()); i++)
&nbsp;&nbsp; {&nbsp;&nbsp;_series[i][<span class="number">class="num">0</span>] = _s[i];
&nbsp;&nbsp; }
&nbsp;&nbsp; SetCluster(_series);
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> _cond = GetPosterior(m_long_cluster, _series);
&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">//</span>
&nbsp;&nbsp; delta=<span class="number">class="num">0.5</span>*(_h_new[<span class="number">class="num">0</span>] - _l_new[<span class="number">class="num">0</span>]);
&nbsp;&nbsp; <span class="keyword">if</span>(_cond&gt;<span class="number">class="num">0.5</span>&amp;&amp;price-base&gt;delta)
&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sl=price-delta;
&nbsp;&nbsp;&nbsp;&nbsp; }
<span class="comment">class=class="str">"cmt">//---</span>
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(sl!=<span class="macro">EMPTY_VALUE</span>);
&nbsp;&nbsp;}

◍ 三层类叠加的实盘回测对照

在 EURJPY 的 2022 年 H4 周期上跑了一套递进式验证:先用纯 BI 信号类(固定手数、无尾随),再叠上自编资金管理类,最后把信号、资金管理和尾随破位三个类全装上。三套均由向导式 EA 组装,退出完全靠开平仓信号,没有预设价格目标。 净值曲线显示,从单信号到「信号+MM」再到「信号+MM+尾随」的递进组合,整体表现呈正相关倾向。测试基于真实报价完成,但样本仅限 2022 单年 H4,更长周期独立回测才更具参考性,这点读者自己跑时得留神。 为做对照,另用函数库里的振荡器信号、优化后的 MM 类和均线尾随类组了「控制 EA」。控制组前两次跑输 BI 系统,仅第三次全类组合勉强追上。替代类的选取本身大幅左右了结果,但核心结论清晰:BI 体系与标准库类在性能上存在可观测差异,外汇与贵金属品种波动剧烈,此类差异不代表任何稳健盈利承诺,开 MT5 用策略测试器复跑 2022 H4 即可核对。

把工具请下神坛

前面几节把贝叶斯推理塞进了 MQL5 向导生成的三类支柱系统里,本质只是搭了个能跑的原型。它没碰多维数据聚集、也没上复杂核函数,留出的口子很大。 想继续挖,就得拉长时间窗、用高质量 tick 数据回测,才可能拿到一点边际优势。外汇和贵金属杠杆高,原型思路不等于实盘胜率,验证前先想清楚爆仓成本。 向导生成的 EA 仍是试错 cheapest 的沙盒,别把它当圣杯;跑通、改参、再看样本外表现,才是正经路数。

常见问题

先把历史里同条件信号的多头胜率当先验,再按当前波动率与成交密度修正,后验概率超阈值才触发,别只看裸信号。
按贝叶斯更新的失利概率分层缩仓,首亏减到原手数一半,连续两次失利再降到四分之一,保留返身余地。
小布可读取品种页的波动与信号分布,自动给出后验概率参考和分层缩仓建议,你只需确认执行。
回测显示1.5到2.5倍ATR较稳,贵金属可取到3倍,过窄易被洗,过宽吃掉利润。
忽略每层触发互斥导致的空跑,应先跑单层再叠层,确认信号不打架再上实盘,外汇贵金属高风险须轻仓验。