"cmt">//+-------------""> "cmt">//+-------------"">
使用经典机器学习方法预测汇率:逻辑回归(logit)模型和概率回归(probit)模型·综合运用
📘

使用经典机器学习方法预测汇率:逻辑回归(logit)模型和概率回归(probit)模型·综合运用

第 3/3 篇

持仓判定与平仓的底层函数

EA 里最容易被忽略的是持仓状态机。下面这段把「是否有某类持仓」和「平掉某类持仓」拆成两个独立函数,逻辑直接挂在 PosExists 与 PosClose 上,实盘里调用一次就能知道当前多空占用。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|Function determines whether there is an open buy or sell position |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool PosExists(class="type">ENUM_POSITION_TYPE type)
  {
   for(class="type">int i=PositionsTotal()-class="num">1; i>=class="num">0; i--)
    if(m_position.SelectByIndex(i))
      if(m_position.Symbol()==Symbol() && m_position.Magic() == MAGIC_NUMBER && m_position.PositionType()==type)
        class="kw">return true;
   class="kw">return class="kw">false;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|The function closes a class="type">long or class="type">class="kw">short trade                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void PosClose(class="type">ENUM_POSITION_TYPE type)
  {
   for(class="type">int i=PositionsTotal()-class="num">1; i>=class="num">0; i--)
    if(m_position.SelectByIndex(i))
      if(m_position.Symbol()==Symbol() && m_position.Magic() == MAGIC_NUMBER && m_position.PositionType()==type)
        if(!m_trade.PositionClose(m_position.Ticket()))
          printf("Failed to close position %d Err=%s",m_position.Ticket(),m_trade.ResultRetcodeDescription());
  }
逐行看 PosExists:从第 3 行倒序遍历 PositionsTotal()-1 到 0,避免删除元素时漏检;第 4 行用 SelectByIndex 把第 i 个持仓塞进 m_position 对象;第 5 行三重过滤——交易品种必须等于当前 Symbol()、Magic 号等于 MAGIC_NUMBER、持仓方向等于传入的 type,全中才返回 true,否则循环完返回 false。 PosClose 结构几乎一致,差别在第 7~8 行:命中后调 m_trade.PositionClose 按 Ticket 平仓,若返回 false 就 printf 打出票据号和 ResultRetcodeDescription 的错误描述。外汇与贵金属杠杆高,这类错误日志能在滑点拒绝时帮你快速定位,而不是盲目重试。 把 MAGIC_NUMBER 设成你 EA 专属值,开 MT5 新建脚本粘进去,用 PosExists(POSITION_TYPE_BUY) 就能在日志里验证当前图表是否已有同魔术码多单。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|Function determines whether there is an open buy or sell position |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool PosExists(class="type">ENUM_POSITION_TYPE type)
  {
   for(class="type">int i=PositionsTotal()-class="num">1; i>=class="num">0; i--)
    if(m_position.SelectByIndex(i))
      if(m_position.Symbol()==Symbol() && m_position.Magic() == MAGIC_NUMBER && m_position.PositionType()==type)
        class="kw">return true;
   class="kw">return class="kw">false;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|The function closes a class="type">long or class="type">class="kw">short trade                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void PosClose(class="type">ENUM_POSITION_TYPE type)
  {
   for(class="type">int i=PositionsTotal()-class="num">1; i>=class="num">0; i--)
    if(m_position.SelectByIndex(i))
      if(m_position.Symbol()==Symbol() && m_position.Magic() == MAGIC_NUMBER && m_position.PositionType()==type)
        if(!m_trade.PositionClose(m_position.Ticket()))
          printf("Failed to close position %d Err=%s",m_position.Ticket(),m_trade.ResultRetcodeDescription());
  }

◍ 重优化之外还得看参数够不够显著

这套 EA 跟普通做法拉开差距的地方有两点。一是它允许每过 reoptimize_step 根 K 线就把分类器参数重新优化一遍,而不是装好就不动。二是它不光估参数,还顺手算这些参数的标准差——大多数实现里这一步直接被跳过了。 只在样本上摸到一组“最优”参数没意义,关键得确认参数和模型本身是否显著。参数不显著时,宁可不交易,也比硬吃噪声信号强,外汇和贵金属这种高波动品种尤其如此。 所以它内部跑了一道显著性检验。零假设 H₀ 是所有参数都为 0(w₁=w₂=…=wₖ=0),备择 H₁ 是至少有一个不为 0,即模型真有点预测力。检验用似然比 LR = 2(LLF – LLF₀),其中 LLF 是拟合模型的对数似然,LLF₀ 是零假设下平凡模型的对数似然,差值越大越倾向拒绝“模型无效”。

「用似然比卡阈值才让EA动手」

在二元分类的EA逻辑里,常规模型只拿样本里“成功”占比 p₀ 当基线,其对数似然 LLF0 = N(p0*Ln(p0) + (1-p0)*Ln(1–p0))。完整模型多塞了特征进去,两模型差出一个 LR 统计量。 LR 在零假设下服从自由度等于特征数量的 χ² 分布。只有算出的 LR 大于临界值 X²crit(α; v=lag_) 落进拒绝域,才推翻“信号无效”的 H₀,EA 这时候才允许开仓,不是看到信号就冲。 拿 GBPUSD 日线回测的场景举例,除了分类器自身参数,还得在外头调四组超参数:历史深度、特征数量、显著性水平、再优化步长,这些都靠 MT5 策略测试器人工挑。 想再榨一点表现,可以把历史深度也写成随市场状态动的变量,类似 logit / probit 参数动态化的做法;这摊事不在这节展开。外汇和贵金属杠杆高,这类统计开仓信号也只是概率倾向有效,实盘前务必自己跑一遍测试器验证。

把这条线请下神坛

LogitExpert 这套 EA 的硬核价值不在「预测汇率」,而在它把分类器参数放在 MT5 里随最新 ticks 实时重训,并用似然比统计量把统计不可靠的信号直接掐掉。协方差矩阵算标准误那段,决定了你看到的权重到底靠不靠谱,而不是看着拟合优度自我感动。 外汇与贵金属属高风险品类,这类二元 logit/probit 模型只给出倾向性概率,实盘前务必用 LogitExpert.mq5 在策略测试器里跑 2023 至今的 EURUSD 十五分钟数据,把重训窗口从默认 500 根调到 300 根对比信号衰减。 作者自己在讨论里也承认没保证击败朴素预测,所以把它当过滤层而非开仓圣杯,才是能落地的用法。

常见问题

看模型输出的上涨概率是否跌破你设的平仓阈值,比如从0.6掉到0.45就手动或让程序平仓,别硬扛。
参数显著性差,模型可能只是拟合了噪声;先查p值或似然比,不显著就别急着上实盘。
小布盯盘的AIGC会直接标出哪些参数显著、似然比过没过阈值,打开品种页就能看到诊断,不用自己算。
常见卡5%显著性对应的临界值,似然比统计量超了才允许EA下单,能过滤掉大部分假信号。
不是,两者都只是给概率倾向,外汇贵金属高风险,信号错是常态,别把这条线请下神坛当必涨依据。