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

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

(2/3)· 固定RSI周期碰上三月无信号?本篇用平均走势倍数改写突破确认逻辑

含代码示例偏理论 第 2/3 篇
把RSI卡在30/70就想等信号,黄金日线曾连续三个月不给面子。硬调周期要么吃噪声要么错过趋势,敞口和漏单一起找上门。

「高低周期联动的突破触发逻辑」

在 MT5 多周期策略里,大周期(TF_1)负责画支撑阻力,小周期(TF_2)负责盯盘下单。代码用 static datetime time_stamp 做时间戳比对,只有当前 K 线时间变了才刷新数据,避免每 tick 重复计算。 大周期侧每次刷新会重取 RSI 与 ATR 缓冲,并用 iLow(Symbol(),TF_1,5) / iHigh(Symbol(),TF_1,5) 把前 5 根 K 的高低点当支撑阻力,先删旧横线再建新线。注意原文 ObjectCreate 里支撑线名字拼成了 "Suppoprt",渲染层会显示两条线名不一致,复制时得手改。 小周期侧在无持仓时判断:若 TF_2 当前最低价大于 resistance,且 rsi[0]>70,则倾向以卖价开空,止损放在 ask + ATR_SIZE*atr[0]、止盈在 ask - ATR_SIZE*atr[0];若 TF_2 最高价小于 support 则走反向买逻辑。外汇与贵金属杠杆高,这类突破反转信号失败概率不低,实盘前务必在策略测试器用历史数据验一遍 ATR_SIZE 取值。

MQL5 / C++
class="type">class="kw">datetime current_time = iTime(Symbol(),TF_1,class="num">0);
class=class="str">"cmt">//Update the time
if(time_stamp != current_time)
  {
   time_stamp = current_time;
   class=class="str">"cmt">//Update indicator readings
   CopyBuffer(rsi_handler,class="num">0,class="num">0,class="num">1,rsi);
   CopyBuffer(atr_handler,class="num">0,class="num">0,class="num">1,atr);
   class=class="str">"cmt">//Update our support and resistance levels
   support    = iLow(Symbol(),TF_1,class="num">5);
   resistance = iHigh(Symbol(),TF_1,class="num">5);
   ObjectDelete(class="num">0,"Support");
   ObjectDelete(class="num">0,"Resistance");
   ObjectCreate(class="num">0,"Suppoprt",OBJ_HLINE,class="num">0,class="num">0,support);
   ObjectCreate(class="num">0,"Resistance",OBJ_HLINE,class="num">0,class="num">0,resistance);
   }
 }
