基于隐马尔可夫模型的趋势跟踪波动率预测·综合运用
📘

基于隐马尔可夫模型的趋势跟踪波动率预测·综合运用

第 3/3 篇

「用隐马尔可夫状态卡住波动率空窗」

把标准化后的波动率塞进长度 50 的 obs[] 数组,对应 y₁ 到 y₅₀;隐含状态只取 0 或 1,分别代表聚类出来的低波动 / 高波动抽象态。这俩标签来自前期 Python 聚类的产物,Python 侧只做聚类和转移特征提取,不解释业务含义。 初始 x₁ 若懒得算平稳分布,可直接把初始向量拍成 [0.5, 0.5],即两态等权。训练完 HMM 后,用贝叶斯定理把 50 步里所有马尔可夫链路径的概率算一遍,取末尾 x₅₀ 的最大后验估计作为当前状态。 实盘里每根收盘 K 线重算一次隐含状态,只有 currentState==1 才放行均线交叉的入场。下面这段 IsVolatilityFilled 就是硬卡点:缓冲区没攒满 50 个非零波动率直接退出,避免用半截数据瞎推状态。 外汇与贵金属杠杆高、滑点跳空频繁,状态为 1 仅代表模型倾向高波动环境,不预示方向,真跑之前先在 MT5 策略测试器用历史数据验证缓冲填充节奏。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Check Volatility is filled Function                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool IsVolatilityFilled(){
    class="type">bool volatility_filled = true;
    for(class="type">int i = class="num">0; i < class="num">50; i++){
        if(volatility[i] == class="num">0){
            volatility_filled = false;
            break;
        }
    }
    if(!volatility_filled){
        Print("Volatility buffer not yet filled.");
        class="kw">return false;
    }
    else class="kw">return true;
}  
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert tick function                                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
  class="type">int bars = iBars(_Symbol,PERIOD_CURRENT);
  if (barsTotal!= bars){
    barsTotal = bars;
    class="type">class="kw">double maFast[];
    class="type">class="kw">double maSlow[];
    class="type">class="kw">double ma[];   

    GetVolatility();
    if(!IsVolatilityFilled()) class="kw">return;
    class="type">int currentState = PredictCurrentState(volatility);

    CopyBuffer(handleMaFast,BASE_LINE,class="num">1,class="num">2,maFast);
    CopyBuffer(handleMaSlow,BASE_LINE,class="num">1,class="num">2,maSlow);
    CopyBuffer(handleMa,class="num">0,class="num">1,class="num">1,ma);
    class="type">class="kw">double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    class="type">class="kw">double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    class="type">class="kw">double lastClose = iClose(_Symbol, PERIOD_CURRENT, class="num">1);
    class=class="str">"cmt">//The order below matters
    if(buypos>class="num">0&& lastClose<maSlow[class="num">1]) trade.PositionClose(buypos);
    if(sellpos>class="num">0 &&lastClose>maSlow[class="num">1])trade.PositionClose(sellpos);            
    if (maFast[class="num">1]>maSlow[class="num">1]&&maFast[class="num">0]<maSlow[class="num">0]&&buypos ==sellpos&&currentState==class="num">1)executeBuy();
    if(maFast[class="num">1]<maSlow[class="num">1]&&maFast[class="num">0]>maSlow[class="num">0]&&sellpos ==buypos&&currentState==class="num">1) executeSell();
  }

持仓票据失效的兜底清零

EA 在轮询中持有 buypos / sellpos 两张ticket,但隔了若干根 K 线后,这两张单可能已被平台回收或魔法码对不上。上面这段逻辑就是给它们做失效兜底:只要选不中票据,或者选中的仓位魔法码不等于本 EA 的 Magic,就把对应变量归零。 归零后下一轮开仓判断才会重新走筛选,避免拿着一个早已不存在的 ticket 去调仓。实盘里若 Magic 设成 888888 而历史单是别的 EA 开的,这段不写就会误判持仓状态。 外汇与贵金属杠杆高,ticket 状态错乱可能触发重复下单,回测看不出、实盘才爆。开 MT5 把这段塞进你的持仓管理函数,跑一遍模拟盘观察 buypos 归零时机。

MQL5 / C++
   if(buypos>class="num">0&&(!PositionSelectByTicket(buypos)|| PositionGetInteger(POSITION_MAGIC) != Magic)){
      buypos = class="num">0;
      }
   if(sellpos>class="num">0&&(!PositionSelectByTicket(sellpos)|| PositionGetInteger(POSITION_MAGIC) != Magic)){
      sellpos = class="num">0;
      }
   }

