# 布林带均值回归EA - 完整MQL4源码
本文提供一个完整的布林带均值回归自动交易EA,配有ATR波动率确认过滤。当价格触及或突破布林带上轨时开空单,触及或突破下轨时开多单,利用价格回归中轨的均值回归特性获利。
策略逻辑
该策略基于均值回归原理运作——当价格显著偏离其移动平均线(布林带中轨)时,倾向于回归。布林带使用标准差来衡量这种偏离程度。EA包含ATR波动率过滤,避免在低波动环境下交易(此时均值回归可能无法提供足够的盈利空间)。附加功能包括保本止损管理、点差控制和交易时间过滤。
核心特性
完整MQL4代码
```mql4
//+------------------------------------------------------------------+
//| BB_MeanReversionEA.mq4 |
//| AI助手独立自主编译 |
//| |
//+------------------------------------------------------------------+
#property copyright "AI助手"
#property link ""
#property version "1.00"
#property strict
//--- 输入参数
input double LotSize = 0.1; // 固定手数
input int BandsPeriod = 20; // 布林带周期
input double BandsDeviation = 2.0; // 标准差倍数
input int ATRPeriod = 14; // ATR过滤周期
input double MinATR = 10; // 最小ATR值(点数)- 低波动过滤
input int StopLoss = 60; // 止损点数
input int TakeProfit = 120; // 止盈点数
input bool UseBreakEven = true; // 启用保本止损
input int BreakEvenPips = 25; // 触发保本所需盈利点数
input int MaxSpread = 30; // 最大允许点差(点数)
input bool UseTimeFilter = false; // 启用交易时间过滤
input int StartHour = 8; // 开始交易小时(服务器时间)
input int EndHour = 20; // 结束交易小时
input int MagicNumber = 202412; // EA魔术号
input bool CloseOpposite = true; // 反向信号时平仓反向单
//--- 全局变量
double upperBand = 0, lowerBand = 0, middleBand = 0;
double atrValue = 0;
int pointMultiplier = 10; // 5位报价平台乘数
//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
// 检测4位报价平台
if(Digits == 4 || Digits == 3)
pointMultiplier = 1;
else if(Digits == 5 || Digits == 2)
pointMultiplier = 10;
if(BandsPeriod < 2)
{
Print("错误: 布林带周期至少为2");
return(INIT_PARAMETERS_INCORRECT);
}
if(ATRPeriod < 2)
{
Print("错误: ATR周期至少为2");
return(INIT_PARAMETERS_INCORRECT);
}
Print("EA初始化成功,品种: ", Symbol());
Print("点数乘数: ", pointMultiplier);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| EA反初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("EA已移除,原因代码: ", reason);
}
//+------------------------------------------------------------------+
//| EA报价处理函数 |
//+------------------------------------------------------------------+
void OnTick()
{
// 检查时间过滤
if(!IsTradingTime())
return;
// 检查点差条件
if(!IsSpreadOK())
return;
// 获取布林带数值
upperBand = iBands(Symbol(), 0, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_UPPER, 0);
lowerBand = iBands(Symbol(), 0, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_LOWER, 0);
middleBand = iBands(Symbol(), 0, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_MAIN, 0);
// 获取ATR波动率值
atrValue = iATR(Symbol(), 0, ATRPeriod, 0);
// 验证数据有效性
if(upperBand == EMPTY_VALUE || lowerBand == EMPTY_VALUE || atrValue == EMPTY_VALUE)
return;
// 转换为点数进行比较
double atrPoints = atrValue / Point / pointMultiplier;
// 低波动过滤 - 跳过交易
if(atrPoints < MinATR)
return;
// 管理现有持仓的保本止损
ManageBreakEven();
// 信号检测
if(IsSellSignal())
{
if(CloseOpposite) CloseBuyPositions();
if(CountSellPositions() == 0)
OpenOrder(OP_SELL);
}
else if(IsBuySignal())
{
if(CloseOpposite) CloseSellPositions();
if(CountBuyPositions() == 0)
OpenOrder(OP_BUY);
}
}
//+------------------------------------------------------------------+
//| 检查买入信号 - 价格触及或跌破下轨 |
//+------------------------------------------------------------------+
bool IsBuySignal()
{
// 使用最低价确认,避免重绘
double lowPrice = Low[0];
return (lowPrice <= lowerBand);
}
//+------------------------------------------------------------------+
//| 检查卖出信号 - 价格触及或突破上轨 |
//+------------------------------------------------------------------+
bool IsSellSignal()
{
// 使用最高价确认,避免重绘
double highPrice = High[0];
return (highPrice >= upperBand);
}
//+------------------------------------------------------------------+
//| 开仓函数 |
//+------------------------------------------------------------------+
void OpenOrder(int cmd)
{
double price = (cmd == OP_BUY) ? Ask : Bid;
double sl = 0, tp = 0;
double slPoints = StopLoss * pointMultiplier * Point;
double tpPoints = TakeProfit * pointMultiplier * Point;
if(StopLoss > 0)
{
if(cmd == OP_BUY)
sl = price - slPoints;
else
sl = price + slPoints;
}
if(TakeProfit > 0)
{
if(cmd == OP_BUY)
tp = price + tpPoints;
else
tp = price - tpPoints;
}
// 发送订单前检查保证金
double marginRequired = MarketInfo(Symbol(), MODE_MARGINREQUIRED);
double freeMargin = AccountFreeMargin();
if(freeMargin < marginRequired * LotSize * 1.5)
{
Print("保证金不足。需要: ", marginRequired * LotSize, " 可用: ", freeMargin);
return;
}
int ticket = OrderSend(Symbol(), cmd, LotSize, price, 3, sl, tp, "BB MeanRev", MagicNumber, 0, clrNONE);
if(ticket < 0)
Print("开仓失败,错误码: ", GetLastError());
else
Print("开仓成功,订单号: ", ticket, " 类型: ", (cmd == OP_BUY ? "买入" : "卖出"));
}
//+------------------------------------------------------------------+
//| 检查当前是否在允许交易时间内 |
//+------------------------------------------------------------------+
bool IsTradingTime()
{
if(!UseTimeFilter) return true;
datetime now = TimeCurrent();
MqlDateTime dt;
TimeToStruct(now, dt);
int hour = dt.hour;
if(hour >= StartHour && hour < EndHour)
return true;
return false;
}
//+------------------------------------------------------------------+
//| 检查点差是否在限制范围内 |
//+------------------------------------------------------------------+
bool IsSpreadOK()
{
if(MaxSpread == 0) return true;
int currentSpread = (int)((Ask - Bid) / Point / pointMultiplier);
return (currentSpread <= MaxSpread);
}
//+------------------------------------------------------------------+
//| 保本止损管理 - 盈利达标后将止损移至开仓价 |
//+------------------------------------------------------------------+
void ManageBreakEven()
{
if(!UseBreakEven) return;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
double breakEvenTrigger = BreakEvenPips * pointMultiplier * Point;
double currentSL = OrderStopLoss();
if(OrderType() == OP_BUY)
{
double profitPoints = (Bid - OrderOpenPrice()) / Point / pointMultiplier;
if(profitPoints >= BreakEvenPips && (currentSL == 0 || currentSL < OrderOpenPrice()))
{
bool modified = OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice(), OrderTakeProfit(), 0, clrNONE);
if(modified)
Print("保本止损已设置 - 买单 #", OrderTicket());
}
}
else if(OrderType() == OP_SELL)
{
double profitPoints = (OrderOpenPrice() - Ask) / Point / pointMultiplier;
if(profitPoints >= BreakEvenPips && (currentSL == 0 || currentSL > OrderOpenPrice()))
{
bool modified = OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice(), OrderTakeProfit(), 0, clrNONE);
if(modified)
Print("保本止损已设置 - 卖单 #", OrderTicket());
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| 统计买单数量 |
//+------------------------------------------------------------------+
int CountBuyPositions()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_BUY)
count++;
}
}
return count;
}
//+------------------------------------------------------------------+
//| 统计卖单数量 |
//+------------------------------------------------------------------+
int CountSellPositions()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_SELL)
count++;
}
}
return count;
}
//+------------------------------------------------------------------+
//| 平仓所有买单 |
//+------------------------------------------------------------------+
void CloseBuyPositions()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_BUY)
{
if(OrderClose(OrderTicket(), OrderLots(), Bid, 3, clrNONE))
Print("已平仓买单 #", OrderTicket());
else
Print("平仓买单失败 #", OrderTicket(), " 错误码: ", GetLastError());
}
}
}
}
//+------------------------------------------------------------------+
//| 平仓所有卖单 |
//+------------------------------------------------------------------+
void CloseSellPositions()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_SELL)
{
if(OrderClose(OrderTicket(), OrderLots(), Ask, 3, clrNONE))
Print("已平仓卖单 #", OrderTicket());
else
Print("平仓卖单失败 #", OrderTicket(), " 错误码: ", GetLastError());
}
}
}
}
//+------------------------------------------------------------------+
```
参数详解
| 参数 | 说明 | 推荐值 |
|------|------|--------|
| 固定手数 | 每笔交易手数 | 0.01 - 1.0 |
| 布林带周期 | 布林带计算周期 | 20(标准) |
| 标准差倍数 | 标准差倍数 | 2.0 - 2.5 |
| ATR周期 | ATR过滤计算周期 | 14 |
| 最小ATR | 最小ATR点数 - 过滤低波动 | 8-15 |
| 止损点数 | 止损距离 | 50-80 |
| 止盈点数 | 止盈距离 | 100-160 |
| 启用保本止损 | 是否启用保本功能 | true |
| 保本触发点数 | 触发保本所需盈利点数 | 20-30 |
| 最大点差 | 允许的最大点差 | 20-50 |
| 启用时间过滤 | 是否限制交易时段 | false |
| 开始小时 | 开始交易时间 | 8 |
| 结束小时 | 停止交易时间 | 20 |
| 魔术号 | EA唯一标识码 | 任意不重复 |
| 反向平仓 | 新信号是否平掉反向单 | true |
安装步骤
1. 在MT4中打开MetaEditor(按F4)
2. 创建新的EA(文件 > 新建 > 智能交易系统)
3. 删除所有默认代码,粘贴上方完整代码
4. 按编译按钮(F7)- 检查底部错误选项卡应为0个错误
5. 关闭MetaEditor,从导航器将EA拖拽到图表上
6. 在输入参数选项卡中调整参数
7. 启用自动交易(绿色三角形按钮)
编译与修改技巧
常见问题处理:
参数优化建议:
参考来源
本文EA源码为自主独立编译,基于均值回归策略原理和标准布林带方法论设计。
*如需包含多周期确认、机器学习过滤和完整回测报告的高级EA策略,请查看我们的付费EA合集。订阅后可每周获取更新和专业交易工具。*