在MQL5中构建自优化智能交易系统(EA)(第五部分):自适应交易规则(基础篇)
📘

在MQL5中构建自优化智能交易系统(EA)(第五部分):自适应交易规则(基础篇)

第 1/3 篇

「RSI固定阈值在趋势市里会集体失效」

技术指标给算法交易提供了明确信号,但标准化规则常和行情对着干。以RSI为例,教科书说低于30买、高于70卖是最优,可黄金日线用20周期RSI时,2019年9月到12月这3个月强跌趋势里,读数始终没触碰70做空线,预期信号根本没出现。 有人觉得调周期就能解决,其实未必。做跨市场扫描的人往往锁定了能揭示特定模式的固定周期,不能动;单品种交易者若加大RSI周期,噪音平滑后反而更难触发,减小周期则引入更多杂讯、信号质量下降。 随意改参数可能让EA出现意外行为:该抓的机会没抓到,或者退出条件没完全满足,使得外汇/贵金属这类高风险品种的市场敞口高过预期。图1里那条趋势线直观显示了信号真空区。

用平均走势幅度筛掉弱信号

要让程序根据历史自动调整规则,核心思路是先搞清楚某个品种平时的走势幅度长什么样。只有先建立「平均走势」的基准,才能谈得上识别强劲走势。 具体落地时,把入场门槛设成平均幅度的 2 倍或 3 倍,应用程序就只会吃那些明显强于常态的行情。以 EURUSD 日线为例,若过去 200 根 K 线实体均值约 40 点,那只做实体 ≥80 点的突破信号,噪音交易会少一大截。 这么做有两个实打实的好处:一是不会因阈值定太高导致连续几周不开仓;二是 RSI 这类指标的周期优化可以省掉大半,因为信号强弱改由价格本身的统计分布说了算。外汇与贵金属波动受事件驱动,用历史均值做基准仍属概率性过滤,不保证信号后一定出趋势。

◍ 用 MQL5 搭一个支撑阻力突破的底仓框架

先把基准跑出来,才有后面迭代的参照。这里用 MQL5 写一个简单的 EA:在 XAGUSD 日线图上判定支撑/阻力,再用 RSI 确认突破后进场,止损止盈幅度固定,这样后续收益变化只反映决策逻辑本身的差异。外汇与贵金属杠杆高、滑点跳空频繁,任何回测盈利都不代表实盘胜率。 支撑位看成引发反弹的低价区,阻力位是触发抛压的关注价区;突破任一边,价格往往出现一段单向移动。文中用「当前价 vs 5 天前对应水平」来识别这些价位,并在价格跌破支撑时寻机做多,仅当阻力反向失守才做空。 回测固定用 2017-01-01 至 2025-01-28 的 M15 历史数据,开启「随机延迟」并选「基于真实点差的每 tick」,贴近经纪商实际环境。首版无输入参数,加载后设好日期直接跑策略测试器即可。 首测结果有看点:总净利润 588.60 美元,夏普比率 0.85,作为后续版本要超越的底线。下面这段代码是系统常量与交易库导入部分,先把骨架立住。 代码逐行拆解:RSI_PERIOD 10 表示 RSI 取 10 周期;RSI_PRICE 用收盘价计算;ATR_SIZE 1.5 代表止损宽度为 1.5 倍 ATR;ATR_PERIOD 14 是 ATR 计算周期;TF_1 用日线做主框架,TF_2 用 M15 做触发;VOL 0.1 手固定仓位。#include 引入 Trade.mqh 并实例化 CTrade,后续开平仓都靠它。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                      Self Adapting Trading Rules.mq5 |
class=class="str">"cmt">//|                                                       Gamuchirai Ndawana |
class=class="str">"cmt">//|                                       [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Gamuchirai Ndawana"
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| System constants                                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define RSI_PERIOD class="num">10           class=class="str">"cmt">//The period for our RSI indicator
class="macro">#define RSI_PRICE  PRICE_CLOSE   class=class="str">"cmt">//The price level our RSI should be applied to
class="macro">#define ATR_SIZE   class="num">1.5           class=class="str">"cmt">//How wide should our Stop loss be?
class="macro">#define ATR_PERIOD class="num">14           class=class="str">"cmt">//The period of calculation for our ATR indicator
class="macro">#define TF_1       PERIOD_D1     class=class="str">"cmt">//The primary time frame for our trading application
class="macro">#define TF_2       PERIOD_M15    class=class="str">"cmt">//The secondary time frame for our trading application
class="macro">#define VOL        class="num">0.1           class=class="str">"cmt">//Our trading volume  
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Libraries we need                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include <Trade/Trade.mqh>
CTrade Trade;
class=class="str">"cmt">//+------------------------------------------------------------------+