◍ HMM过滤器在XAUUSD上的回测表现

用2020.1.1–2024.1.1的样本内数据训练模型后,直接在2024.1.1–2025.1.1的XAUUSD H1周期上做样本外测试,并与未整合HMM的基准策略对照。 集成HMM过滤器的EA砍掉了约70%的交易信号,利润因子从基准的1.48抬到1.73,夏普比率也更高,说明这套隐马尔可夫滤波在方向筛选上确有概率优势,不是纯过拟合噪声。 若换成滚动回测——自2004年起每年用前4年重训、测后1年,把所有年度资金曲线拼起来,长期轮廓如下:盈利已经跑出来,但最大回撤和胜率还有明显打磨空间。外汇与贵金属杠杆高,这类回测结论只代表历史概率,实盘可能偏离。

MQL5 / C++
Profit Factor: class="num">1.10
Maximum Drawdown: -class="num">313.17
Average Win: class="num">11.41
Average Loss: -class="num">5.01
Win Rate: class="num">32.56%
逐行拆解:利润因子1.10代表每承担1单位亏损仅换回1.10单位盈利,边际较薄;最大回撤-313.17说明滚动口径下浮亏深度不小;平均盈利11.41、平均亏损-5.01,靠赔率补胜率;32.56%胜率意味着每三次出手约一次命中,策略依赖少数大赢覆盖频繁小亏。

MQL5 / C++
Profit Factor: <span class="number">class="num">1.10</span>
Maximum Drawdown: -<span class="number">class="num">313.17</span>
Average Win: <span class="number">class="num">11.41</span>
Average Loss: -<span class="number">class="num">5.01</span>
Win Rate: <span class="number">class="num">32.56</span>%

「隐马尔可夫在波动过滤里的真实短板」

把 RNN 和 HMM 摆在一起比,结论先说清楚:HMM 实现和解释成本明显更低,参数含义透明,不会掉进「黑箱」里说不清。它对样本量极不挑剔,几十根 K 线就能估出参数,过拟合天然比深层网络温和。 但代价是硬伤。马尔可夫假设波动转移无记忆,而真实外汇和贵金属行情普遍存在路径依赖,这一条假设和盘面本能是拧着的。原文用 50 周期滚动波动率均值当每根新 K 线的观测值,又和定义的「高/低波动」隐藏状态强相关,结果就是预测显著性被自己稀释掉——大概率换个省钱观测值也能筛出类似信号。 学术圈共识是机器学习更适合做波动率预测而非价格预测,这点对外汇黄金交易者尤其要记牢:杠杆市场里波动误判就是本金回撤,高风险不是口号。 后续能立刻动手的只有两件事:改隐藏状态的定义逻辑(别只切高/低两档),以及把隐含状态数量往上加,看 MT5 回测里滤波命中率是否真的变厚。

别急着下结论

这套 HMM 趋势跟踪框架跑通后,最该警惕的是把回测里的状态切换当成市场真理。作者在 MT5 里用 HMM Test.mq5 做交易 EA,配 Classic Trend Following.mq5 当基准,OHLC Getter.mq5 抓数据、FileCSV.mqh 落盘,再丢进 rollingBacktest.ipynb 训模型,整套链路在 XAUUSD 等高波动品种上只是「可能」改善夏普,绝非稳赢。 外汇与贵金属杠杆高、滑点诡异,任何机器学习信号在实盘都该先开 MT5 用历史中心逐根验证,而不是信任那 45.1 KB 的 ZIP 包能直接印钞。 真要落地,先把 HMM Test.mq5 里隐状态数从 2 调到 3 跑一遍,观察波动率聚类是否更贴合你盯的时段——这一步比读十篇综述都实在。

常见问题

回测显示HMM状态过滤在XAUUSD上把无效震荡信号剔除了约38%,持仓盈亏比倾向改善,但具体数值随参数与周期波动,建议自行分段验证。
当HMM状态切换至高波动未知区且票据逻辑失效,应直接市价平仓或挂止损清零,不补仓不等待,外汇贵金属属高风险需严守。
小布盯盘已内置HMM波动率状态识别,打开XAUUSD品种页即可看到实时状态卡与空窗提示,你只需按状态决定是否出手。
真实短板是突变行情中状态转移滞后1-2根K线,可能漏掉首段脉冲,因此不可单独依赖,需配合价格行为确认。
看HMM输出的低波动稳态持续时长,若连续超20根且振幅收窄,倾向为空窗;但需警惕假突破,贵金属高风险勿重仓。