class=class="str">"cmt">//While other duties need more attention, and must be handled on lower time frames.
  {
   class="kw">static class="type">class="kw">datetime time_stamp;
   class="type">class="kw">datetime current_time = iTime(Symbol(),TF_2,class="num">0);
   class="type">class="kw">double bid,ask;            
   class=class="str">"cmt">//Update the time
   if(time_stamp != current_time)
     {
      time_stamp = current_time;
      bid=SymbolInfoDouble(Symbol(),SYMBOL_BID);
      ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
      
      class=class="str">"cmt">//Check if we have broken either extreme
      if(PositionsTotal() == class="num">0)
        {
         class=class="str">"cmt">//We are looking for opportunities to sell
         if(iLow(Symbol(),TF_2,class="num">0) > resistance)
           {
            if(rsi[class="num">0] > class="num">70) Trade.Sell(VOL,Symbol(),bid,(ask + (ATR_SIZE * atr[class="num">0])),(ask - (ATR_SIZE * atr[class="num">0])));
           }
         
         class=class="str">"cmt">//We are looking for opportunities to buy
         if(iHigh(Symbol(),TF_2,class="num">0) < support)
           {

RSI 超卖后的下单与宏定义收尾

当 RSI 最新值跌破 30 阈值,脚本以当前卖价 ask 买入 0.1 手,止损设在 bid 减去 1.5 倍 ATR,止盈设在 bid 加上 1.5 倍 ATR,这套边界用 ATR 动态宽度替代固定点数。 交易块跑完之后,程序用 #undef 把 RSI_PERIOD、RSI_PRICE、ATR_PERIOD、ATR_SIZE、TF_1、TF_2、VOL 全部取消定义,避免宏名泄漏到后续编译单元。 紧接着是第二段源码的头部:重新 #define RSI_PERIOD 10、RSI_PRICE 为收盘价、ATR_SIZE 1.5、ATR_PERIOD 14、TF_1 为日线、TF_2 为 M15、VOL 0.1,并引入 CTrade Trade 做下单对象。外汇与贵金属杠杆高,这类固定 0.1 手参数在极端波动下可能触发超预期浮亏,上 MT5 改 VOL 前先算账户净值承受度。

MQL5 / C++
if(rsi[class="num">0] < class="num">30) Trade.Buy(VOL,Symbol(),ask,(bid - (ATR_SIZE * atr[class="num">0])),(bid + (ATR_SIZE * atr[class="num">0])));
class="macro">#undef RSI_PERIOD
class="macro">#undef RSI_PRICE
class="macro">#undef ATR_PERIOD
class="macro">#undef ATR_SIZE
class="macro">#undef TF_1
class="macro">#undef TF_2
class="macro">#undef VOL
class="macro">#define RSI_PERIOD class="num">10
class="macro">#define RSI_PRICE PRICE_CLOSE
class="macro">#define ATR_SIZE class="num">1.5
class="macro">#define ATR_PERIOD class="num">14
class="macro">#define TF_1 PERIOD_D1
class="macro">#define TF_2 PERIOD_M15
class="macro">#define VOL class="num">0.1
class="macro">#include <Trade/Trade.mqh>
CTrade Trade;

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

EA 跑起来最怕两件事:指标句柄重复创建拖慢终端,以及支撑阻力用了过期报价。下面这段初始化骨架把 RSI、ATR 句柄和前 5 根 K 的高低点一次性挂到全局,后续 OnTick 只做更新不重建。 注意 iHigh(Symbol(),TF_1,5) 取的是 TF_1 周期上 shift=5 的那根 K 线高点,不是实时最新价。若 TF_1 选 H1、品种跳空,这个 support/resistance 可能滞后一根,实盘前先在 MT5 用 Comment(support,resistance) 打印核对。 setup() 里 ATR_PERIOD、RSI_PERIOD 都是外部宏,没填具体数字就编译不过;复制代码时先把这些常量在文件头定义好,例如 #define ATR_PERIOD 14#define RSI_PERIOD 14。外汇与贵金属杠杆高,句柄泄漏会让策略在长跑中变卡甚至掉单,释放逻辑不能省。

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;

「小周期上用突破极值抓反转单」

大周期负责画支撑阻力,真正下单的触发逻辑放在更低周期 TF_2 里跑。每次 TF_2 的 0 号 K 线换新(用静态 time_stamp 比对 iTime 判断),才刷新一次 bid / ask 并扫条件,避免每 tick 重复计算。 无持仓时,若 TF_2 当前最低价高于 resistance,且 rsi[0] 大于 70,倾向视为向上假突破后的反转卖点;若 TF_2 当前最高价低于 support,且 rsi[0] 小于 30,则倾向抓向下假突破后的回买。止损止盈都用 ATR_SIZE 倍的 atr[0] 对称展开。 注意原文 ObjectCreate 里把支撑线名字拼成了 "Suppoprt",而删除时叫 "Support",这条线实际删不掉,会在图表上留垃圾。外汇与贵金属杠杆高,假突破失败概率不低,参数未经实盘验证前先用策略测试器跑一遍。 下面这段是 TF_2 触发块的核心,去掉了外层大周期部分,方便直接对照:

MQL5 / C++
{
  class="kw">static class="type">class="kw">datetime time_stamp;
  class="type">class="kw">datetime current_time = iTime(Symbol(),TF_2,class="num">0);
  class="type">class="kw">double bid,ask;
  class=class="str">"cmt">//Update the time
  if(time_stamp != current_time)
    {
     time_stamp = current_time;
     bid=SymbolInfoDouble(Symbol(),SYMBOL_BID);
     ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
     class=class="str">"cmt">//Check if we have broken either extreme
     if(PositionsTotal() == class="num">0)
       {
        class=class="str">"cmt">//We are looking for opportunities to sell
        if(iLow(Symbol(),TF_2,class="num">0) > resistance)
          {
           if(rsi[class="num">0] > class="num">70)
             Trade.Sell(VOL,Symbol(),bid,(ask + (ATR_SIZE * atr[class="num">0])),(ask - (ATR_SIZE * atr[class="num">0])));
          }
        class=class="str">"cmt">//We are looking for opportunities to buy
        if(iHigh(Symbol(),TF_2,class="num">0) < support)
          {
           if(rsi[class="num">0] < class="num">30)
             Trade.Buy(VOL,Symbol(),ask,(bid - (ATR_SIZE * atr[class="num">0])),(bid + (ATR_SIZE * atr[class="num">0])));
          }
       }
    }
}
把阈值计算交给小布盯盘
这些诊断小布盯盘的AIGC已内置,打开对应品种页即可看到该品种平均走势倍数与突破强度,你只管定规则。

常见问题

标准化超买超卖阈值依赖震荡市假设,强劲单边中读数可能始终不穿阈值,导致信号真空,需以相对平均走势的幅度替代绝对读数。
本篇框架固定止损止盈规模,使盈利变化直接来自信号质量提升,而非仓位或目标位变动,便于隔离规则效果。
特定周期往往用于揭示跨市场结构模式,随意改动会破坏模式一致性,因此应以自适应规则而非调周期来应对个体行情。
可以,小布盯盘对应品种页已用AIGC算出历史平均走势及倍数区间,省去手动在策略测试器跑基准的重复劳动。
RSI不再判绝对超买超卖,而是确认突破时的走势幅度是否高于平均倍数,从而过滤弱突破与长期静默。