「把指标句柄和支撑阻力塞进全局」

EA 骨架搭完之后,真正干活的是全局变量和那几个自定义方法。先把 rsi_handler、atr_handler 两个句柄以及 rsi[]、atr[] 数组、support、resistance 双价挂在全局,后面 tick 里随时能取,不用反复声明。 setup() 在 OnInit 里被调用,负责把指标句柄加载进来:atr 用当前品种、TF_1 周期、ATR_PERIOD 参数;rsi 用 TF_2 周期、RSI_PERIOD 和 RSI_PRICE。支撑取 TF_1 第 5 根 K 线最低价,阻力取同周期第 5 根最高价——这两个值后续作为区间边界,外汇与贵金属波动大,实际只会是动态参考而非硬边界。 OnDeinit 里调 release() 释放句柄,IndicatorRelease 不写的话 MT5 长时间跑多个实例可能漏内存。OnTick 只做一件事:调 update() 刷新变量并找形态,逻辑全塞在用户函数里,主流程保持干净。 想验证就开 MT5 建个 EA,把 TF_1 设成 H1、TF_2 设成 M15,ATR_PERIOD 给 14,编译后看第 5 根 K 的 support/resistance 是否随新柱重置,黄金品种上这种边界重算频率明显高于欧元。

MQL5 / C++
class=class="str">"cmt">//| Global variables                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int rsi_handler,atr_handler;
class="type">class="kw">double rsi[],atr[];
class="type">class="kw">double support,resistance;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//---
   setup();
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert deinitialization function                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(const class="type">int reason)
  {
class=class="str">"cmt">//---
   release();
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert tick function                                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
class=class="str">"cmt">//---
   update();
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| User defined methods                                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Initialize our system variables                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void setup(class="type">void)
  {
class=class="str">"cmt">//Load our technical indicators
   atr_handler = iATR(Symbol(),TF_1,ATR_PERIOD);
   rsi_handler = iRSI(Symbol(),TF_2,RSI_PERIOD,RSI_PRICE);
   resistance  = iHigh(Symbol(),TF_1,class="num">5);
   support     = iLow(Symbol(),TF_1,class="num">5);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Let go of resources we are no longer consuming                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void release(class="type">void)
  {
class=class="str">"cmt">//Free up resources we are not class="kw">using
   IndicatorRelease(atr_handler);
   IndicatorRelease(rsi_handler);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Update our system variables and look for trading setups            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void update(class="type">void)
  {
class=class="str">"cmt">//Update our system variables
class=class="str">"cmt">//Some duties must be performed periodically on the higher time frame
    {
      class="kw">static class="type">class="kw">datetime time_stamp;

常见问题

趋势市中价格容易长时间停留在超买超卖区,固定阈值会集体失效;可改用平均走势幅度过滤弱信号,只在幅度达标时交易。
先算一段周期内的平均价格波动幅度,只有当突破距离大于该幅度时才视为有效信号,弱信号直接跳过。
小布可基于你设定的平均幅度阈值自动标注突破有效性,并把弱信号过滤掉,你只需看它标出的候选单。
把指标句柄和支撑阻力存到全局,初始化一次就能在多个函数复用,避免重复计算拖慢运行。
贵金属波动大且跳空多,固定点数常扛不住;建议结合平均走势幅度动态设距,并认清高风险属性。