构建K线图趋势约束模型(第九部分):多策略EA(第一部分)·进阶篇
🧩

构建K线图趋势约束模型(第九部分):多策略EA(第一部分)·进阶篇

(2/3)· 只用RSI和日线约束的EA在震荡市常失灵,这篇教你接进新策略与魔法数字

含代码示例实战向 第 2/3 篇
很多交易者把EA绑死在单一RSI信号上,遇到横盘就反复止损却懒得扩展逻辑。本篇把七类成熟策略模块化接入趋势约束框架,先解决编译警告再谈整合路径。

三种经典策略的数学底子

趋势跟踪这套打法源头在查尔斯·道 1896 年立的道氏理论,核心假设就一句:价格按趋势走,认出趋势就有交易机会。落到计算层,最常用的是简单移动平均线(SMA),用过去 N 个周期的价格求平均把噪音磨平。 理查德·丹尼斯 1980 年代的乌龟实验拿实盘证明,强趋势市里跟趋势确实能挤出可观利润,但震荡市会被反复打脸,外汇和贵金属的高杠杆环境下回撤可能很凶。 区间交易靠的是支撑阻力,J. 韦尔斯·怀尔德 1978 年在《技术交易系统新概念》里把这套写透了。前一周期 High、Low、Close 三数相加除三得到枢轴点 PP,再往外推就出支撑阻力带,约翰·墨菲 1986 年的书强调它们更多是交易者的心理锚点。 突破交易是理查德·唐奇安 1950 年代用通道带起来的,上轨取 N 周期最高 HH、下轨取最低 LL,价格穿出就认为波动释放。卡尼曼和特沃斯基 1979 年的前景理论点出,散户跟风追突破会放大动量,这类品种上假突破概率不低,下单前最好用 MT5 回测通道周期参数。

◍ 利率差与微观结构里的两种抢钱逻辑

套息交易吃的是两种货币之间的利率差,底层逻辑是利率平价理论(IRP)。萨缪尔森在1947年《经济分析基础》里把这套框架系统化,而罗格夫1998年的论文指出,当利率平价出现偏离时,就可能腾出盈利空间。预期收益可写成:高息货币利率减低息货币利率,再叠加预期汇率变动(ΔS);外汇和贵金属杠杆高,汇率一段反向跳动就可能吞掉利差,属高风险玩法。 剥头皮靠市场微观结构理论(MMT)抓极短线的微小波动,哈里斯1998年那篇关于纳斯达克做市商财务表现的论文是重要参考。单笔利润 = 卖出价减买入价再减交易成本,买卖价差和订单流是核心输入。因为单笔盈利极薄,少数几笔滑点偏大的交易就能把前面积累吐光,风控必须按笔闭环。 基本面分析看经济指标与地缘事件对货币估值的冲击,格雷厄姆和多德1934年《证券分析》奠定方法论。除GDP增速、失业率外,跨市场可借市盈率思路:市价除以每股收益(EPS)估偏离。重要经济报告或央行公告发布前后,波动幅度往往明显放大,是这类策略的主要触发窗口。

「RSI 背后的动量假设与实战依据」

技术分析的根基来自查尔斯·道等人的市场观:历史价格轨迹可能包含预测未来行为的线索。在这一框架下,RSI 这类动量指标被用来量化买压与卖压的失衡程度。 RSI 的核心算式是 RS = 特定周期内平均涨幅 / 平均跌幅,再映射为 0–100 的区间值。这个比值不预测方向,只反映当前动量偏离均值的程度。 托马斯·布尔科夫斯基 2008 年《K线图百科全书》的统计数据表明,部分形态叠加指标过滤后,信号胜率较裸价格判断有可观测提升。外汇与贵金属属高杠杆高风险品种,任何指标都只是概率工具,不存在确定性触发。 实操上,开 MT5 插入 RSI 后先观察 14 周期在 EURUSD 日线上的 30/70 边界反应,再决定是否把周期缩短到 9 做日内过滤。

用魔法数字给EA交易打专属戳

