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

| 发表于 7 天前 | 显示全部楼层 |复制链接
001.png 002.png 2.png 003.png
黄金1H。100刀起步

  1. //+------------------------------------------------------------------+
  2. //|                                      波段突破策略.mq5                |
  3. //| v6.1 Pivot局部高低点突破 + 动态刷新压力支撑位                       |
  4. //+------------------------------------------------------------------+
  5. #property copyright "Swing Breakout Strategy"
  6. #property version   "6.10"
  7. #include <Trade\Trade.mqh>
  8. CTrade trade;
  9. //+------------------------------------------------------------------+
  10. //| 参数                                                              |
  11. //+------------------------------------------------------------------+
  12. input int      PivotLeft        = 5;      // 左侧K线数(找波段高/低点)
  13. input int      PivotRight       = 5;      // 右侧K线数
  14. input double   SL_Points        = 150;    // 止损点数
  15. input double   TP_Step          = 50;     // 止盈步进(点)
  16. input double   RiskPercent      = 1.0;    // 每笔风险%
  17. input int      MagicNumber      = 8888;
  18. input int      Slippage         = 3;
  19. input string   TradeComment     = "Swing";
  20. input bool     EnableLong       = true;
  21. input bool     EnableShort      = true;
  22. input int      ConfirmBars      = 1;      // 突破确认K线数
  23. //+------------------------------------------------------------------+
  24. double g_point;
  25. datetime g_lastBar = 0;
  26. double g_entryPrice = 0;
  27. double g_swingHigh = 0;
  28. double g_swingLow = 0;
  29. bool g_levelsValid = false;
  30. datetime g_lastSignalBar = 0;
  31. int g_tpSteps = 0;
  32. int g_cooldownBars = 0;
  33. int g_breakoutConfirm = 0;
  34. //+------------------------------------------------------------------+
  35. //| 初始化                                                             |
  36. //+------------------------------------------------------------------+
  37. int OnInit()
  38. {
  39.    trade.SetExpertMagicNumber(MagicNumber);
  40.    trade.SetDeviationInPoints(Slippage);
  41.    trade.SetTypeFillingBySymbol(_Symbol);
  42.    
  43.    g_point = _Point;
  44.    if(_Digits == 3 || _Digits == 5) g_point *= 10;
  45.    
  46.    g_cooldownBars = 0;
  47.    g_levelsValid = false;
  48.    g_breakoutConfirm = 0;
  49.    
  50.    Print("波段突破 v6.0 | Pivot(", PivotLeft, ",", PivotRight, ") 止损:", SL_Points, "点  步进:", TP_Step, "点");
  51.    return INIT_SUCCEEDED;
  52. }
  53. //+------------------------------------------------------------------+
  54. void OnDeinit(const int reason)
  55. {
  56.    if(ObjectFind(0, "Swing_High") >= 0) ObjectDelete(0, "Swing_High");
  57.    if(ObjectFind(0, "Swing_Low") >= 0) ObjectDelete(0, "Swing_Low");
  58.    Comment("");
  59. }
  60. //+------------------------------------------------------------------+
  61. //| 找最近的波段高点(pivot high: 比左右都高)                         |
  62. //+------------------------------------------------------------------+
  63. double FindRecentSwingHigh()
  64. {
  65.    double high[];
  66.    ArraySetAsSeries(high, true);
  67.    
  68.    // 扩大搜索范围到100根K线
  69.    int searchRange = 100;
  70.    int leftBars = PivotLeft;
  71.    int rightBars = PivotRight;
  72.    int needBars = searchRange + leftBars + rightBars;
  73.    
  74.    if(CopyHigh(_Symbol, PERIOD_CURRENT, 1, needBars, high) < needBars) return 0;
  75.    
  76.    // 从近到远搜索,找到第一个有效的pivot high
  77.    for(int start = rightBars; start < needBars - leftBars; start++)
  78.    {
  79.       bool isPivot = true;
  80.       for(int l = 1; l <= leftBars; l++)
  81.       {
  82.          if(high[start+l] >= high[start]) { isPivot = false; break; }
  83.       }
  84.       if(!isPivot) continue;
  85.       for(int r = 1; r <= rightBars; r++)
  86.       {
  87.          if(high[start-r] >= high[start]) { isPivot = false; break; }
  88.       }
  89.       if(isPivot)
  90.       {
  91.          Print("找到波段高点 #", start, " 价格:", high[start]);
  92.          return high[start];
  93.       }
  94.    }
  95.    
  96.    // 找不到pivot就用最近N根K线的最高价
  97.    double maxH = 0;
  98.    int lookback = MathMin(50, needBars);
  99.    for(int i = 1; i < lookback; i++)
  100.    {
  101.       if(high[i] > maxH) maxH = high[i];
  102.    }
  103.    return maxH > 0 ? maxH : high[1];
  104. }
  105. //+------------------------------------------------------------------+
  106. //| 找最近的波段低点(pivot low: 比左右都低)                          |
  107. //+------------------------------------------------------------------+
  108. double FindRecentSwingLow()
  109. {
  110.    double low[];
  111.    ArraySetAsSeries(low, true);
  112.    
  113.    int searchRange = 100;
  114.    int leftBars = PivotLeft;
  115.    int rightBars = PivotRight;
  116.    int needBars = searchRange + leftBars + rightBars;
  117.    
  118.    if(CopyLow(_Symbol, PERIOD_CURRENT, 1, needBars, low) < needBars) return 0;
  119.    
  120.    for(int start = rightBars; start < needBars - leftBars; start++)
  121.    {
  122.       bool isPivot = true;
  123.       for(int l = 1; l <= leftBars; l++)
  124.       {
  125.          if(low[start+l] <= low[start]) { isPivot = false; break; }
  126.       }
  127.       if(!isPivot) continue;
  128.       for(int r = 1; r <= rightBars; r++)
  129.       {
  130.          if(low[start-r] <= low[start]) { isPivot = false; break; }
  131.       }
  132.       if(isPivot)
  133.       {
  134.          Print("找到波段低点 #", start, " 价格:", low[start]);
  135.          return low[start];
  136.       }
  137.    }
  138.    
  139.    // 找不到pivot就用最近N根K线的最低价
  140.    double minL = DBL_MAX;
  141.    int lookback = MathMin(50, needBars);
  142.    for(int i = 1; i < lookback; i++)
  143.    {
  144.       if(low[i] < minL) minL = low[i];
  145.    }
  146.    return minL < DBL_MAX ? minL : low[1];
  147. }
  148. //+------------------------------------------------------------------+
  149. //| 更新压力/支撑位(持仓时也要定期更新)                              |
  150. //+------------------------------------------------------------------+
  151. bool CalcLevels()
  152. {
  153.    double sh = FindRecentSwingHigh();
  154.    double sl = FindRecentSwingLow();
  155.    
  156.    if(sh > 0 && sl > 0 && sh > sl)
  157.    {
  158.       g_swingHigh = sh;
  159.       g_swingLow = sl;
  160.       g_levelsValid = true;
  161.       return true;
  162.    }
  163.    return false;
  164. }
  165. //+------------------------------------------------------------------+
  166. //| 检查是否需要刷新压力/支撑位                                        |
  167. //+------------------------------------------------------------------+
  168. void CheckRefreshLevels()
  169. {
  170.    if(!g_levelsValid)
  171.    {
  172.       CalcLevels();
  173.       return;
  174.    }
  175.    
  176.    // 价格已经远离当前区间,需要重新计算
  177.    double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
  178.    if(bid <= 0) return;
  179.    
  180.    bool farAbove = (bid > g_swingHigh + g_point * 50);  // 远离压力位50点以上
  181.    bool farBelow = (bid < g_swingLow - g_point * 50);   // 远离支撑位50点以上
  182.    
  183.    if(farAbove || farBelow)
  184.    {
  185.       Print("价格远离当前区间,刷新压力支撑位");
  186.       g_levelsValid = false;
  187.       CalcLevels();
  188.    }
  189. }
  190. //+------------------------------------------------------------------+
  191. void ResetAfterClose()
  192. {
  193.    g_entryPrice = 0;
  194.    g_tpSteps = 0;
  195.    g_levelsValid = false;
  196.    g_cooldownBars = 10;
  197.    g_breakoutConfirm = 0;
  198. }
  199. //+------------------------------------------------------------------+
  200. void OnTick()
  201. {
  202.    MqlRates rates[];
  203.    ArraySetAsSeries(rates, true);
  204.    if(CopyRates(_Symbol, PERIOD_CURRENT, 0, 3, rates) < 3) return;
  205.    
  206.    bool isNewBar = (rates[0].time != g_lastBar);
  207.    
  208.    if(!isNewBar)
  209.    {
  210.       int posType = GetPositionType();
  211.       if(posType != 0) CheckTPStep(rates);
  212.       return;
  213.    }
  214.    g_lastBar = rates[0].time;
  215.    
  216.    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
  217.    double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
  218.    if(ask <= 0 || bid <= 0 || ask == bid) return;
  219.    
  220.    int posType = GetPositionType();
  221.    
  222.    // 检测持仓是否刚被止损
  223.    if(posType == 0 && g_entryPrice > 0)
  224.    {
  225.       Print(">>>检测止损平仓<<< 重置状态");
  226.       ResetAfterClose();
  227.       UpdateDashboard();
  228.       return;
  229.    }
  230.    
  231.    // 持仓中
  232.    if(posType != 0)
  233.    {
  234.       CheckTPStep(rates);
  235.       UpdateDashboard();
  236.       return;
  237.    }
  238.    
  239.    // 冷却期
  240.    if(g_cooldownBars > 0)
  241.    {
  242.       g_cooldownBars--;
  243.       UpdateDashboard();
  244.       return;
  245.    }
  246.    
  247.    // 动态计算/刷新压力/支撑位
  248.    CheckRefreshLevels();
  249.    if(!g_levelsValid)
  250.    {
  251.       UpdateDashboard();
  252.       return;
  253.    }
  254.    
  255.    // 每根新K线都重绘
  256.    DrawLevelLines(g_swingHigh, g_swingLow);
  257.    
  258.    double prevClose = rates[1].close;
  259.    
  260.    static int dbg = 0;
  261.    if(dbg < 15)
  262.    {
  263.       Print("信号[", dbg, "] 收盘:", DoubleToString(prevClose, _Digits),
  264.             " 压力:", DoubleToString(g_swingHigh, _Digits),
  265.             " 支撑:", DoubleToString(g_swingLow, _Digits),
  266.             " 突破压力:", (prevClose > g_swingHigh),
  267.             " 跌破支撑:", (prevClose < g_swingLow));
  268.       dbg++;
  269.    }
  270.    
  271.    bool above = (prevClose > g_swingHigh);
  272.    bool below = (prevClose < g_swingLow);
  273.    
  274.    if(!above && !below)
  275.    {
  276.       g_breakoutConfirm = 0;
  277.    }
  278.    else if(above && g_lastSignalBar != rates[1].time)
  279.    {
  280.       if(g_breakoutConfirm <= 0) g_breakoutConfirm = 1;
  281.       else g_breakoutConfirm++;
  282.    }
  283.    else if(below && g_lastSignalBar != rates[1].time)
  284.    {
  285.       if(g_breakoutConfirm >= 0) g_breakoutConfirm = -1;
  286.       else g_breakoutConfirm--;
  287.    }
  288.    
  289.    if(EnableLong && g_breakoutConfirm >= ConfirmBars)
  290.    {
  291.       double lot = CalcLot();
  292.       double slPrice = ask - SL_Points * g_point;
  293.       if(trade.Buy(lot, _Symbol, ask, slPrice, 0, TradeComment))
  294.       {
  295.          g_entryPrice = ask;
  296.          g_tpSteps = 0;
  297.          g_lastSignalBar = rates[1].time;
  298.          g_breakoutConfirm = 0;
  299.          Print(">>>突破做多<<< 压力:", DoubleToString(g_swingHigh, _Digits),
  300.                " 入场:", ask, " 止损:", slPrice, " 手数:", lot);
  301.       }
  302.       else Print("做多失败: ", trade.ResultRetcodeDescription());
  303.    }
  304.    else if(EnableShort && g_breakoutConfirm <= -ConfirmBars)
  305.    {
  306.       double lot = CalcLot();
  307.       double slPrice = bid + SL_Points * g_point;
  308.       if(trade.Sell(lot, _Symbol, bid, slPrice, 0, TradeComment))
  309.       {
  310.          g_entryPrice = bid;
  311.          g_tpSteps = 0;
  312.          g_lastSignalBar = rates[1].time;
  313.          g_breakoutConfirm = 0;
  314.          Print(">>>跌破做空<<< 支撑:", DoubleToString(g_swingLow, _Digits),
  315.                " 入场:", bid, " 止损:", slPrice, " 手数:", lot);
  316.       }
  317.       else Print("做空失败: ", trade.ResultRetcodeDescription());
  318.    }
  319.    
  320.    UpdateDashboard();
  321. }
  322. //+------------------------------------------------------------------+
  323. double CalcLot()
  324. {
  325.    double equity = AccountInfoDouble(ACCOUNT_EQUITY);
  326.    double riskAmount = equity * RiskPercent / 100.0;
  327.    double slDistance = SL_Points * g_point;
  328.    
  329.    double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
  330.    double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
  331.    if(tickValue <= 0 || tickSize <= 0) return 0.01;
  332.    
  333.    double dollarPerPoint = tickValue / tickSize;
  334.    double lot = riskAmount / (slDistance * dollarPerPoint);
  335.    
  336.    double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
  337.    double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
  338.    double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
  339.    
  340.    if(lot < minLot) lot = minLot;
  341.    if(lot > maxLot) lot = maxLot;
  342.    lot = MathFloor(lot / step) * step;
  343.    return lot;
  344. }
  345. //+------------------------------------------------------------------+
  346. int GetPositionType()
  347. {
  348.    for(int i = PositionsTotal() - 1; i >= 0; i--)
  349.    {
  350.       ulong tk = PositionGetTicket(i);
  351.       if(!PositionSelectByTicket(tk)) continue;
  352.       if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
  353.       if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
  354.       return (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ? 1 : -1;
  355.    }
  356.    return 0;
  357. }
  358. //+------------------------------------------------------------------+
  359. void CloseAll()
  360. {
  361.    for(int i = PositionsTotal() - 1; i >= 0; i--)
  362.    {
  363.       ulong tk = PositionGetTicket(i);
  364.       if(!PositionSelectByTicket(tk)) continue;
  365.       if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
  366.       if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
  367.       trade.PositionClose(tk);
  368.    }
  369. }
  370. //+------------------------------------------------------------------+
  371. void DrawLevelLines(double high, double low)
  372. {
  373.    static double prevH = 0, prevL = 0;
  374.    double tol = g_point * 5;
  375.    if(MathAbs(high - prevH) < tol && MathAbs(low - prevL) < tol) return;
  376.    
  377.    prevH = high;
  378.    prevL = low;
  379.    
  380.    if(ObjectFind(0, "Swing_High") >= 0) ObjectDelete(0, "Swing_High");
  381.    if(ObjectFind(0, "Swing_Low") >= 0) ObjectDelete(0, "Swing_Low");
  382.    
  383.    ObjectCreate(0, "Swing_High", OBJ_HLINE, 0, 0, high);
  384.    ObjectSetInteger(0, "Swing_High", OBJPROP_COLOR, clrRed);
  385.    ObjectSetInteger(0, "Swing_High", OBJPROP_STYLE, STYLE_SOLID);
  386.    ObjectSetInteger(0, "Swing_High", OBJPROP_WIDTH, 2);
  387.    ObjectSetInteger(0, "Swing_High", OBJPROP_SELECTABLE, false);
  388.    ObjectSetInteger(0, "Swing_High", OBJPROP_BACK, true);
  389.    
  390.    ObjectCreate(0, "Swing_Low", OBJ_HLINE, 0, 0, low);
  391.    ObjectSetInteger(0, "Swing_Low", OBJPROP_COLOR, clrBlue);
  392.    ObjectSetInteger(0, "Swing_Low", OBJPROP_STYLE, STYLE_SOLID);
  393.    ObjectSetInteger(0, "Swing_Low", OBJPROP_WIDTH, 2);
  394.    ObjectSetInteger(0, "Swing_Low", OBJPROP_SELECTABLE, false);
  395.    ObjectSetInteger(0, "Swing_Low", OBJPROP_BACK, true);
  396. }
  397. //+------------------------------------------------------------------+
  398. void CheckTPStep(MqlRates &rates[])
  399. {
  400.    int posType = GetPositionType();
  401.    if(posType == 0)
  402.    {
  403.       ResetAfterClose();
  404.       return;
  405.    }
  406.    
  407.    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
  408.    double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
  409.    
  410.    double currentProfit = 0;
  411.    int posCount = 0;
  412.    double openPrice = 0;
  413.    
  414.    for(int i = PositionsTotal() - 1; i >= 0; i--)
  415.    {
  416.       ulong tk = PositionGetTicket(i);
  417.       if(!PositionSelectByTicket(tk)) continue;
  418.       if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
  419.       if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
  420.       
  421.       currentProfit += PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP);
  422.       openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
  423.       posCount++;
  424.    }
  425.    
  426.    if(posCount == 0) return;
  427.    
  428.    double profitPoints = 0;
  429.    if(posType == 1)
  430.       profitPoints = (bid - openPrice) / g_point;
  431.    else
  432.       profitPoints = (openPrice - ask) / g_point;
  433.    
  434.    if(profitPoints <= -SL_Points)
  435.    {
  436.       Print(">>>止损触发<<< 亏损:[        DISCUZ_CODE_0        ]quot;, DoubleToString(currentProfit, 2));
  437.       CloseAll();
  438.       ResetAfterClose();
  439.       return;
  440.    }
  441.    
  442.    int targetSteps = (int)(profitPoints / TP_Step);
  443.    if(targetSteps > g_tpSteps && targetSteps > 0)
  444.    {
  445.       g_tpSteps = targetSteps;
  446.       double newSL = 0;
  447.       if(posType == 1)
  448.          newSL = openPrice + (g_tpSteps - 1) * TP_Step * g_point;
  449.       else
  450.          newSL = openPrice - (g_tpSteps - 1) * TP_Step * g_point;
  451.       
  452.       for(int i = PositionsTotal() - 1; i >= 0; i--)
  453.       {
  454.          ulong tk = PositionGetTicket(i);
  455.          if(!PositionSelectByTicket(tk)) continue;
  456.          if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
  457.          if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
  458.          
  459.          double curSL = PositionGetDouble(POSITION_SL);
  460.          if(posType == 1 && (newSL > curSL || curSL == 0))
  461.             trade.PositionModify(tk, newSL, 0);
  462.          else if(posType == -1 && (newSL < curSL || curSL == 0))
  463.             trade.PositionModify(tk, newSL, 0);
  464.       }
  465.       
  466.       Print(">>>止盈步进<<< 第", g_tpSteps, "步 新止损:", DoubleToString(newSL, _Digits), " 盈利:[        DISCUZ_CODE_0        ]quot;, DoubleToString(currentProfit, 2));
  467.    }
  468. }
  469. //+------------------------------------------------------------------+
  470. void UpdateDashboard()
  471. {
  472.    double eq = AccountInfoDouble(ACCOUNT_EQUITY);
  473.    double bal = AccountInfoDouble(ACCOUNT_BALANCE);
  474.    int posType = GetPositionType();
  475.    string dirStr = (posType == 1) ? "多" : (posType == -1) ? "空" : "无";
  476.    
  477.    string s = "═══ 波段突破 v6.1 ══\n";
  478.    s += "Balance [        DISCUZ_CODE_0        ]quot; + DoubleToString(bal, 2) + "  Equity [        DISCUZ_CODE_0        ]quot; + DoubleToString(eq, 2) + "\n";
  479.    s += "方向: " + dirStr + "  止盈步进: " + IntegerToString(g_tpSteps);
  480.    if(g_cooldownBars > 0) s += "  冷却: " + IntegerToString(g_cooldownBars) + "根";
  481.    if(g_breakoutConfirm != 0) s += "  突破计数: " + IntegerToString(MathAbs(g_breakoutConfirm));
  482.    s += "\n压力: " + DoubleToString(g_swingHigh, _Digits) + "  支撑: " + DoubleToString(g_swingLow, _Digits) + "\n";
  483.    s += "Pivot(" + IntegerToString(PivotLeft) + "," + IntegerToString(PivotRight) + ")  止损: " + DoubleToString(SL_Points, 0) + "点  步进: " + DoubleToString(TP_Step, 0) + "点";
  484.    
  485.    if(posType != 0 && g_entryPrice > 0)
  486.       s += "\n开仓: " + DoubleToString(g_entryPrice, _Digits);
  487.    
  488.    Comment(s);
  489. }
  490. //+------------------------------------------------------------------+
复制代码
[/codebuy]
举报

评论 使用道具

精彩评论1

dyt20
DD
| 发表于 前天 23:30 | 显示全部楼层
是自动EA还是半自动的?
举报

点赞 评论 使用道具

发新帖
EA交易
您需要登录后才可以评论 登录 | 立即注册