# 布林带剥头皮EA - 完整MQL4源码
本EA使用布林带实现基于波动率的剥头皮策略。当价格触及下轨时,EA开多单预期价格反弹回中轨;当价格触及上轨时,开空单预期价格回落。
策略逻辑
EA在每根新K线上监控价格相对于布林带的位置。为避免高波动期间的假信号,EA包含确认机制并且每根K线只交易一次。移动止损在交易有利方向移动时保护利润。
完整MQL4代码
```mql4
//+------------------------------------------------------------------+
//| BBandScalperEA.mq4 |
//| |
//| |
//+------------------------------------------------------------------+
#property copyright "外汇策略工坊"
#property link ""
#property version "1.00"
#property strict
//--- 输入参数
input double Lots = 0.05; // 固定手数
input int BandsPeriod = 20; // 布林带周期
input double BandsDeviation = 2.0; // 标准差倍数
input int BandsShift = 0; // 布林带偏移
input int StopLoss = 25; // 止损点数
input int TakeProfit = 40; // 止盈点数
input int TrailingStart = 15; // 移动止损激活点数
input int TrailingStep = 5; // 移动止损步长点数
input int MaxSpread = 25; // 最大允许点差
input int Slippage = 2; // 滑点点数
input int MagicNumber = 202412; // EA魔术号
input bool CloseOpposite = true; // 新信号时平掉反向仓位
//--- 全局变量
datetime lastBarTime = 0;
double upperBand = 0, lowerBand = 0, middleBand = 0;
//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
if(BandsPeriod < 2)
{
Print("错误: 布林带周期至少为2");
return(INIT_PARAMETERS_INCORRECT);
}
Print("布林带剥头皮EA初始化成功。周期: ", BandsPeriod, " 偏差: ", BandsDeviation);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| EA反初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("EA已移除。原因代码: ", reason);
}
//+------------------------------------------------------------------+
//| EA报价处理函数 |
//+------------------------------------------------------------------+
void OnTick()
{
// 检查自动交易是否开启
if(!IsTradeAllowed())
return;
// 检查点差条件
int currentSpread = (int)MarketInfo(Symbol(), MODE_SPREAD);
if(currentSpread > MaxSpread * 10)
return;
// 检查新K线
bool isNewBar = CheckNewBar();
// 计算布林带
upperBand = iBands(Symbol(), 0, BandsPeriod, BandsDeviation, BandsShift, PRICE_CLOSE, MODE_UPPER, 0);
lowerBand = iBands(Symbol(), 0, BandsPeriod, BandsDeviation, BandsShift, PRICE_CLOSE, MODE_LOWER, 0);
middleBand = iBands(Symbol(), 0, BandsPeriod, BandsDeviation, BandsShift, PRICE_CLOSE, MODE_MAIN, 0);
double bid = Bid;
double ask = Ask;
//--- 买入信号:价格触及或跌破下轨
if(CloseOpposite)
{
if(CountPositions() > 0)
{
ManageTrailingStop();
CheckAndCloseOpposite(bid, ask);
}
}
else
{
if(CountPositions() > 0)
{
ManageTrailingStop();
return;
}
}
// 入场条件 - 仅在新K线上执行以减少噪音
if(isNewBar)
{
// 买入条件:收盘价接近或低于下轨
double close1 = iClose(Symbol(), 0, 1);
if(close1 <= lowerBand + (upperBand - lowerBand) * 0.1)
{
if(CountPositions() == 0)
{
OpenBuy();
return;
}
}
// 卖出条件:收盘价接近或高于上轨
if(close1 >= upperBand - (upperBand - lowerBand) * 0.1)
{
if(CountPositions() == 0)
{
OpenSell();
return;
}
}
}
// 管理现有持仓的移动止损
ManageTrailingStop();
}
//+------------------------------------------------------------------+
//| 检查是否形成新K线 |
//+------------------------------------------------------------------+
bool CheckNewBar()
{
datetime currentBarTime = Time[0];
if(currentBarTime != lastBarTime)
{
lastBarTime = currentBarTime;
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 开买入订单 |
//+------------------------------------------------------------------+
void OpenBuy()
{
double sl = 0, tp = 0;
double price = Ask;
if(StopLoss > 0)
sl = price - StopLoss * Point * 10;
if(TakeProfit > 0)
tp = price + TakeProfit * Point * 10;
int ticket = OrderSend(Symbol(), OP_BUY, Lots, price, Slippage, sl, tp, "BB剥头皮买入", MagicNumber, 0, clrDodgerBlue);
if(ticket > 0)
Print("买入订单已开。票据号: ", ticket, " 价格: ", price);
else
Print("买入订单失败。错误: ", GetLastError());
}
//+------------------------------------------------------------------+
//| 开卖出订单 |
//+------------------------------------------------------------------+
void OpenSell()
{
double sl = 0, tp = 0;
double price = Bid;
if(StopLoss > 0)
sl = price + StopLoss * Point * 10;
if(TakeProfit > 0)
tp = price - TakeProfit * Point * 10;
int ticket = OrderSend(Symbol(), OP_SELL, Lots, price, Slippage, sl, tp, "BB剥头皮卖出", MagicNumber, 0, clrCrimson);
if(ticket > 0)
Print("卖出订单已开。票据号: ", ticket, " 价格: ", price);
else
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 newStopLoss = 0;
if(OrderType() == OP_BUY)
{
double profitPips = (Bid - OrderOpenPrice()) / (Point * 10);
if(profitPips >= TrailingStart)
{
newStopLoss = Bid - TrailingStep * Point * 10;
if(newStopLoss > OrderStopLoss() || OrderStopLoss() == 0)
{
if(OrderModify(OrderTicket(), OrderOpenPrice(), newStopLoss, OrderTakeProfit(), 0, clrNONE))
Print("买入移动止损已更新至: ", newStopLoss);
}
}
}
else if(OrderType() == OP_SELL)
{
double profitPips = (OrderOpenPrice() - Ask) / (Point * 10);
if(profitPips >= TrailingStart)
{
newStopLoss = Ask + TrailingStep * Point * 10;
if(newStopLoss < OrderStopLoss() || OrderStopLoss() == 0)
{
if(OrderModify(OrderTicket(), OrderOpenPrice(), newStopLoss, OrderTakeProfit(), 0, clrNONE))
Print("卖出移动止损已更新至: ", newStopLoss);
}
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| 信号反转时平掉反向仓位 |
//+------------------------------------------------------------------+
void CheckAndCloseOpposite(double bid, double ask)
{
if(!CloseOpposite) return;
bool hasBuy = false, hasSell = false;
int buyTicket = -1, sellTicket = -1;
double buyLots = 0, sellLots = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUY)
{
hasBuy = true;
buyTicket = OrderTicket();
buyLots = OrderLots();
}
else if(OrderType() == OP_SELL)
{
hasSell = true;
sellTicket = OrderTicket();
sellLots = OrderLots();
}
}
}
}
// 检查当前价格是否指示反转
double close1 = iClose(Symbol(), 0, 1);
// 如果持有多单但价格现在高于上轨,平多单
if(hasBuy && close1 >= upperBand)
{
if(OrderSelect(buyTicket, SELECT_BY_TICKET))
{
OrderClose(buyTicket, buyLots, bid, Slippage, clrNONE);
Print("因反转信号关闭买入订单");
}
}
// 如果持有空单但价格现在低于下轨,平空单
if(hasSell && close1 <= lowerBand)
{
if(OrderSelect(sellTicket, SELECT_BY_TICKET))
{
OrderClose(sellTicket, sellLots, ask, Slippage, clrNONE);
Print("因反转信号关闭卖出订单");
}
}
}
//+------------------------------------------------------------------+
//| 统计当前魔术号的持仓数量 |
//+------------------------------------------------------------------+
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;
}
//+------------------------------------------------------------------+
```
参数详解
| 参数 | 说明 | 推荐值 |
|------|------|--------|
| 固定手数 | 固定交易手数 | 0.01-0.10 |
| 布林带周期 | 布林带计算周期 | 20 |
| 标准差倍数 | 标准差乘数 | 2.0 |
| 布林带偏移 | 水平偏移 | 0 |
| 止损点数 | 止损距离 | 20-30 |
| 止盈点数 | 止盈距离 | 35-50 |
| 移动止损激活 | 多少点利润后激活移动止损 | 15 |
| 移动止损步长 | 移动止损距离 | 5-8 |
| 最大点差 | 允许的最大点差 | 20-30 |
| 滑点 | 允许滑点 | 2-3 |
| 魔术号 | EA唯一标识 | 任意数字 |
| 反转平仓 | 信号反转时平掉反向仓位 | true |
安装指南
1. 打开MetaEditor(MT4中按F4)
2. 文件 > 新建 > 智能交易系统 > 下一步
3. 命名EA(如"BBandScalperEA")
4. 点击完成
5. 删除所有默认代码
6. 粘贴上方完整代码
7. 编译(F7或点击编译按钮)
8. 在"智能交易系统"选项卡检查"0个错误,0个警告"
9. 将EA从导航器拖拽到图表上
10. 设置参数并启用自动交易
编译说明
优化建议
1. 时间周期:M5或M15最适合此剥头皮策略
2. 货币对:EURUSD、GBPUSD、USDJPY(低点差品种)
3. 布林带偏差:趋势市场增至2.2-2.5,震荡市场减至1.8
4. 移动止损:12-15点激活,5点步长
5. 交易时间:避开重大新闻事件(可手动控制时间)
风险提示
剥头皮策略需要低点差环境。实盘交易前请充分进行模拟测试。本EA默认每根K线只开一笔交易以减少过度交易。
参考来源
基于John Bollinger开发的经典布林带方法。本EA源码由作者独立编写测试。