在MT5里跑多个EA最怕串味:A策略下的单被B策略误平。魔法数字就是每套EA印在订单上的唯一ID,把它设成input参数,不用改源码就能在外部切换标识,比如默认填12345678。 EA每次扫持仓都用PositionGetInteger(POSITION_MAGIC)把单子的魔法数字捞出来比对,对得上才动刀。这样调试时一笔单能直接反查到来源策略,多策略同账户也不会互相干扰。外汇与贵金属杠杆高,误平一个单可能直接放大回撤,隔离是必要的。 下面这段是引入与使用的核心写法,逐行拆一下: input int MagicNumber = 12345678; // 外部可调的唯一ID,EA所有单都带它 // 在OnTick或相关函数里 for(int i=0; i<PositionsTotal(); i++){ // 遍历全部未平持仓 if(PositionGetSymbol(i)==_Symbol && PositionSelect(PositionGetSymbol(i))){ // 锁定当前品种并选中 long currentMagicNumber = PositionGetInteger(POSITION_MAGIC); // 取该单魔法数字 if(currentMagicNumber == MagicNumber){ // 确认是自家单 // 在这里做平仓或修改等动作 } } } // 开新单时 double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // 取卖价 double sl = price - StopLoss * _Point; // 算止损 double tp = price + TakeProfit * _Point; // 算止盈 if(trade.Buy(Lots, _Symbol, price, sl, tp, "TrendFollowing Buy")){ // 下买单 Print("Buy order placed with Magic Number: ", MagicNumber); // 打印确认 } else { Print("Error placing buy order: ", GetLastError()); // 报错输出 } 开MT5新建EA把这段代码贴进去,把MagicNumber改成你别的策略没用过的数,回测时切到 EURUSD 15分钟图,能直观看到只有带该数的单被处理。

MQL5 / C++
class="kw">input class="type">int MagicNumber = class="num">12345678; class=class="str">"cmt">// Define a unique magic number for the EA
class=class="str">"cmt">// Within the OnTick() or relevant function
for (class="type">int i = class="num">0; i < PositionsTotal(); i++) {
    if (PositionGetSymbol(i) == _Symbol && PositionSelect(PositionGetSymbol(i))) {
        class="type">long currentMagicNumber = PositionGetInteger(POSITION_MAGIC);
        if (currentMagicNumber == MagicNumber) { class=class="str">"cmt">// Check if the position belongs to this EA
            class=class="str">"cmt">// Perform actions on this position, like closing or modifying
        }
    }
}
class=class="str">"cmt">// When opening a new trade
class="type">class="kw">double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
class="type">class="kw">double sl = price - StopLoss * _Point;
class="type">class="kw">double tp = price + TakeProfit * _Point;
if (trade.Buy(Lots, _Symbol, price, sl, tp, "TrendFollowing Buy")) {
    Print("Buy order placed with Magic Number: ", MagicNumber);
} else {
    Print("Error placing buy order: ", GetLastError());
}

◍ 用双EMA给RSI信号加一层趋势过滤

套息交易和基本面策略在当前纯价格数据环境里跑不起来,利率差、GDP、就业率都得接外部源,所以EA里只落地最现实的一条路:趋势跟踪。用50周期与200周期指数移动平均线(EMA)定方向,比单看RSI更少接飞刀。 具体逻辑放在OnInit里用iMA挂两条EMA句柄,OnTick每次重算。短期EMA上穿长期EMA视为上升趋势倾向,下穿则偏空。把这个结果和既有RSI结合:上升途中RSI低于30才开买,下降途中RSI高于70才开卖,进场噪声会明显压缩。 平仓侧也在同一个tick循环里处理——EMA交叉反向时顺势退出,配合CTrade设止损100点、止盈200点、追踪止损50点。外汇与贵金属杠杆高,这套参数仍可能在剧烈反转中断损,实盘前请在MT5策略测试器用至少一年数据回测。 下面这段是可直接拷进MT5看行为的核心片段,注意变量名is_bullish/is_bearish需在你原有RSI模块里先定义:

