Summary: 专业的布林带波动率突破剥头皮EA。当价格触碰外轨并确认后开仓交易。具备移动止损、点差控制和每根K线仅交易一次的逻辑。




# 布林带剥头皮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. 设置参数并启用自动交易

编译说明



  • 适用于MT4 build 600及以上版本

  • 五位数报价平台的点差计算已使用`Point * 10`编码

  • 不需要外部DLL

  • 所有指标均为MT4内置标准指标


  • 优化建议



    1. 时间周期:M5或M15最适合此剥头皮策略
    2. 货币对:EURUSD、GBPUSD、USDJPY(低点差品种)
    3. 布林带偏差:趋势市场增至2.2-2.5,震荡市场减至1.8
    4. 移动止损:12-15点激活,5点步长
    5. 交易时间:避开重大新闻事件(可手动控制时间)

    风险提示



    剥头皮策略需要低点差环境。实盘交易前请充分进行模拟测试。本EA默认每根K线只开一笔交易以减少过度交易。

    参考来源



    基于John Bollinger开发的经典布林带方法。本EA源码由作者独立编写测试。