量化魔方 发表于 2026-9-17 11:40:11

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

黄金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[1];
[*]}
[*]
[*]//+------------------------------------------------------------------+
[*]//| 找最近的波段低点(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[1];
[*]}
[*]
[*]//+------------------------------------------------------------------+
[*]//| 更新压力/支撑位(持仓时也要定期更新)                              |
[*]//+------------------------------------------------------------------+
[*]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[0].time != g_lastBar);
[*]
[*]   if(!isNewBar)
[*]   {
[*]      int posType = GetPositionType();
[*]      if(posType != 0) CheckTPStep(rates);
[*]      return;
[*]   }
[*]   g_lastBar = rates[0].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[1].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[1].time)
[*]   {
[*]      if(g_breakoutConfirm <= 0) g_breakoutConfirm = 1;
[*]      else g_breakoutConfirm++;
[*]   }
[*]   else if(below && g_lastSignalBar != rates[1].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[1].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[1].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);
[*]}
[*]//+------------------------------------------------------------------+

页: [1]
查看完整版本: 波段突破策略,计算近期高点和地点做突破 (MT5)