# 布林带+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. 启用自动交易按钮
编译与修改技巧
常见编译问题:
策略参数调优:
参考来源
本文EA源码为自主编译。策略概念结合了布林带突破逻辑与ATR波动率过滤,是专业算法交易中常用的多指标确认方法。通过双重条件过滤,有效减少了单一指标策略常见的假信号问题。
*如需更专业的优化版EA策略(包含机器学习过滤、多周期分析、5年以上完整回测报告),请查看我们的付费EA合集。订阅后可每周获取更新和独家交易工具。*