Summary: 专业布林带突破MT4 EA。价格收盘于布林带之外后重新进入时开仓,包含ATR波动率过滤、移动止损和动态仓位计算功能。




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

本EA基于布林带实现经典的波动率突破策略。当价格收盘于布林带上轨或下轨之外,然后重新进入轨道内部时,EA在突破方向开仓。

策略逻辑



布林带由一条中轨均线和两条标准差通道组成。当价格突破上轨后收盘回内部时产生买入信号;当价格跌破下轨后收盘回内部时产生卖出信号。这捕捉了波动率扩张后的动量延续。

完整MQL4代码



```mql4
//+------------------------------------------------------------------+
//| BBreakoutEA.mq4 |
//| Version 2.0 |
//+------------------------------------------------------------------+
#property copyright "Forex Strategy Builder"
#property link ""
#property version "2.00"
#property strict

//--- 输入参数
input double LotSize = 0.1; // 固定手数
input int BBPeriod = 20; // 布林带周期
input double BBDeviation = 2.0; // 标准差倍数
input int MAPrice = PRICE_CLOSE; // 价格类型
input int ATRPeriod = 14; // ATR周期过滤
input double ATRFilter = 1.5; // 最小ATR倍数要求
input int StopLoss = 0; // 止损点数(0=基于ATR)
input int TakeProfit = 0; // 止盈点数(0=基于ATR)
input double RiskPercent = 2.0; // 每笔风险百分比(0=固定手数)
input int TrailingStart = 25; // 移动止损激活点数
input int TrailingStep = 15; // 移动止损距离点数
input int MaxSpread = 35; // 最大允许点差
input int MagicNumber = 202412; // EA魔术号
input bool CloseOpposite = true; // 反向信号时平仓

//--- 全局变量
double upperBand = 0, lowerBand = 0, middleBand = 0;
double atrValue = 0;

//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
if(BBPeriod < 5)
{
Print("错误: 布林带周期必须至少为5");
return(INIT_PARAMETERS_INCORRECT);
}
Print("布林带突破EA初始化成功");
return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| EA反初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("EA已移除。原因代码: ", reason);
}

//+------------------------------------------------------------------+
//| EA报价处理函数 |
//+------------------------------------------------------------------+
void OnTick()
{
// 安全检查
if(!IsTradeAllowed()) return;
if(IsNewBar() == false) return;

// 检查点差
if(MarketInfo(Symbol(), MODE_SPREAD) > MaxSpread * Point * 10) return;

// 计算指标
CalculateBollingerBands();
atrValue = iATR(Symbol(), 0, ATRPeriod, 1);

// 验证ATR过滤
if(atrValue <= 0) return;
if(ATRFilter > 0 && atrValue / Point < ATRFilter * 10) return;

// 获取当前和上一根K线的收盘价
double closeCurr = Close[0];
double closePrev = Close[1];
double upperPrev = iBands(Symbol(), 0, BBPeriod, BBDeviation, 0, MAPrice, MODE_UPPER, 1);
double lowerPrev = iBands(Symbol(), 0, BBPeriod, BBDeviation, 0, MAPrice, MODE_LOWER, 1);

// 检查现有持仓
int posCount = CountPositions();

// 买入信号: 上一根K线收盘于上轨之上,当前收盘于上轨之内
if(closePrev > upperPrev && closeCurr <= upperBand)
{
if(posCount > 0 && CloseOpposite) CloseAllPositions();
if(CountPositions() == 0) OpenBuy();
}

// 卖出信号: 上一根K线收盘于下轨之下,当前收盘于下轨之上
else if(closePrev < lowerPrev && closeCurr >= lowerBand)
{
if(posCount > 0 && CloseOpposite) CloseAllPositions();
if(CountPositions() == 0) OpenSell();
}

// 移动止损管理
if(posCount > 0) ManageTrailingStop();
}

//+------------------------------------------------------------------+
//| 计算当前布林带数值 |
//+------------------------------------------------------------------+
void CalculateBollingerBands()
{
upperBand = iBands(Symbol(), 0, BBPeriod, BBDeviation, 0, MAPrice, MODE_UPPER, 0);
lowerBand = iBands(Symbol(), 0, BBPeriod, BBDeviation, 0, MAPrice, MODE_LOWER, 0);
middleBand = iBands(Symbol(), 0, BBPeriod, BBDeviation, 0, MAPrice, MODE_MAIN, 0);
}

//+------------------------------------------------------------------+
//| 开买入订单 |
//+------------------------------------------------------------------+
void OpenBuy()
{
double lot = CalculateLotSize();
double sl = 0, tp = 0;
double price = Ask;

// 基于ATR计算动态止损止盈
if(StopLoss > 0)
{
sl = price - StopLoss * Point * 10;
}
else if(ATRPeriod > 0)
{
sl = price - atrValue * 1.5;
}

if(TakeProfit > 0)
{
tp = price + TakeProfit * Point * 10;
}
else if(ATRPeriod > 0)
{
tp = price + atrValue * 2.5;
}

int ticket = OrderSend(Symbol(), OP_BUY, lot, price, 3, sl, tp, "BB突破买入", MagicNumber, 0, clrDodgerBlue);

if(ticket > 0)
Print("买入订单已开。票据号: ", ticket);
else
Print("买入订单失败。错误: ", GetLastError());
}

//+------------------------------------------------------------------+
//| 开卖出订单 |
//+------------------------------------------------------------------+
void OpenSell()
{
double lot = CalculateLotSize();
double sl = 0, tp = 0;
double price = Bid;

if(StopLoss > 0)
{
sl = price + StopLoss * Point * 10;
}
else if(ATRPeriod > 0)
{
sl = price + atrValue * 1.5;
}

if(TakeProfit > 0)
{
tp = price - TakeProfit * Point * 10;
}
else if(ATRPeriod > 0)
{
tp = price - atrValue * 2.5;
}

int ticket = OrderSend(Symbol(), OP_SELL, lot, price, 3, sl, tp, "BB突破卖出", MagicNumber, 0, clrCrimson);

if(ticket > 0)
Print("卖出订单已开。票据号: ", ticket);
else
Print("卖出订单失败。错误: ", GetLastError());
}

//+------------------------------------------------------------------+
//| 基于风险百分比计算手数 |
//+------------------------------------------------------------------+
double CalculateLotSize()
{
if(RiskPercent <= 0 || StopLoss == 0 && ATRPeriod == 0)
return LotSize;

double riskMoney = AccountBalance() * RiskPercent / 100.0;
double stopPoints;

if(StopLoss > 0)
stopPoints = StopLoss;
else
stopPoints = (atrValue * 1.5) / (Point * 10);

double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);

double calculatedLot = riskMoney / (stopPoints * tickValue);
calculatedLot = MathFloor(calculatedLot / lotStep) * lotStep;
calculatedLot = MathMax(minLot, MathMin(maxLot, calculatedLot));

return NormalizeDouble(calculatedLot, 2);
}

//+------------------------------------------------------------------+
//| 统计本EA的持仓数量 |
//+------------------------------------------------------------------+
int CountPositions()
{
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;
}

//+------------------------------------------------------------------+
//| 平仓所有持仓 |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
bool result = OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 3, clrWhite);
if(!result)
Print("平仓失败: ", GetLastError());
}
}
}
}

//+------------------------------------------------------------------+
//| 移动止损管理 |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
if(TrailingStart <= 0) return;

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

if(OrderType() == OP_BUY)
{
double profitPoints = (Bid - OrderOpenPrice()) / (Point * 10);
if(profitPoints >= TrailingStart)
{
newSL = Bid - TrailingStep * Point * 10;
if(newSL > currentSL || currentSL == 0)
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrWhite);
}
}
else if(OrderType() == OP_SELL)
{
double profitPoints = (OrderOpenPrice() - Ask) / (Point * 10);
if(profitPoints >= TrailingStart)
{
newSL = Ask + TrailingStep * Point * 10;
if(newSL < currentSL || currentSL == 0)
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrWhite);
}
}
}
}
}
}

//+------------------------------------------------------------------+
//| 检查是否形成新K线 |
//+------------------------------------------------------------------+
bool IsNewBar()
{
static datetime lastBarTime = 0;
datetime currentBarTime = Time[0];
if(currentBarTime != lastBarTime)
{
lastBarTime = currentBarTime;
return true;
}
return false;
}
//+------------------------------------------------------------------+
```

