使用经典机器学习方法预测汇率:逻辑回归(logit)模型和概率回归(probit)模型·综合运用
持仓判定与平仓的底层函数
EA 里最容易被忽略的是持仓状态机。下面这段把「是否有某类持仓」和「平掉某类持仓」拆成两个独立函数,逻辑直接挂在 PosExists 与 PosClose 上,实盘里调用一次就能知道当前多空占用。
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()); }
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 根对比信号衰减。 作者自己在讨论里也承认没保证击败朴素预测,所以把它当过滤层而非开仓圣杯,才是能落地的用法。