bzjc 发表于 2026-6-6 13:20:25

波段突破策略,计算近期高点和地点做突破


黄金1H。100刀起步

//+------------------------------------------------------------------+
//|                                    波段突破策略.mq5                |
//| v6.1 Pivot局部高低点突破 + 动态刷新压力支撑位                     |
//+------------------------------------------------------------------+
#property copyright "Swing Breakout Strategy"
#property version   "6.10"

#include <Trade\Trade.mqh>
CTrade trade;

//+------------------------------------------------------------------+
//| 参数                                                            |
//+------------------------------------------------------------------+
input int      PivotLeft      = 5;      // 左侧K线数(找波段高/低点)
input int      PivotRight       = 5;      // 右侧K线数
input double   SL_Points      = 150;    // 止损点数
input double   TP_Step          = 50;   // 止盈步进(点)
input double   RiskPercent      = 1.0;    // 每笔风险%
input int      MagicNumber      = 8888;
input int      Slippage         = 3;
input string   TradeComment   = "Swing";
input bool   EnableLong       = true;
input bool   EnableShort      = true;
input int      ConfirmBars      = 1;      // 突破确认K线数

//+------------------------------------------------------------------+
double g_point;
datetime g_lastBar = 0;
double g_entryPrice = 0;

double g_swingHigh = 0;
double g_swingLow = 0;
bool g_levelsValid = false;

datetime g_lastSignalBar = 0;
int g_tpSteps = 0;
int g_cooldownBars = 0;
int g_breakoutConfirm = 0;

//+------------------------------------------------------------------+
//| 初始化                                                             |
//+------------------------------------------------------------------+
int OnInit()
{
   trade.SetExpertMagicNumber(MagicNumber);
   trade.SetDeviationInPoints(Slippage);
   trade.SetTypeFillingBySymbol(_Symbol);
   
   g_point = _Point;
   if(_Digits == 3 || _Digits == 5) g_point *= 10;
   
   g_cooldownBars = 0;
   g_levelsValid = false;
   g_breakoutConfirm = 0;
   
   Print("波段突破 v6.0 | Pivot(", PivotLeft, ",", PivotRight, ") 止损:", SL_Points, "点步进:", TP_Step, "点");
   return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if(ObjectFind(0, "Swing_High") >= 0) ObjectDelete(0, "Swing_High");
   if(ObjectFind(0, "Swing_Low") >= 0) ObjectDelete(0, "Swing_Low");
   Comment("");
}

//+------------------------------------------------------------------+
//| 找最近的波段高点(pivot high: 比左右都高)                         |
//+------------------------------------------------------------------+
double FindRecentSwingHigh()
{
   double high[];
   ArraySetAsSeries(high, true);
   
   // 扩大搜索范围到100根K线
   int searchRange = 100;
   int leftBars = PivotLeft;
   int rightBars = PivotRight;
   int needBars = searchRange + leftBars + rightBars;
   
   if(CopyHigh(_Symbol, PERIOD_CURRENT, 1, needBars, high) < needBars) return 0;
   
   // 从近到远搜索,找到第一个有效的pivot high
   for(int start = rightBars; start < needBars - leftBars; start++)
   {
      bool isPivot = true;
      for(int l = 1; l <= leftBars; l++)
      {
         if(high >= high) { isPivot = false; break; }
      }
      if(!isPivot) continue;
      for(int r = 1; r <= rightBars; r++)
      {
         if(high >= high) { isPivot = false; break; }
      }
      if(isPivot)
      {
         Print("找到波段高点 #", start, " 价格:", high);
         return high;
      }
   }
   
   // 找不到pivot就用最近N根K线的最高价
   double maxH = 0;
   int lookback = MathMin(50, needBars);
   for(int i = 1; i < lookback; i++)
   {
      if(high > maxH) maxH = high;
   }
   return maxH > 0 ? maxH : high;
}