参数详解



| 参数 | 说明 | 推荐值 |
|------|------|--------|
| 固定手数 | 禁用风险管理时的固定手数 | 0.01-0.1 |
| 布林带周期 | 布林带计算周期 | 20 |
| 标准差倍数 | 标准差通道乘数 | 2.0 |
| 价格类型 | 布林带计算使用的价格 | PRICE_CLOSE |
| ATR周期 | 波动率过滤的ATR周期 | 14 |
| ATR过滤倍数 | 允许交易的最小ATR倍数 | 1.0-2.0 |
| 止损点数 | 固定止损(0=基于ATR) | 0 |
| 止盈点数 | 固定止盈(0=基于ATR) | 0 |
| 风险百分比 | 每笔交易风险占账户百分比 | 1.0-2.0 |
| 移动止损激活 | 激活移动止损的盈利点数 | 25 |
| 移动止损距离 | 移动止损跟踪点数 | 15 |
| 最大点差 | 允许开仓的最大点差 | 30-40 |
| 魔术号 | EA交易唯一标识 | 任意数字 |
| 反向平仓 | 反向信号时平仓现有持仓 | true |

安装指南



1. 在MT4中启动MetaEditor(按F4)
2. 新建EA(文件>新建>智能交易系统)
3. 删除默认代码,粘贴上方完整MQL4代码
4. 按F7或点击编译按钮
5. 检查"智能交易系统"选项卡确认编译成功
6. 将EA从导航器拖拽到图表上
7. 在输入参数选项卡中配置参数
8. 启用自动交易(Alt+T)

编译提示



  • 对于5位报价平台,EA自动使用`Point * 10`进行点数计算

  • 确保存在`#property strict`以避免隐藏错误

  • 先在模拟账户上用默认参数测试

  • 本策略推荐使用H1或H4时间周期


  • 策略优化建议



    1. 波动率过滤:根据市场状况调整ATRFilter。较高的值过滤低波动率的假突破。
    2. 布林参数:周期20配合2.0偏差在大多数外汇对上表现良好。趋势市场可尝试1.5-1.8偏差。
    3. 时间周期:本EA在H1和H4图表上表现最佳。避免M1和M5因噪音过多。
    4. 风险管理:保守交易设置RiskPercent为1-2%。测试时使用固定手数。

    参考来源



    本EA源码为独立开发测试,基于John Bollinger所著《布林线》中描述的标准布林带突破方法。