MQL5 / C++
class=class="str">"cmt">//Calculate moving averages
class="type">class="kw">double ma_short = iMA(_Symbol, PERIOD_CURRENT, class="num">50, class="num">0, MODE_EMA, PRICE_CLOSE, class="num">0);
class="type">class="kw">double ma_long = iMA(_Symbol, PERIOD_CURRENT, class="num">200, class="num">0, MODE_EMA, PRICE_CLOSE, class="num">0);
class=class="str">"cmt">//Determine the trend  direction
class="type">bool is_uptrend = ma_short > ma_long;
class="type">bool is_downtrend = ma_short < ma_long;
if (is_bullish && is_uptrend && rsi_value < RSI_Oversold)
{
   class=class="str">"cmt">// Implement buy order logic here
}
if (is_bearish && is_downtrend && rsi_value > RSI_Overbought)
{
   class=class="str">"cmt">// Implement sell order logic here
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 Trend Constraint Expert.mq5 |
class=class="str">"cmt">//|                                                  Copyright class="num">2024, Clemence Benjamin |
class=class="str">"cmt">//|                                     [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2024, Clemence Benjamin"
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class="macro">#class="kw">property strict
class="macro">#include <Trade\Trade.mqh>   class=class="str">"cmt">// Include the trade library
class=class="str">"cmt">// Input parameters
class="kw">input class="type">int     RSI_Period = class="num">14;            class=class="str">"cmt">// RSI period
class="kw">input class="type">class="kw">double RSI_Overbought = class="num">70.0;      class=class="str">"cmt">// RSI overbought level
class="kw">input class="type">class="kw">double RSI_Oversold = class="num">30.0;        class=class="str">"cmt">// RSI oversold level
class="kw">input class="type">class="kw">double Lots = class="num">0.1;                 class=class="str">"cmt">// Lot size
class="kw">input class="type">class="kw">double StopLoss = class="num">100;             class=class="str">"cmt">// Stop Loss in points
class="kw">input class="type">class="kw">double TakeProfit = class="num">200;           class=class="str">"cmt">// Take Profit in points
class="kw">input class="type">class="kw">double TrailingStop = class="num">50;          class=class="str">"cmt">// Trailing Stop in points
class="kw">input class="type">int    MagicNumber = class="num">12345678;     class=class="str">"cmt">// Magic number for this EA

「把均线方向与RSI塞进EA骨架」

下面这段 EA 骨架把 RSI 句柄和快慢 EMA 都挂上了,能直接丢进 MT5 的 EA 编辑器编译跑通。注意 ma_short 用了 50 周期、ma_long 用了 200 周期 EMA,这是典型的趋势过滤组合,在 XAUUSD 的 H1 上多次出现短线上穿长线后延续的概率偏高,但外汇和贵金属杠杆高,反向刺穿也常见。 OnInit 里只建了 RSI 句柄,没建均线句柄——因为 iMA 在 OnTick 直接取数,省一次全局维护。OnDeinit 用 IndicatorRelease 释放句柄,避免 MT5 终端反复加载 EA 时句柄泄漏。 OnTick 的前几行就干两件事:算 50/200 EMA,比大小定方向;再把 RSI 当前值拷进数组。后面用 for 循环扫当前品种持仓,按 POSITION_TYPE 和 POSITION_MAGIC 辨别仓位,为趋势反转平仓留接口。 让小布替你跑这套 把 RSI_Period 从默认 14 改成 7,在 EURUSD M15 回测,你会看到均线过滤后的信号频率明显上升,但假突破也更多,自己权衡周期。

MQL5 / C++
class=class="str">"cmt">// Global variables
class="type">class="kw">double rsi_value;
class="type">int rsi_handle;
CTrade trade;   class=class="str">"cmt">// Declare an instance of the CTrade class
class=class="str">"cmt">// Variables for moving averages
class="type">class="kw">double ma_short;
class="type">class="kw">double ma_long;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
   class=class="str">"cmt">// Create an RSI indicator handle
   rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE);
   if (rsi_handle == INVALID_HANDLE)
     {
      Print("Failed to create RSI indicator handle");
      class="kw">return(INIT_FAILED);
     }
   class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert deinitialization function                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(class="kw">const class="type">int reason)
  {
   class=class="str">"cmt">// Release the RSI indicator handle
   IndicatorRelease(rsi_handle);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert tick function                                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
   class=class="str">"cmt">// Calculate moving averages
   ma_short = iMA(_Symbol, PERIOD_CURRENT, class="num">50, class="num">0, MODE_EMA, PRICE_CLOSE);
   ma_long  = iMA(_Symbol, PERIOD_CURRENT, class="num">200, class="num">0, MODE_EMA, PRICE_CLOSE);
   class=class="str">"cmt">// Determine trend direction
   class="type">bool is_uptrend = ma_short > ma_long;
   class="type">bool is_downtrend = ma_short < ma_long;
   class=class="str">"cmt">// Get the RSI value for the current bar
   class="type">class="kw">double rsi_values[];
   if (CopyBuffer(rsi_handle, class="num">0, class="num">0, class="num">1, rsi_values) <= class="num">0)
     {
      Print("Failed to get RSI value");
      class="kw">return;
     }
   rsi_value = rsi_values[class="num">0];
   class=class="str">"cmt">// Close open positions if the trend changes
   for (class="type">int i = class="num">0; i < PositionsTotal(); i++) class=class="str">"cmt">// Correct loop initialization
     {
      if (PositionGetSymbol(i) == _Symbol && PositionSelect(PositionGetSymbol(i)))  class=class="str">"cmt">// Select position by symbol
        {
         class="type">long position_type = PositionGetInteger(POSITION_TYPE);
         class="type">long currentMagicNumber = PositionGetInteger(POSITION_MAGIC);
交给小布盯盘看多周期共振
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到 D1 情绪与 M1 极端位的叠加提示,你只需核对策略开关。

常见问题

D1定方向做约束,M1提供更早的极端RSI信号以缩小入场延迟,二者分工降低逆势概率。
订单票号或魔法数字超出int范围时会触发第78列这类警告,改long即可兼容大数值订单标识。
小布已内置多周期情绪与策略冲突提示,可映射本文魔法数字分仓逻辑,但实盘参数仍需你自调,外汇贵金属高风险。
可能。若D1看跌但利差显著,模块应设权重衰减,否则易在急跌中暴露敞口。
区分同品种不同子策略的订单,便于分别平仓与统计,避免信号互锁。