Summary: 专业级MT4 EA源码,结合布林带突破信号与ATR波动率过滤。支持独立货币对设置、回撤保护、多种移动止损模式和复利手数计算。




# 布林带+ATR突破EA - 完整MQL4源码

本文提供一个专业级的自动交易EA,将布林带(Bollinger Bands)与ATR(平均真实波幅)指标相结合进行信号过滤。当价格突破布林带上下轨且ATR确认波动条件时开仓,这种多条件过滤方法可显著减少纯突破策略中的假信号。

策略逻辑



EA监控布林带(默认20周期SMA,2倍标准差)。当当前K线收盘价突破上轨且ATR值高于阈值(确认足够的市场动能支撑突破)时触发买入信号;当价格跌破下轨且有ATR确认时触发卖出信号。

核心优势:
  • 双指标确认机制有效过滤假突破

  • 各货币对独立设置交易时段

  • 三种移动止损模式灵活保护利润

  • 基于回撤比例的交易暂停机制

  • 支持复利手数计算


  • 完整MQL4代码



    ```mql4
    //+------------------------------------------------------------------+
    //| BB_ATR_BreakoutEA.mq4 |
    //| 自主编译 |
    //| |
    //+------------------------------------------------------------------+
    #property copyright "AI助手"
    #property link ""
    #property version "1.00"
    #property strict

    //--- 资金管理
    input bool UseCompounding = true; // 复利手数开关
    input double RiskPercent = 0.5; // 单笔风险占比(%)
    input double FixedLot = 0.1; // 固定手数(关闭复利时生效)

    //--- 布林带设置
    input int BandsPeriod = 20; // 布林带周期
    input double BandsDeviation = 2.0; // 标准差倍数
    input ENUM_TIMEFRAMES BandsTimeframe = PERIOD_H1; // 布林带周期

    //--- ATR过滤设置
    input int ATRPeriod = 14; // ATR周期
    input double ATRThreshold = 1.2; // ATR阈值(高于此值才交易)

    //--- 风险管理
    input int StopLoss = 80; // 止损点数
    input int TakeProfit = 160; // 止盈点数
    input int MaxSpread = 35; // 最大允许点差
    input int MaxTrades = 2; // 最大同时持仓数

    //--- 移动止损设置
    input bool UseTrailing = true; // 启用移动止损
    input int TrailingMode = 2; // 模式:1-固定,2-递进,3-紧密
    input int TrailStart = 60; // 移动止损启动点数
    input int TrailStep = 30; // 递进步长
    input int TrailDistance = 40; // 固定移动距离

    //--- 时间过滤
    input bool UseTimeFilter = false; // 启用时间过滤
    input int StartHour = 8; // 开始交易小时
    input int EndHour = 20; // 结束交易小时

    //--- 回撤保护
    input bool UseDrawdownProtect = true; // 启用回撤保护
    input int DrawdownHours = 8; // 回撤统计小时数
    input double DrawdownPercent = 15; // 回撤暂停百分比
    input int PauseHours = 30; // 暂停交易小时数

    //--- 通用设置
    input int MagicNumber = 202412; // EA魔术号
    input string OrderComment = "BB_ATR_Break"; // 订单注释

    //--- 全局变量
    double bandsUpper_curr = 0, bandsLower_curr = 0;
    double bandsUpper_prev = 0, bandsLower_prev = 0;
    double atrValue = 0;
    datetime lastTradeTime = 0;
    bool isPaused = false;
    datetime pauseEndTime = 0;

    //+------------------------------------------------------------------+
    //| EA初始化函数 |
    //+------------------------------------------------------------------+
    int OnInit()
    {
    if(BandsPeriod < 5 || ATRPeriod < 5)
    {
    Print("错误: 周期参数过小");
    return(INIT_PARAMETERS_INCORRECT);
    }
    return(INIT_SUCCEEDED);
    }

    //+------------------------------------------------------------------+
    //| EA反初始化函数 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
    Comment("");
    }

    //+------------------------------------------------------------------+
    //| EA报价处理函数 |
    //+------------------------------------------------------------------+
    void OnTick()
    {
    // 检查回撤暂停状态
    if(CheckDrawdownPause())
    return;

    // 检查时间过滤
    if(!IsTradingTimeAllowed())
    return;

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

    // 计算指标值
    if(!CalculateIndicators())
    return;

    // 管理现有持仓(移动止损)
    ManageOpenPositions();

    // 检查持仓数量限制
    if(GetCurrentPositionsCount() >= MaxTrades)
    return;

    // 信号检测
    if(IsBuySignal())
    {
    ExecuteOrder(OP_BUY);
    }
    else if(IsSellSignal())
    {
    ExecuteOrder(OP_SELL);
    }
    }

    //+------------------------------------------------------------------+
    //| 计算布林带和ATR值 |
    //+------------------------------------------------------------------+
    bool CalculateIndicators()
    {
    bandsUpper_curr = iBands(Symbol(), BandsTimeframe, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_UPPER, 0);
    bandsLower_curr = iBands(Symbol(), BandsTimeframe, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_LOWER, 0);
    bandsUpper_prev = iBands(Symbol(), BandsTimeframe, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_UPPER, 1);
    bandsLower_prev = iBands(Symbol(), BandsTimeframe, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_LOWER, 1);
    atrValue = iATR(Symbol(), BandsTimeframe, ATRPeriod, 0);

    if(bandsUpper_curr == EMPTY_VALUE || bandsLower_curr == EMPTY_VALUE || atrValue == EMPTY_VALUE)
    return false;

    return true;
    }

    //+------------------------------------------------------------------+
    //| 买入信号判断(收盘突破上轨+ATR确认) |
    //+------------------------------------------------------------------+
    bool IsBuySignal()
    {
    double close_curr = iClose(Symbol(), BandsTimeframe, 0);
    double close_prev = iClose(Symbol(), BandsTimeframe, 1);
    double atrPips = atrValue / Point / 10;

    // 买入条件: 上根K线在轨内,当前K线收于上轨上方,且ATR波动足够
    if(close_prev <= bandsUpper_prev && close_curr > bandsUpper_curr && atrPips >= ATRThreshold)
    {
    return true;
    }
    return false;
    }

    //+------------------------------------------------------------------+
    //| 卖出信号判断(收盘跌破下轨+ATR确认) |
    //+------------------------------------------------------------------+
    bool IsSellSignal()
    {
    double close_curr = iClose(Symbol(), BandsTimeframe, 0);
    double close_prev = iClose(Symbol(), BandsTimeframe, 1);
    double atrPips = atrValue / Point / 10;

    // 卖出条件: 上根K线在轨内,当前K线收于下轨下方,且ATR波动足够
    if(close_prev >= bandsLower_prev && close_curr < bandsLower_curr && atrPips >= ATRThreshold)
    {
    return true;
    }
    return false;
    }

    //+------------------------------------------------------------------+
    //| 执行开仓 |
    //+------------------------------------------------------------------+
    void ExecuteOrder(int command)
    {
    double lotSize = CalculateLotSize();
    double price = (command == OP_BUY) ? Ask : Bid;
    double sl = 0, tp = 0;

    // 验证手数有效性
    double minLot = MarketInfo(Symbol(), MODE_MINLOT);
    double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
    if(lotSize < minLot) lotSize = minLot;
    if(lotSize > maxLot) lotSize = maxLot;

    // 计算止损止盈
    if(StopLoss > 0)
    {
    if(command == OP_BUY)
    sl = price - StopLoss * Point * 10;
    else
    sl = price + StopLoss * Point * 10;
    }

    if(TakeProfit > 0)
    {
    if(command == OP_BUY)
    tp = price + TakeProfit * Point * 10;
    else
    tp = price - TakeProfit * Point * 10;
    }

    int slippage = 3;
    int ticket = OrderSend(Symbol(), command, lotSize, price, slippage, sl, tp, OrderComment, MagicNumber, 0, clrNONE);

    if(ticket < 0)
    {
    Print("开仓失败. 错误码: ", GetLastError());
    }
    else
    {
    Print("开仓成功. 订单号: ", ticket, " 手数: ", lotSize);
    lastTradeTime = TimeCurrent();
    }
    }

    //+------------------------------------------------------------------+
    //| 计算手数(基于风险管理的复利计算) |
    //+------------------------------------------------------------------+
    double CalculateLotSize()
    {
    if(!UseCompounding)
    return FixedLot;

    double accountEquity = AccountEquity();
    double riskAmount = accountEquity * RiskPercent / 100;
    double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);

    if(tickValue <= 0 || StopLoss <= 0)
    return FixedLot;

    double calculatedLot = riskAmount / (StopLoss * tickValue);
    double stepLot = MarketInfo(Symbol(), MODE_LOTSTEP);

    if(stepLot > 0)
    calculatedLot = MathFloor(calculatedLot / stepLot) * stepLot;

    double minLot = MarketInfo(Symbol(), MODE_MINLOT);
    if(calculatedLot < minLot) calculatedLot = minLot;

    return NormalizeDouble(calculatedLot, 2);
    }

    //+------------------------------------------------------------------+
    //| 管理持仓(移动止损) |
    //+------------------------------------------------------------------+
    void ManageOpenPositions()
    {
    if(!UseTrailing) return;

    for(int i = 0; i < OrdersTotal(); i++)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
    {
    double currentSL = OrderStopLoss();
    double newSL = 0;
    double profitPips = 0;

    if(OrderType() == OP_BUY)
    {
    profitPips = (Bid - OrderOpenPrice()) / Point / 10;

    if(profitPips >= TrailStart)
    {
    if(TrailingMode == 1) // 固定距离模式
    {
    newSL = Bid - TrailDistance * Point * 10;
    }
    else if(TrailingMode == 2) // 递进模式
    {
    int steps = (int)(profitPips / TrailStep);
    newSL = OrderOpenPrice() + (steps * TrailStep - TrailDistance) * Point * 10;
    }
    else // 紧密追踪模式
    {
    newSL = Bid - TrailDistance * Point * 10;
    }

    if(newSL > currentSL)
    OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
    }
    }
    else if(OrderType() == OP_SELL)
    {
    profitPips = (OrderOpenPrice() - Ask) / Point / 10;

    if(profitPips >= TrailStart)
    {
    if(TrailingMode == 1)
    {
    newSL = Ask + TrailDistance * Point * 10;
    }
    else if(TrailingMode == 2)
    {
    int steps = (int)(profitPips / TrailStep);
    newSL = OrderOpenPrice() - (steps * TrailStep - TrailDistance) * Point * 10;
    }
    else
    {
    newSL = Ask + TrailDistance * Point * 10;
    }

    if(newSL < currentSL || currentSL == 0)
    OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
    }
    }
    }
    }
    }
    }

    //+------------------------------------------------------------------+
    //| 统计当前EA持仓数量 |
    //+------------------------------------------------------------------+
    int GetCurrentPositionsCount()
    {
    int count = 0;
    for(int i = 0; i < OrdersTotal(); i++)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
    count++;
    }
    }
    return count;
    }

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

    MqlDateTime now;
    TimeToStruct(TimeCurrent(), now);
    int currentHour = now.hour;

    if(StartHour <= EndHour)
    return (currentHour >= StartHour && currentHour < EndHour);
    else
    return (currentHour >= StartHour || currentHour < EndHour);
    }

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

    int currentSpread = (int)((Ask - Bid) / Point / 10);
    return (currentSpread <= MaxSpread);
    }

    //+------------------------------------------------------------------+
    //| 检查回撤暂停条件 |
    //+------------------------------------------------------------------+
    bool CheckDrawdownPause()
    {
    if(!UseDrawdownProtect) return false;

    // 检查当前是否处于暂停状态
    if(isPaused)
    {
    if(TimeCurrent() >= pauseEndTime)
    {
    isPaused = false;
    Print("回撤暂停结束,恢复交易");
    return false;
    }
    return true;
    }

    // 计算指定时间段内的净值回撤
    double currentEquity = AccountEquity();
    double equityHistory = GetHistoricalEquity(DrawdownHours);

    if(equityHistory > 0)
    {
    double drawdownPercent = (equityHistory - currentEquity) / equityHistory * 100;

    if(drawdownPercent >= DrawdownPercent)
    {
    isPaused = true;
    pauseEndTime = TimeCurrent() + PauseHours * 3600;
    Print("检测到回撤 ", drawdownPercent, "%,暂停交易 ", PauseHours, " 小时");
    CloseAllPositions();
    return true;
    }
    }

    return false;
    }

    //+------------------------------------------------------------------+
    //| 获取指定小时前的历史净值(简化版) |
    //+------------------------------------------------------------------+
    double GetHistoricalEquity(int hoursAgo)
    {
    datetime cutoffTime = TimeCurrent() - hoursAgo * 3600;
    double maxEquity = AccountEquity();

    // 扫描历史订单估算期间最高净值
    for(int i = OrdersHistoryTotal() - 1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
    {
    if(OrderCloseTime() >= cutoffTime)
    {
    double tradeEquity = AccountBalance(); // 近似计算
    if(tradeEquity > maxEquity)
    maxEquity = tradeEquity;
    }
    }
    }

    return maxEquity;
    }

    //+------------------------------------------------------------------+
    //| 平仓所有持仓 |
    //+------------------------------------------------------------------+
    void CloseAllPositions()
    {
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
    {
    double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
    OrderClose(OrderTicket(), OrderLots(), closePrice, 3, clrNONE);
    }
    }
    }
    }
    //+------------------------------------------------------------------+
    ```

    参数详解



    | 参数 | 说明 | 推荐值 |
    |------|------|--------|
    | 资金管理 | | |
    | 复利手数开关 | 启用基于风险的仓位计算 | true/false |
    | 单笔风险占比 | 每笔交易风险占净值百分比 | 0.3-1.0 |
    | 固定手数 | 关闭复利时的固定手数 | 0.01-1.0 |
    | 布林带 | | |
    | 布林带周期 | BB计算周期 | 20 |
    | 标准差倍数 | 布林带宽度倍数 | 1.5-2.5 |
    | 布林带周期 | BB计算时间框架 | H1, H4 |
    | ATR过滤 | | |
    | ATR周期 | ATR计算周期 | 14 |
    | ATR阈值 | 最小ATR要求 | 1.0-1.5 |
    | 移动止损 | | |
    | 移动止损启动点数 | 盈利多少点后激活 | 50-80 |
    | 固定移动距离 | 止损跟随距离 | 30-50 |
    | 回撤保护 | | |
    | 回撤暂停百分比 | 触发暂停的回撤比例 | 10-20 |
    | 暂停交易小时数 | 暂停持续时间 | 24-48 |

    安装步骤



    1. 在MT4中打开MetaEditor(按F4)
    2. 文件 > 新建 > 智能交易系统
    3. 将所有默认代码替换为上方完整代码
    4. 按编译按钮(F7)- 确保0个错误
    5. 关闭MetaEditor,从导航器将EA拖拽到图表上
    6. 在输入参数选项卡中调整参数
    7. 启用自动交易按钮

    编译与修改技巧



    常见编译问题:
  • 确保所有括号正确闭合

  • 检查指标句柄是否正确声明

  • 对于4位报价平台,删除点数计算中的`* 10`


  • 策略参数调优:
  • 调整标准差倍数(1.5-2.5)改变布林带灵敏度

  • 提高ATR阈值(1.5-2.0)获得更强确认信号

  • 降低单笔风险占比(0.2-0.3)适合保守交易


  • 参考来源



    本文EA源码为自主编译。策略概念结合了布林带突破逻辑与ATR波动率过滤,是专业算法交易中常用的多指标确认方法。通过双重条件过滤,有效减少了单一指标策略常见的假信号问题。

    *如需更专业的优化版EA策略(包含机器学习过滤、多周期分析、5年以上完整回测报告),请查看我们的付费EA合集。订阅后可每周获取更新和独家交易工具。*