//+------------------------------------------------------------------+
//| 找最近的波段低点(pivot low: 比左右都低)                        |
//+------------------------------------------------------------------+
double FindRecentSwingLow()
{
   double low[];
   ArraySetAsSeries(low, true);
   
   int searchRange = 100;
   int leftBars = PivotLeft;
   int rightBars = PivotRight;
   int needBars = searchRange + leftBars + rightBars;
   
   if(CopyLow(_Symbol, PERIOD_CURRENT, 1, needBars, low) < needBars) return 0;
   
   for(int start = rightBars; start < needBars - leftBars; start++)
   {
      bool isPivot = true;
      for(int l = 1; l <= leftBars; l++)
      {
         if(low <= low) { isPivot = false; break; }
      }
      if(!isPivot) continue;
      for(int r = 1; r <= rightBars; r++)
      {
         if(low <= low) { isPivot = false; break; }
      }
      if(isPivot)
      {
         Print("找到波段低点 #", start, " 价格:", low);
         return low;
      }
   }
   
   // 找不到pivot就用最近N根K线的最低价
   double minL = DBL_MAX;
   int lookback = MathMin(50, needBars);
   for(int i = 1; i < lookback; i++)
   {
      if(low < minL) minL = low;
   }
   return minL < DBL_MAX ? minL : low;
}

//+------------------------------------------------------------------+
//| 更新压力/支撑位(持仓时也要定期更新)                              |
//+------------------------------------------------------------------+
bool CalcLevels()
{
   double sh = FindRecentSwingHigh();
   double sl = FindRecentSwingLow();
   
   if(sh > 0 && sl > 0 && sh > sl)
   {
      g_swingHigh = sh;
      g_swingLow = sl;
      g_levelsValid = true;
      return true;
   }
   return false;
}

//+------------------------------------------------------------------+
//| 检查是否需要刷新压力/支撑位                                        |
//+------------------------------------------------------------------+
void CheckRefreshLevels()
{
   if(!g_levelsValid)
   {
      CalcLevels();
      return;
   }
   
   // 价格已经远离当前区间,需要重新计算
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   if(bid <= 0) return;
   
   bool farAbove = (bid > g_swingHigh + g_point * 50);// 远离压力位50点以上
   bool farBelow = (bid < g_swingLow - g_point * 50);   // 远离支撑位50点以上
   
   if(farAbove || farBelow)
   {
      Print("价格远离当前区间,刷新压力支撑位");
      g_levelsValid = false;
      CalcLevels();
   }
}

//+------------------------------------------------------------------+
void ResetAfterClose()
{
   g_entryPrice = 0;
   g_tpSteps = 0;
   g_levelsValid = false;
   g_cooldownBars = 10;
   g_breakoutConfirm = 0;
}

//+------------------------------------------------------------------+
void OnTick()
{
   MqlRates rates[];
   ArraySetAsSeries(rates, true);
   if(CopyRates(_Symbol, PERIOD_CURRENT, 0, 3, rates) < 3) return;
   
   bool isNewBar = (rates.time != g_lastBar);
   
   if(!isNewBar)
   {
      int posType = GetPositionType();
      if(posType != 0) CheckTPStep(rates);
      return;
   }
   g_lastBar = rates.time;
   
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   if(ask <= 0 || bid <= 0 || ask == bid) return;
   
   int posType = GetPositionType();
   
   // 检测持仓是否刚被止损
   if(posType == 0 && g_entryPrice > 0)
   {
      Print(">>>检测止损平仓<<< 重置状态");
      ResetAfterClose();
      UpdateDashboard();
      return;
   }
   
   // 持仓中
   if(posType != 0)
   {
      CheckTPStep(rates);
      UpdateDashboard();
      return;
   }
   
   // 冷却期
   if(g_cooldownBars > 0)
   {
      g_cooldownBars--;
      UpdateDashboard();
      return;
   }
   
   // 动态计算/刷新压力/支撑位
   CheckRefreshLevels();
   if(!g_levelsValid)
   {
      UpdateDashboard();
      return;
   }
   
   // 每根新K线都重绘
   DrawLevelLines(g_swingHigh, g_swingLow);
   
   double prevClose = rates.close;
   
   static int dbg = 0;
   if(dbg < 15)
   {
      Print("信号[", dbg, "] 收盘:", DoubleToString(prevClose, _Digits),
            " 压力:", DoubleToString(g_swingHigh, _Digits),
            " 支撑:", DoubleToString(g_swingLow, _Digits),
            " 突破压力:", (prevClose > g_swingHigh),
            " 跌破支撑:", (prevClose < g_swingLow));
      dbg++;
   }
   
   bool above = (prevClose > g_swingHigh);
   bool below = (prevClose < g_swingLow);
   
   if(!above && !below)
   {
      g_breakoutConfirm = 0;
   }
   else if(above && g_lastSignalBar != rates.time)
   {
      if(g_breakoutConfirm <= 0) g_breakoutConfirm = 1;
      else g_breakoutConfirm++;
   }
   else if(below && g_lastSignalBar != rates.time)
   {
      if(g_breakoutConfirm >= 0) g_breakoutConfirm = -1;
      else g_breakoutConfirm--;
   }
   
   if(EnableLong && g_breakoutConfirm >= ConfirmBars)
   {
      double lot = CalcLot();
      double slPrice = ask - SL_Points * g_point;
      if(trade.Buy(lot, _Symbol, ask, slPrice, 0, TradeComment))
      {
         g_entryPrice = ask;
         g_tpSteps = 0;
         g_lastSignalBar = rates.time;
         g_breakoutConfirm = 0;
         Print(">>>突破做多<<< 压力:", DoubleToString(g_swingHigh, _Digits),
               " 入场:", ask, " 止损:", slPrice, " 手数:", lot);
      }
      else Print("做多失败: ", trade.ResultRetcodeDescription());
   }
   else if(EnableShort && g_breakoutConfirm <= -ConfirmBars)
   {
      double lot = CalcLot();
      double slPrice = bid + SL_Points * g_point;
      if(trade.Sell(lot, _Symbol, bid, slPrice, 0, TradeComment))
      {
         g_entryPrice = bid;
         g_tpSteps = 0;
         g_lastSignalBar = rates.time;
         g_breakoutConfirm = 0;
         Print(">>>跌破做空<<< 支撑:", DoubleToString(g_swingLow, _Digits),
               " 入场:", bid, " 止损:", slPrice, " 手数:", lot);
      }
      else Print("做空失败: ", trade.ResultRetcodeDescription());
   }
   
   UpdateDashboard();
}

