| 
 | 
 
#property copyright "Copyright 2024, MetaQuotes Ltd." 
#property link      "https://www.mql5.com" 
#property version   "1.00" 
#property description "Bollinger Bands Squeeze with SAR Filter" 
 
#include <Trade/Trade.mqh> 
 
//--- 输入参数 
input int      BandsPeriod = 20;           // 布林带周期 
input double   BandsDeviations = 2.0;      // 标准差倍数 
input double   WidthMultiplier = 1.1;      // 宽度乘数 
input int      LookbackPeriod = 20;        // 回溯周期 
input double   SARStep = 0.02;             // SAR步长 
input double   SARMaximum = 0.2;           // SAR最大值 
input double   LotSize = 0.1;              // 交易手数 
input ulong    MagicNumber = 123456;       // 魔术码 
input int      Slippage = 3;               // 滑点 
input int      TakeProfitPoints = 6000;    // 止盈点数 
input int      StopLossPoints = 5000;      // 止损点数 
 
//--- 全局变量 
int handleBands, handleSAR; 
CTrade trade; 
 
//+------------------------------------------------------------------+ 
//| Expert initialization function                                   | 
//+------------------------------------------------------------------+ 
int OnInit() 
{ 
   // 初始化指标句柄 
   handleBands = iBands(_Symbol, _Period, BandsPeriod, 0, BandsDeviations, PRICE_CLOSE); 
   handleSAR = iSAR(_Symbol, _Period, SARStep, SARMaximum); 
 
   if(handleBands == INVALID_HANDLE || handleSAR == INVALID_HANDLE) { 
      Print("指标初始化失败!"); 
      return(INIT_FAILED); 
   } 
 
   trade.SetExpertMagicNumber(MagicNumber); 
   trade.SetDeviationInPoints(Slippage); 
   return(INIT_SUCCEEDED); 
} 
 
//+------------------------------------------------------------------+ 
//| 持仓检查函数                                                     | 
//+------------------------------------------------------------------+ 
bool HasPosition() 
{ 
   for(int i=PositionsTotal()-1; i>=0; i--) 
      if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber) 
         return true; 
   return false; 
} 
 
//+------------------------------------------------------------------+ 
//| Expert tick function                                             | 
//+------------------------------------------------------------------+ 
void OnTick() 
{ 
   static datetime prevBarTime = 0; 
   datetime currentTime = iTime(_Symbol, _Period, 0); 
 
   if(currentTime == prevBarTime) return; 
   prevBarTime = currentTime; 
 
   // 获取布林带数据 
   double upper[], middle[], lower[]; 
   ArraySetAsSeries(upper, true); 
   ArraySetAsSeries(middle, true); 
   ArraySetAsSeries(lower, true); 
 
   if(CopyBuffer(handleBands, 1, 0, LookbackPeriod+1, upper) <= 0 || 
      CopyBuffer(handleBands, 0, 0, LookbackPeriod+1, middle) <= 0 || 
      CopyBuffer(handleBands, 2, 0, LookbackPeriod+1, lower) <= 0) 
   { 
      Print("布林带数据获取失败!"); 
      return; 
   } 
 
   // 获取SAR数据 
   double sarArray[]; 
   ArraySetAsSeries(sarArray, true); 
   if(CopyBuffer(handleSAR, 0, 0, 2, sarArray) <= 0) { 
      Print("SAR数据获取失败!"); 
      return; 
   } 
   double currentSAR = sarArray[0];  // 当前SAR值 
   double lastSAR = sarArray[1];     // 上一根K线SAR值 
 
   // 获取价格数据 
   double lastClose = iClose(_Symbol, _Period, 1); 
   double currentClose = iClose(_Symbol, _Period, 0); 
 
   // 计算当前布林带宽度 
   double currentWidth = upper[0] - lower[0]; 
 
   // 寻找过去N个宽度中的最小值 
   double minWidth = DBL_MAX; 
   for(int i=1; i<=LookbackPeriod; i++) { 
      double w = upper - lower; 
      if(w < minWidth) minWidth = w; 
   } 
 
   // SAR过滤条件 
   bool sarBuyCondition = currentClose > currentSAR;  // 价格在SAR之上 
   bool sarSellCondition = currentClose < currentSAR; // 价格在SAR之下 
 
   // 交易信号检测 
   if(!HasPosition() && currentWidth < minWidth * WidthMultiplier) 
   { 
      // 多单入场条件 
      if(lastClose > upper[1] && sarBuyCondition) 
      { 
         double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); 
         double sl = NormalizeDouble(ask - StopLossPoints * _Point, _Digits); 
         double tp = NormalizeDouble(ask + TakeProfitPoints * _Point, _Digits); 
         trade.Buy(LotSize, _Symbol, ask, sl, tp, "BB SAR Buy"); 
      } 
      // 空单入场条件 
      else if(lastClose < lower[1] && sarSellCondition) 
      { 
         double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); 
         double sl = NormalizeDouble(bid + StopLossPoints * _Point, _Digits); 
         double tp = NormalizeDouble(bid - TakeProfitPoints * _Point, _Digits); 
         trade.Sell(LotSize, _Symbol, bid, sl, tp, "BB SAR Sell"); 
      } 
   } 
} 
 
当布林带出现挤压形态时才会触发交易 多头交易需要满足价格在SAR指标上方 空头交易需要满足价格在SAR指标下方 使用当前K线的SAR值进行实时判断 保持原有的止盈止损和风险管理逻辑  
  
 |   
 
 
 
 |