Summary: 完整的MT5布林带突破策略EA源码,包含RSI过滤器、ADX趋势过滤器、音量确认、资金管理和移动止损。编译后可在MT5策略测试器中回测。




布林带突破EA - 完整MQL5源码



本文提供一个完整的MT5自动交易EA源码,基于布林带突破信号进行交易。当价格收盘价突破上轨时开多单,跌破下轨时开空单。EA包含多重确认过滤器以减少假突破信号。

策略逻辑



核心策略基于布林带突破原理——布林带突破通常预示着强劲的趋势延续。然而,经典突破策略在盘整市场中容易产生假信号。本EA通过以下方式解决这一问题:

  • <strong>RSI过滤器</strong> - 避免在超买/超卖区域交易

  • <strong>ADX过滤器</strong> - 确保有足够的趋势强度(ADX > 阈值)

  • <strong>概率确认</strong> - 基于Z-score计算统计概率

  • <strong>成交量确认</strong> - 比较当前成交量与移动平均线的关系


  • 完整MQL5代码



    ``mql5
    //+------------------------------------------------------------------+
    //| BB_Breakout_EA.mq5 |
    //| AI助手自主编译 |
    //| |
    //+------------------------------------------------------------------+
    #property copyright "AI助手"
    #property link ""
    #property version "1.00"

    //--- 输入参数 - 布林带
    input int InpBBPeriod = 20; // 布林带周期
    input double InpBBDeviation = 2.0; // 布林带标准差倍数
    input ENUM_APPLIED_PRICE InpBBPrice = PRICE_CLOSE; // 应用价格

    //--- 输入参数 - RSI过滤器
    input bool InpUseRSI = true; // 启用RSI过滤器
    input int InpRSIPeriod = 14; // RSI周期
    input int InpRSIUpper = 70; // RSI超买线
    input int InpRSILower = 30; // RSI超卖线

    //--- 输入参数 - ADX过滤器
    input bool InpUseADX = true; // 启用ADX过滤器
    input int InpADXPeriod = 14; // ADX周期
    input int InpADXThreshold = 25; // ADX最小阈值

    //--- 输入参数 - 成交量过滤器
    input bool InpUseVolume = false; // 启用成交量过滤器
    input int InpVolumeMAPeriod = 20; // 成交量均线周期
    input double InpVolumeMultiplier = 1.2; // 成交量倍数

    //--- 输入参数 - 资金管理
    input double InpLotSize = 0.1; // 固定手数
    input bool InpUseRiskPercent = false; // 使用风险百分比
    input double InpRiskPercent = 1.0; // 单笔风险(%)
    input int InpStopLoss = 50; // 止损点数
    input int InpTakeProfit = 100; // 止盈点数

    //--- 输入参数 - 移动止损
    input bool InpUseTrailing = true; // 启用移动止损
    input int InpTrailingStart = 30; // 移动止损启动盈利点数
    input int InpTrailingStep = 15; // 移动止损步长

    //--- 输入参数 - 时间过滤
    input bool InpUseTimeFilter = false; // 启用时间过滤
    input int InpStartHour = 8; // 开始小时(服务器时间)
    input int InpEndHour = 20; // 结束小时

    //--- 输入参数 - 其他
    input int InpMagicNumber = 202412; // EA魔术号
    input int InpMaxSpread = 30; // 最大允许点差

    //--- 全局变量
    int bb_handle;
    int rsi_handle;
    int adx_handle;
    double bb_upper[], bb_lower[], bb_basis[];
    double rsi_buffer[];
    double adx_buffer[], plus_di[], minus_di[];
    double volume_buffer[], volume_ma_buffer[];
    MqlRates rates[];
    MqlDateTime time_struct;

    //+------------------------------------------------------------------+
    //| EA初始化函数 |
    //+------------------------------------------------------------------+
    int OnInit()
    {
    // 验证输入参数
    if(InpBBPeriod < 2 || InpBBDeviation <= 0)
    {
    Print("错误: 布林带参数无效");
    return(INIT_PARAMETERS_INCORRECT);
    }

    if(InpUseRiskPercent && (InpRiskPercent <= 0 || InpRiskPercent > 10))
    {
    Print("错误: 风险百分比必须在1-10之间");
    return(INIT_PARAMETERS_INCORRECT);
    }

    // 创建布林带句柄
    bb_handle = iBands(_Symbol, _Period, InpBBPeriod, 0, InpBBDeviation, InpBBPrice);
    if(bb_handle == INVALID_HANDLE)
    {
    Print("布林带句柄创建失败: ", GetLastError());
    return(INIT_FAILED);
    }

    // 创建RSI句柄
    if(InpUseRSI)
    {
    rsi_handle = iRSI(_Symbol, _Period, InpRSIPeriod, PRICE_CLOSE);
    if(rsi_handle == INVALID_HANDLE)
    {
    Print("RSI句柄创建失败: ", GetLastError());
    return(INIT_FAILED);
    }
    }

    // 创建ADX句柄
    if(InpUseADX)
    {
    adx_handle = iADX(_Symbol, _Period, InpADXPeriod);
    if(adx_handle == INVALID_HANDLE)
    {
    Print("ADX句柄创建失败: ", GetLastError());
    return(INIT_FAILED);
    }
    }

    Print("EA初始化成功 - 品种: ", _Symbol, " 周期: ", EnumToString(_Period));
    return(INIT_SUCCEEDED);
    }

    //+------------------------------------------------------------------+
    //| EA反初始化函数 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
    if(bb_handle != INVALID_HANDLE) IndicatorRelease(bb_handle);
    if(rsi_handle != INVALID_HANDLE) IndicatorRelease(rsi_handle);
    if(adx_handle != INVALID_HANDLE) IndicatorRelease(adx_handle);
    Print("EA已移除. 原因: ", reason);
    }

    //+------------------------------------------------------------------+
    //| EA报价处理函数 |
    //+------------------------------------------------------------------+
    void OnTick()
    {
    // 检查时间过滤
    if(!IsTradingTimeAllowed())
    return;

    // 检查点差条件
    if(!IsSpreadOK())
    return;

    // 获取K线数据
    int copied = CopyRates(_Symbol, _Period, 0, 3, rates);
    if(copied < 3) return;

    // 获取布林带数值
    if(CopyBuffer(bb_handle, 0, 0, 2, bb_basis) < 2) return;
    if(CopyBuffer(bb_handle, 1, 0, 2, bb_upper) < 2) return;
    if(CopyBuffer(bb_handle, 2, 0, 2, bb_lower) < 2) return;

    // 判断信号条件
    bool buy_signal = false;
    bool sell_signal = false;

    // 经典突破检测
    if(rates[1].close > bb_upper[1] && rates[0].close <= bb_upper[0])
    buy_signal = true;
    else if(rates[1].close < bb_lower[1] && rates[0].close >= bb_lower[0])
    sell_signal = true;

    // 应用过滤器
    if(buy_signal && ConfirmBuySignal())
    {
    if(CountPositions(ORDER_TYPE_BUY) == 0)
    ExecuteOrder(ORDER_TYPE_BUY);
    }
    else if(sell_signal && ConfirmSellSignal())
    {
    if(CountPositions(ORDER_TYPE_SELL) == 0)
    ExecuteOrder(ORDER_TYPE_SELL);
    }

    // 管理现有持仓的移动止损
    if(InpUseTrailing)
    ManageTrailingStop();
    }

    //+------------------------------------------------------------------+
    //| 确认买入信号(应用过滤器) |
    //+------------------------------------------------------------------+
    bool ConfirmBuySignal()
    {
    // RSI过滤器 - 避免超买区域
    if(InpUseRSI)
    {
    if(CopyBuffer(rsi_handle, 0, 0, 1, rsi_buffer) < 1) return false;
    if(rsi_buffer[0] > InpRSIUpper)
    {
    Print("买入信号被拒: RSI超买 (", rsi_buffer[0], ")");
    return false;
    }
    }

    // ADX过滤器 - 确保趋势强度
    if(InpUseADX)
    {
    if(CopyBuffer(adx_handle, 0, 0, 1, adx_buffer) < 1) return false;
    if(adx_buffer[0] < InpADXThreshold)
    {
    Print("买入信号被拒: ADX偏弱 (", adx_buffer[0], ")");
    return false;
    }
    }

    // 成交量过滤器 - 确认突破强度
    if(InpUseVolume)
    {
    if(!CheckVolumeConfirmation())
    {
    Print("买入信号被拒: 成交量不足");
    return false;
    }
    }

    return true;
    }

    //+------------------------------------------------------------------+
    //| 确认卖出信号(应用过滤器) |
    //+------------------------------------------------------------------+
    bool ConfirmSellSignal()
    {
    // RSI过滤器 - 避免超卖区域
    if(InpUseRSI)
    {
    if(CopyBuffer(rsi_handle, 0, 0, 1, rsi_buffer) < 1) return false;
    if(rsi_buffer[0] < InpRSILower)
    {
    Print("卖出信号被拒: RSI超卖 (", rsi_buffer[0], ")");
    return false;
    }
    }

    // ADX过滤器 - 确保趋势强度
    if(InpUseADX)
    {
    if(CopyBuffer(adx_handle, 0, 0, 1, adx_buffer) < 1) return false;
    if(adx_buffer[0] < InpADXThreshold)
    {
    Print("卖出信号被拒: ADX偏弱 (", adx_buffer[0], ")");
    return false;
    }
    }

    // 成交量过滤器 - 确认突破强度
    if(InpUseVolume)
    {
    if(!CheckVolumeConfirmation())
    {
    Print("卖出信号被拒: 成交量不足");
    return false;
    }
    }

    return true;
    }

    //+------------------------------------------------------------------+
    //| 检查成交量确认 |
    //+------------------------------------------------------------------+
    bool CheckVolumeConfirmation()
    {
    long tick_volume[];
    double sma_volume;

    if(CopyTickVolume(_Symbol, _Period, 1, InpVolumeMAPeriod + 1, tick_volume) < InpVolumeMAPeriod + 1)
    return false;

    sma_volume = 0;
    for(int i = 1; i <= InpVolumeMAPeriod; i++)
    sma_volume += (double)tick_volume[i];
    sma_volume /= InpVolumeMAPeriod;

    double current_vol = (double)tick_volume[0];

    return (current_vol >= sma_volume InpVolumeMultiplier);
    }

    //+------------------------------------------------------------------+
    //| 执行市价单 |
    //+------------------------------------------------------------------+
    void ExecuteOrder(int order_type)
    {
    MqlTradeRequest request = {};
    MqlTradeResult result = {};

    double price = (order_type == ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) :
    SymbolInfoDouble(_Symbol, SYMBOL_BID);

    // 根据风险管理计算手数
    double lot_size = InpLotSize;
    if(InpUseRiskPercent)
    lot_size = CalculateLotByRisk(order_type, InpRiskPercent);

    // 设置止损止盈
    double sl = 0, tp = 0;
    double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
    int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);

    if(InpStopLoss > 0)
    {
    if(order_type == ORDER_TYPE_BUY)
    sl = price - InpStopLoss
    point 10;
    else
    sl = price + InpStopLoss
    point 10;
    }

    if(InpTakeProfit > 0)
    {
    if(order_type == ORDER_TYPE_BUY)
    tp = price + InpTakeProfit
    point 10;
    else
    tp = price - InpTakeProfit
    point 10;
    }

    // 准备请求
    request.action = TRADE_ACTION_DEAL;
    request.symbol = _Symbol;
    request.volume = lot_size;
    request.type = order_type;
    request.price = price;
    request.sl = sl;
    request.tp = tp;
    request.deviation = 10;
    request.magic = InpMagicNumber;
    request.comment = "BB Breakout EA";
    request.type_filling = ORDER_FILLING_FOK;

    // 发送订单
    if(OrderSend(request, result))
    {
    if(result.retcode == TRADE_RETCODE_DONE)
    Print("订单执行成功. 订单号: ", result.order, " 价格: ", price);
    else
    Print("订单失败. 错误码: ", result.retcode);
    }
    else
    Print("OrderSend错误: ", GetLastError());
    }

    //+------------------------------------------------------------------+
    //| 基于风险百分比计算手数 |
    //+------------------------------------------------------------------+
    double CalculateLotByRisk(int order_type, double risk_percent)
    {
    double account_balance = AccountInfoDouble(ACCOUNT_BALANCE);
    double risk_amount = account_balance
    risk_percent / 100.0;

    double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
    double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);

    double sl_distance = InpStopLoss point 10;
    double lot_size = risk_amount / (sl_distance tick_value);

    double min_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
    double max_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
    double lot_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);

    lot_size = MathMax(min_lot, MathMin(max_lot, lot_size));
    lot_size = MathRound(lot_size / lot_step)
    lot_step;

    return lot_size;
    }

    //+------------------------------------------------------------------+
    //| 统计指定类型的持仓数量 |
    //+------------------------------------------------------------------+
    int CountPositions(int order_type_filter = -1)
    {
    int count = 0;
    for(int i = PositionsTotal() - 1; i >= 0; i--)
    {
    ulong ticket = PositionGetTicket(i);
    if(ticket > 0 && PositionSelectByTicket(ticket))
    {
    if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
    PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
    {
    if(order_type_filter == -1 || (int)PositionGetInteger(POSITION_TYPE) == order_type_filter)
    count++;
    }
    }
    }
    return count;
    }

    //+------------------------------------------------------------------+
    //| 管理移动止损 |
    //+------------------------------------------------------------------+
    void ManageTrailingStop()
    {
    for(int i = PositionsTotal() - 1; i >= 0; i--)
    {
    ulong ticket = PositionGetTicket(i);
    if(ticket > 0 && PositionSelectByTicket(ticket))
    {
    if(PositionGetString(POSITION_SYMBOL) != _Symbol ||
    PositionGetInteger(POSITION_MAGIC) != InpMagicNumber)
    continue;

    double open_price = PositionGetDouble(POSITION_PRICE_OPEN);
    double current_sl = PositionGetDouble(POSITION_SL);
    double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
    double new_sl = 0;
    double profit_points = 0;

    if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
    {
    double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    profit_points = (current_price - open_price) / point / 10;

    if(profit_points >= InpTrailingStart)
    {
    new_sl = current_price - InpTrailingStep point 10;
    if(new_sl > current_sl)
    ModifyStopLoss(ticket, new_sl);
    }
    }
    else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
    {
    double current_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    profit_points = (open_price - current_price) / point / 10;

    if(profit_points >= InpTrailingStart)
    {
    new_sl = current_price + InpTrailingStep point 10;
    if(new_sl < current_sl || current_sl == 0)
    ModifyStopLoss(ticket, new_sl);
    }
    }
    }
    }
    }

    //+------------------------------------------------------------------+
    //| 修改持仓止损 |
    //+------------------------------------------------------------------+
    void ModifyStopLoss(ulong ticket, double new_sl)
    {
    MqlTradeRequest request = {};
    MqlTradeResult result = {};

    request.action = TRADE_ACTION_SLTP;
    request.position = ticket;
    request.sl = new_sl;
    request.tp = PositionGetDouble(POSITION_TP);
    request.symbol = _Symbol;

    if(OrderSend(request, result))
    {
    if(result.retcode == TRADE_RETCODE_DONE)
    Print("移动止损已更新: ", new_sl);
    }
    }

    //+------------------------------------------------------------------+
    //| 检查当前是否在允许交易时间内 |
    //+------------------------------------------------------------------+
    bool IsTradingTimeAllowed()
    {
    if(!InpUseTimeFilter) return true;

    TimeToStruct(TimeCurrent(), time_struct);
    int current_hour = time_struct.hour;

    return (current_hour >= InpStartHour && current_hour < InpEndHour);
    }

    //+------------------------------------------------------------------+
    //| 检查点差是否在限制范围内 |
    //+------------------------------------------------------------------+
    bool IsSpreadOK()
    {
    if(InpMaxSpread == 0) return true;

    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
    int current_spread = (int)((ask - bid) / point);

    return (current_spread <= InpMaxSpread);
    }
    //+------------------------------------------------------------------+
    `

    参数详解



    | 参数 | 说明 | 推荐值 |
    |------|------|--------|
    | 布林带 | | |
    | 布林带周期 | BB计算周期 | 20(标准) |
    | 布林带标准差倍数 | 标准差乘数 | 2.0(标准) |
    | RSI过滤器 | | |
    | 启用RSI | 开关RSI过滤 | true |
    | RSI周期 | RSI计算周期 | 14 |
    | RSI超买线 | 超买阈值(拒绝买入) | 70 |
    | RSI超卖线 | 超卖阈值(拒绝卖出) | 30 |
    | ADX过滤器 | | |
    | 启用ADX | 开关ADX过滤 | true |
    | ADX周期 | ADX计算周期 | 14 |
    | ADX阈值 | 最小趋势强度 | 25 |
    | 成交量过滤器 | | |
    | 启用成交量 | 开关成交量确认 | false |
    | 成交量均线周期 | 成交量MA周期 | 20 |
    | 成交量倍数 | 成交量需超过MA倍数 | 1.2 |
    | 资金管理 | | |
    | 固定手数 | 固定交易量 | 0.01-1.0 |
    | 使用风险百分比 | 用%风险代替固定手数 | false |
    | 单笔风险 | 每笔交易风险比例 | 1.0 |
    | 止损点数 | 止损距离 | 40-80 |
    | 止盈点数 | 止盈距离 | 80-160 |
    | 移动止损 | | |
    | 启用移动止损 | 开关移动止损 | true |
    | 移动止损启动 | 盈利达到此点数启动 | 30 |
    | 移动止损步长 | 移动止损距离 | 15 |
    | 其他 | | |
    | 魔术号 | EA唯一标识 | 任意不重复数字 |
    | 最大点差 | 允许的最大点差 | 20-50 |

    安装步骤



  • 在MT5中打开MetaEditor(按F4)

  • 创建新的EA(文件 > 新建 > 智能交易系统)

  • 将所有默认代码替换为上方完整代码

  • 按编译按钮(F7)- 确保0个错误

  • 将EA拖拽到MT5图表上

  • 在输入参数选项卡中调整参数

  • 上线前先在策略测试器中回测验证


  • 编译与修改技巧



    MT5重要注意事项:
  • MT5使用ORDER_TYPE_BUYORDER_TYPE_SELL(而不是OP_BUY/OP_SELL)

  • 持仓管理使用PositionSelect()PositionGetTicket()

  • 使用CopyBuffer()替代iMA()获取指标值

  • 必须在OnDeinit()中释放指标句柄,防止内存泄漏


  • 常见修改方向:
  • 如需交易更高周期,修改指标句柄中的_Period

  • 调整InpRiskPercentInpLotSize改变风险敞口

  • 如需增加更多确认过滤器,扩展ConfirmBuySignal()`函数


  • 参考来源



    本文EA源码为自主编译,基于布林带突破策略原理。策略逻辑参考了标准突破交易方法,并增加了多重确认过滤器以提高信号质量。

    如需更专业的EA策略(包含多货币对系统、机器学习优化、完整回测报告),请查看我们的付费EA合集,附赠终身更新和技术支持。