//+------------------------------------------------------------------+
double CalcLot()
{
   double equity = AccountInfoDouble(ACCOUNT_EQUITY);
   double riskAmount = equity * RiskPercent / 100.0;
   double slDistance = SL_Points * g_point;
   
   double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   if(tickValue <= 0 || tickSize <= 0) return 0.01;
   
   double dollarPerPoint = tickValue / tickSize;
   double lot = riskAmount / (slDistance * dollarPerPoint);
   
   double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   
   if(lot < minLot) lot = minLot;
   if(lot > maxLot) lot = maxLot;
   lot = MathFloor(lot / step) * step;
   return lot;
}

//+------------------------------------------------------------------+
int GetPositionType()
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong tk = PositionGetTicket(i);
      if(!PositionSelectByTicket(tk)) continue;
      if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      return (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ? 1 : -1;
   }
   return 0;
}

//+------------------------------------------------------------------+
void CloseAll()
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong tk = PositionGetTicket(i);
      if(!PositionSelectByTicket(tk)) continue;
      if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      trade.PositionClose(tk);
   }
}

//+------------------------------------------------------------------+
void DrawLevelLines(double high, double low)
{
   static double prevH = 0, prevL = 0;
   double tol = g_point * 5;
   if(MathAbs(high - prevH) < tol && MathAbs(low - prevL) < tol) return;
   
   prevH = high;
   prevL = low;
   
   if(ObjectFind(0, "Swing_High") >= 0) ObjectDelete(0, "Swing_High");
   if(ObjectFind(0, "Swing_Low") >= 0) ObjectDelete(0, "Swing_Low");
   
   ObjectCreate(0, "Swing_High", OBJ_HLINE, 0, 0, high);
   ObjectSetInteger(0, "Swing_High", OBJPROP_COLOR, clrRed);
   ObjectSetInteger(0, "Swing_High", OBJPROP_STYLE, STYLE_SOLID);
   ObjectSetInteger(0, "Swing_High", OBJPROP_WIDTH, 2);
   ObjectSetInteger(0, "Swing_High", OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, "Swing_High", OBJPROP_BACK, true);
   
   ObjectCreate(0, "Swing_Low", OBJ_HLINE, 0, 0, low);
   ObjectSetInteger(0, "Swing_Low", OBJPROP_COLOR, clrBlue);
   ObjectSetInteger(0, "Swing_Low", OBJPROP_STYLE, STYLE_SOLID);
   ObjectSetInteger(0, "Swing_Low", OBJPROP_WIDTH, 2);
   ObjectSetInteger(0, "Swing_Low", OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, "Swing_Low", OBJPROP_BACK, true);
}

