快捷导航
查看: 10|回复: 0

[策略/信号] 波段突破策略,计算近期高点和地点做突破 (MT5)

[复制链接]

284

主题

6

回帖

1060

积分

管理员

积分
1060
发表于 昨天 11:40 | 显示全部楼层 |阅读模式
黄金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[start+l] >= high[start]) { isPivot = false; break; }
  •       }
  •       if(!isPivot) continue;
  •       for(int r = 1; r <= rightBars; r++)
  •       {
  •          if(high[start-r] >= high[start]) { isPivot = false; break; }
  •       }
  •       if(isPivot)
  •       {
  •          Print("找到波段高点 #", start, " 价格:", high[start]);
  •          return high[start];
  •       }
  •    }
  •    // 找不到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[start+l] <= low[start]) { isPivot = false; break; }
  •       }
  •       if(!isPivot) continue;
  •       for(int r = 1; r <= rightBars; r++)
  •       {
  •          if(low[start-r] <= low[start]) { isPivot = false; break; }
  •       }
  •       if(isPivot)
  •       {
  •          Print("找到波段低点 #", start, " 价格:", low[start]);
  •          return low[start];
  •       }
  •    }
  •    // 找不到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);
  • }
  • //+------------------------------------------------------------------+

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|量化魔方 ( 陕ICP备2025062059号-3 )|网站地图

GMT+8, 2026-9-18 01:22 , Processed in 0.146076 second(s), 5 queries , Redis On.

快速回复 返回顶部 返回列表