//+------------------------------------------------------------------+
void CheckTPStep(MqlRates &rates[])
{
   int posType = GetPositionType();
   if(posType == 0)
   {
      ResetAfterClose();
      return;
   }
   
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   
   double currentProfit = 0;
   int posCount = 0;
   double openPrice = 0;
   
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong tk = PositionGetTicket(i);
      if(!PositionSelectByTicket(tk)) continue;
      if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      
      currentProfit += PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP);
      openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
      posCount++;
   }
   
   if(posCount == 0) return;
   
   double profitPoints = 0;
   if(posType == 1)
      profitPoints = (bid - openPrice) / g_point;
   else
      profitPoints = (openPrice - ask) / g_point;
   
   if(profitPoints <= -SL_Points)
   {
      Print(">>>止损触发<<< 亏损:[      DISCUZ_CODE_0      ]quot;, DoubleToString(currentProfit, 2));
      CloseAll();
      ResetAfterClose();
      return;
   }
   
   int targetSteps = (int)(profitPoints / TP_Step);
   if(targetSteps > g_tpSteps && targetSteps > 0)
   {
      g_tpSteps = targetSteps;
      double newSL = 0;
      if(posType == 1)
         newSL = openPrice + (g_tpSteps - 1) * TP_Step * g_point;
      else
         newSL = openPrice - (g_tpSteps - 1) * TP_Step * g_point;
      
      for(int i = PositionsTotal() - 1; i >= 0; i--)
      {
         ulong tk = PositionGetTicket(i);
         if(!PositionSelectByTicket(tk)) continue;
         if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
         if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
         
         double curSL = PositionGetDouble(POSITION_SL);
         if(posType == 1 && (newSL > curSL || curSL == 0))
            trade.PositionModify(tk, newSL, 0);
         else if(posType == -1 && (newSL < curSL || curSL == 0))
            trade.PositionModify(tk, newSL, 0);
      }
      
      Print(">>>止盈步进<<< 第", g_tpSteps, "步 新止损:", DoubleToString(newSL, _Digits), " 盈利:[      DISCUZ_CODE_0      ]quot;, DoubleToString(currentProfit, 2));
   }
}

//+------------------------------------------------------------------+
void UpdateDashboard()
{
   double eq = AccountInfoDouble(ACCOUNT_EQUITY);
   double bal = AccountInfoDouble(ACCOUNT_BALANCE);
   int posType = GetPositionType();
   string dirStr = (posType == 1) ? "多" : (posType == -1) ? "空" : "无";
   
   string s = "═══ 波段突破 v6.1 ══\n";
   s += "Balance [      DISCUZ_CODE_0      ]quot; + DoubleToString(bal, 2) + "Equity [      DISCUZ_CODE_0      ]quot; + DoubleToString(eq, 2) + "\n";
   s += "方向: " + dirStr + "止盈步进: " + IntegerToString(g_tpSteps);
   if(g_cooldownBars > 0) s += "冷却: " + IntegerToString(g_cooldownBars) + "根";
   if(g_breakoutConfirm != 0) s += "突破计数: " + IntegerToString(MathAbs(g_breakoutConfirm));
   s += "\n压力: " + DoubleToString(g_swingHigh, _Digits) + "支撑: " + DoubleToString(g_swingLow, _Digits) + "\n";
   s += "Pivot(" + IntegerToString(PivotLeft) + "," + IntegerToString(PivotRight) + ")止损: " + DoubleToString(SL_Points, 0) + "点步进: " + DoubleToString(TP_Step, 0) + "点";
   
   if(posType != 0 && g_entryPrice > 0)
      s += "\n开仓: " + DoubleToString(g_entryPrice, _Digits);
   
   Comment(s);
}
//+------------------------------------------------------------------+

dyt20 发表于 7 天前

是自动EA还是半自动的?

Lune 发表于 前天 09:33

函数参考......

Gdibjbbb 发表于 前天 14:28

非常好 就是有点看不懂

joker888 发表于 前天 21:53

函数非常好
页: [1]
查看完整版本: 波段突破策略,计算近期高点和地点做突破