网格+对冲,黄金EA源码

| 发表于 2026-4-27 20:17:18 | 显示全部楼层 |复制链接
  1. #property strict
  2. #property copyright "加仓-安全版"
  3. #property link      ""
  4. //============================================================
  5. // 外部参数:资金管理
  6. //============================================================
  7. extern string MM_block      = "==== 资金管理 ====";
  8. extern bool   UseMoneyManagement = true;   // 是否启用自动资金管理(按风险百分比自动算手数)
  9. extern double RiskPerTrade  = 2.0;         // 每笔单风险百分比(用于主网格单基础手数)
  10. extern double MaxDrawdown   = 20.0;        // 最大允许回撤百分比(从EA启动时权益算),超过则清仓并停止交易
  11. extern double MinLots       = 0.01;        // 最小手数(还会按券商 minlot/lotstep 规范化)
  12. extern double MaxLots       = 0.5;         // 最大手数(还会按券商 maxlot 规范化)
  13. extern int    StopLossPips  = 600;         // 主网格每单止损(pips)。Digits=2且pip=0.01时:600=6美元
  14. extern int    TakeProfitPips= 600;         // 主网格每单止盈(pips)
  15. //============================================================
  16. // 外部参数:加仓与网格
  17. //============================================================
  18. extern string Grid_block    = "==== 加仓设置 ====";
  19. extern int    MaxTrades     = 6;           // 主网格同向最大持仓笔数(达到后停止同向加仓)
  20. extern double LotExponent   = 1.3;         // 主网格每层手数倍增
  21. extern bool   UseDynamicPips= true;        // 动态加仓间距
  22. extern int    DefaultPips   = 300;         // 静态加仓间距(pips)
  23. extern int    DynamicDepth  = 24;
  24. extern int    DynamicDivider= 3;
  25. //============================================================
  26. // 外部参数:对冲设置
  27. // 触发条件:主网格浮亏 >= 余额(Balance) 的 HedgeTriggerBalancePercent%
  28. //============================================================
  29. extern string Hedge_block   = "==== 对冲设置 ====";
  30. extern bool   UseHedge      = true;        // 是否启用对冲
  31. extern double HedgeTriggerBalancePercent = 20.0; // 余额20%触发对冲(你的要求)
  32. extern int    HedgeStepPips = 200;         // 对冲分段触发间距(pips)
  33. extern double HedgePct1     = 0.10;        // 第1次对冲手数 = 主网格总手数*10%
  34. extern double HedgePct2     = 0.15;        // 第2次对冲手数 = 主网格总手数*15%
  35. extern double HedgePct3     = 0.20;        // 第3次对冲手数 = 主网格总手数*20%
  36. extern bool   HedgeUseTP    = false;       // 对冲单是否设TP(建议false)
  37. extern bool   HedgeUseSL    = true;        // 对冲单是否设SL(建议true)
  38. extern int    HedgeSLPips   = 1200;        // 对冲单SL(pips)
  39. extern int    HedgeTPPips   = 600;         // 对冲单TP(pips)仅HedgeUseTP=true有效
  40. //============================================================
  41. // 外部参数:方向控制与指标
  42. //============================================================
  43. extern string Dir_block     = "==== 方向控制与信号 ====";
  44. extern bool   onlylong      = true;
  45. extern bool   onlyshort     = true;
  46. extern int    CCI_Period    = 55;
  47. extern int    RSI_Period    = 14;
  48. extern int    CCI_Timeframe = PERIOD_M15;
  49. extern int    RSI_Timeframe = PERIOD_H1;
  50. extern double CCI_Limit     = 200.0;       // |CCI|>=该值时触发紧急清仓(主网格+对冲一起平)
  51. extern double RSI_SellLevel = 70.0;
  52. extern double RSI_BuyLevel  = 30.0;
  53. //============================================================
  54. // 外部参数:其它风控与交易参数
  55. //============================================================
  56. extern string Risk_block      = "==== 其它风控 ====";
  57. extern bool   UseTrailingStop = true;      // 主网格跟踪止损开关
  58. extern int    TrailingStart   = 300;       // 主网格盈利超过多少pips开始跟踪
  59. extern int    TrailingStep    = 100;       // 主网格跟踪止损距离(pips)
  60. // 整组风控阈值:主网格浮亏达到权益百分比 -> 停止同向加仓(不影响对冲)
  61. extern double MaxBasketLossPercent = 20.0;
  62. extern string Trade_block   = "==== 交易参数 ====";
  63. extern int    MagicNumber   = 2222;        // 主网格magic
  64. extern int    Slip          = 30;          // 滑点(points)
  65. //============================================================
  66. // 全局变量
  67. //============================================================
  68. double gInitialEquity = 0.0;
  69. bool   gTradingStopped = false;
  70. bool   gNoMoreAdd = false;
  71. //============================================================
  72. // 工具函数:pip/手数/止损距离规范化
  73. //============================================================
  74. double GetPipSize()
  75. {
  76.    if (Digits == 3 || Digits == 5) return(10 * Point);
  77.    return(Point);
  78. }
  79. double NormalizeLots(double lots)
  80. {
  81.    double minLot  = MarketInfo(Symbol(), MODE_MINLOT);
  82.    double maxLot  = MarketInfo(Symbol(), MODE_MAXLOT);
  83.    double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
  84.    if (lotStep <= 0) lotStep = 0.01;
  85.    if (lots < minLot) lots = minLot;
  86.    if (lots > maxLot) lots = maxLot;
  87.    lots = MathFloor(lots / lotStep) * lotStep;
  88.    int d = 2;
  89.    if (lotStep < 0.01)  d = 3;
  90.    if (lotStep < 0.001) d = 4;
  91.    return NormalizeDouble(lots, d);
  92. }
  93. double GetMinStopDistancePrice()
  94. {
  95.    int stopLevel   = (int)MarketInfo(Symbol(), MODE_STOPLEVEL);
  96.    int freezeLevel = (int)MarketInfo(Symbol(), MODE_FREEZELEVEL);
  97.    int minLevel = stopLevel;
  98.    if (freezeLevel > minLevel) minLevel = freezeLevel;
  99.    return minLevel * Point;
  100. }
  101. void AdjustStopsToBrokerRules(int type, double entry, double &sl, double &tp)
  102. {
  103.    double minDist = GetMinStopDistancePrice();
  104.    if (minDist > 0)
  105.    {
  106.       if (type == OP_BUY)
  107.       {
  108.          if (sl > 0 && (entry - sl) < minDist) sl = entry - minDist;
  109.          if (tp > 0 && (tp - entry) < minDist) tp = entry + minDist;
  110.       }
  111.       else if (type == OP_SELL)
  112.       {
  113.          if (sl > 0 && (sl - entry) < minDist) sl = entry + minDist;
  114.          if (tp > 0 && (entry - tp) < minDist) tp = entry - minDist;
  115.       }
  116.    }
  117.    if (sl > 0) sl = NormalizeDouble(sl, Digits);
  118.    if (tp > 0) tp = NormalizeDouble(tp, Digits);
  119. }
  120. //============================================================
  121. // 订单统计(按magic区分主网格/对冲)
  122. //============================================================
  123. int CountOpenTradesByMagic(int magic)
  124. {
  125.    int count = 0;
  126.    for (int i = OrdersTotal() - 1; i >= 0; i--)
  127.    {
  128.       if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
  129.       if(OrderSymbol()!=Symbol() || OrderMagicNumber()!=magic) continue;
  130.       int type = OrderType();
  131.       if(type==OP_BUY || type==OP_SELL) count++;
  132.    }
  133.    return count;
  134. }
  135. int CountOpenTrades()
  136. {
  137.    return CountOpenTradesByMagic(MagicNumber);
  138. }
  139. int GetCurrentDirection()
  140. {
  141.    bool hasBuy=false, hasSell=false;
  142.    for (int i = OrdersTotal() - 1; i >= 0; i--)
  143.    {
  144.       if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
  145.       if(OrderSymbol()!=Symbol() || OrderMagicNumber()!=MagicNumber) continue;
  146.       if(OrderType()==OP_BUY)  hasBuy=true;
  147.       if(OrderType()==OP_SELL) hasSell=true;
  148.    }
  149.    if(hasBuy && !hasSell) return 1;
  150.    if(!hasBuy && hasSell) return -1;
  151.    return 0;
  152. }
  153. double GetLastOpenPriceByMagic(int type, int magic)
  154. {
  155.    double price=0.0;
  156.    datetime lastTime=0;
  157.    for (int i = OrdersTotal() - 1; i >= 0; i--)
  158.    {
  159.       if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
  160.       if(OrderSymbol()!=Symbol() || OrderMagicNumber()!=magic) continue;
  161.       if(OrderType()==type && OrderOpenTime() > lastTime)
  162.       {
  163.          lastTime = OrderOpenTime();
  164.          price = OrderOpenPrice();
  165.       }
  166.    }
  167.    return price;
  168. }
  169. double GetLastOpenPrice(int type)
  170. {
  171.    return GetLastOpenPriceByMagic(type, MagicNumber);
  172. }
  173. double GetBasketFloatingProfitByMagic(int magic)
  174. {
  175.    double sum = 0.0;
  176.    for (int i = OrdersTotal() - 1; i >= 0; i--)
  177.    {
  178.       if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
  179.       if(OrderSymbol()!=Symbol() || OrderMagicNumber()!=magic) continue;
  180.       int type = OrderType();
  181.       if(type!=OP_BUY && type!=OP_SELL) continue;
  182.       sum += OrderProfit() + OrderSwap() + OrderCommission();
  183.    }
  184.    return sum;
  185. }
  186. double GetBasketFloatingProfit()
  187. {
  188.    return GetBasketFloatingProfitByMagic(MagicNumber);
  189. }
  190. double GetBasketLotsByMagicAndDirection(int magic, int dir)
  191. {
  192.    double lots = 0.0;
  193.    for (int i = OrdersTotal() - 1; i >= 0; i--)
  194.    {
  195.       if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
  196.       if(OrderSymbol()!=Symbol() || OrderMagicNumber()!=magic) continue;
  197.       int type = OrderType();
  198.       if (dir == 1 && type == OP_BUY)  lots += OrderLots();
  199.       if (dir == -1 && type == OP_SELL) lots += OrderLots();
  200.    }
  201.    return lots;
  202. }
  203. void CloseAllTradesByMagic(int magic)
  204. {
  205.    for (int i = OrdersTotal() - 1; i >= 0; i--)
  206.    {
  207.       if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
  208.       if(OrderSymbol()!=Symbol() || OrderMagicNumber()!=magic) continue;
  209.       int type = OrderType();
  210.       if(type!=OP_BUY && type!=OP_SELL) continue;
  211.       double price = (type==OP_BUY) ? Bid : Ask;
  212.       if(!OrderClose(OrderTicket(), OrderLots(), price, Slip, clrRed))
  213.          Print("CloseAllTrades failed magic=", magic, " ticket=", OrderTicket(), " err=", GetLastError());
  214.    }
  215. }
  216. void CloseAllTrades()
  217. {
  218.    CloseAllTradesByMagic(MagicNumber);
  219.    CloseAllTradesByMagic(MagicNumber + 1);
  220. }
  221. //============================================================
  222. // 资金管理:pip价值/基础手数/分层手数
  223. //============================================================
  224. double GetPipValuePerLot()
  225. {
  226.    double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
  227.    double tickSize  = MarketInfo(Symbol(), MODE_TICKSIZE);
  228.    if (tickSize <= 0.0) return 0.0;
  229.    double pipSize = GetPipSize();
  230.    return tickValue * (pipSize / tickSize);
  231. }
  232. double CalcBaseLots()
  233. {
  234.    double lots = MinLots;
  235.    if (!UseMoneyManagement || StopLossPips <= 0)
  236.       return NormalizeLots(lots);
  237.    double equity    = AccountEquity();
  238.    double riskMoney = equity * RiskPerTrade / 100.0;
  239.    double pipValue  = GetPipValuePerLot();
  240.    if (pipValue <= 0.0) return NormalizeLots(lots);
  241.    double riskPerLot = StopLossPips * pipValue;
  242.    if (riskPerLot <= 0.0) return NormalizeLots(lots);
  243.    lots = riskMoney / riskPerLot;
  244.    if (lots < MinLots) lots = MinLots;
  245.    if (lots > MaxLots) lots = MaxLots;
  246.    return NormalizeLots(lots);
  247. }
  248. double CalcLotForLevel(int level)
  249. {
  250.    double lots = CalcBaseLots() * MathPow(LotExponent, level);
  251.    if (lots < MinLots) lots = MinLots;
  252.    if (lots > MaxLots) lots = MaxLots;
  253.    return NormalizeLots(lots);
  254. }
  255. //============================================================
  256. // 风控:最大回撤停机;整组亏损阈值判断
  257. //============================================================
  258. void CheckEquityStop()
  259. {
  260.    if (MaxDrawdown <= 0.0) return;
  261.    if (gInitialEquity <= 0.0) gInitialEquity = AccountEquity();
  262.    double dd = (gInitialEquity - AccountEquity()) / gInitialEquity * 100.0;
  263.    if (dd >= MaxDrawdown)
  264.    {
  265.       Print("MaxDrawdown triggered. DD=", DoubleToStr(dd,2), "% >= ", DoubleToStr(MaxDrawdown,2), "%");
  266.       CloseAllTrades();
  267.       gTradingStopped = true;
  268.    }
  269. }
  270. // 主网格是否达到“停止同向加仓阈值”(按权益%)
  271. bool IsAddStopLossOverLimit()
  272. {
  273.    if (MaxBasketLossPercent <= 0.0) return false;
  274.    if (CountOpenTrades() <= 0) return false;
  275.    double eq = AccountEquity();
  276.    if (eq <= 0.0) return false;
  277.    double basketProfit = GetBasketFloatingProfit(); // 负数=浮亏
  278.    if (basketProfit >= 0.0) return false;
  279.    double lossPercent = (-basketProfit) / eq * 100.0;
  280.    return (lossPercent >= MaxBasketLossPercent);
  281. }
  282. // 主网格是否达到“启动对冲阈值”(按余额Balance%)——你的要求
  283. bool IsHedgeTriggerOverLimit()
  284. {
  285.    if (HedgeTriggerBalancePercent <= 0.0) return false;
  286.    if (CountOpenTrades() <= 0) return false;
  287.    double bal = AccountBalance();
  288.    if (bal <= 0.0) return false;
  289.    double basketProfit = GetBasketFloatingProfit(); // 负数=浮亏
  290.    if (basketProfit >= 0.0) return false;
  291.    double lossPercent = (-basketProfit) / bal * 100.0;
  292.    return (lossPercent >= HedgeTriggerBalancePercent);
  293. }
  294. void CheckBasketLossStopAdding()
  295. {
  296.    gNoMoreAdd = IsAddStopLossOverLimit();
  297. }
  298. //============================================================
  299. // 跟踪止损:只对主网格单应用
  300. //============================================================
  301. void ApplyTrailingStop()
  302. {
  303.    if (!UseTrailingStop) return;
  304.    double pip = GetPipSize();
  305.    for (int i = OrdersTotal() - 1; i >= 0; i--)
  306.    {
  307.       if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
  308.       if(OrderSymbol()!=Symbol() || OrderMagicNumber()!=MagicNumber) continue;
  309.       int type = OrderType();
  310.       if(type!=OP_BUY && type!=OP_SELL) continue;
  311.       double open = OrderOpenPrice();
  312.       double sl   = OrderStopLoss();
  313.       double tp   = OrderTakeProfit();
  314.       if (type == OP_BUY)
  315.       {
  316.          double profitPips = (Bid - open) / pip;
  317.          if (profitPips > TrailingStart)
  318.          {
  319.             double newSL = Bid - TrailingStep * pip;
  320.             if (sl < newSL)
  321.             {
  322.                if(!OrderModify(OrderTicket(), open, NormalizeDouble(newSL, Digits), tp, 0, clrAqua))
  323.                   Print("Trailing BUY modify fail ticket=", OrderTicket(), " err=", GetLastError());
  324.             }
  325.          }
  326.       }
  327.       else
  328.       {
  329.          double profitPips = (open - Ask) / pip;
  330.          if (profitPips > TrailingStart)
  331.          {
  332.             double newSL = Ask + TrailingStep * pip;
  333.             if (sl == 0.0 || sl > newSL)
  334.             {
  335.                if(!OrderModify(OrderTicket(), open, NormalizeDouble(newSL, Digits), tp, 0, clrAqua))
  336.                   Print("Trailing SELL modify fail ticket=", OrderTicket(), " err=", GetLastError());
  337.             }
  338.          }
  339.       }
  340.    }
  341. }
  342. //============================================================
  343. // 动态加仓间距(pips)
  344. //============================================================
  345. double CalcDynamicPips()
  346. {
  347.    if (!UseDynamicPips || DynamicDepth <= 0 || DynamicDivider <= 0)
  348.       return(DefaultPips);
  349.    double pip = GetPipSize();
  350.    int hi = iHighest(NULL, 0, MODE_HIGH, DynamicDepth, 1);
  351.    int lo = iLowest(NULL, 0, MODE_LOW,  DynamicDepth, 1);
  352.    double rangePips = (High[hi] - Low[lo]) / pip;
  353.    if (rangePips <= 0.0) return(DefaultPips);
  354.    double dist = rangePips / DynamicDivider;
  355.    double minDist = DefaultPips / 2.0;
  356.    double maxDist = DefaultPips * 3.0;
  357.    if (dist < minDist) dist = minDist;
  358.    if (dist > maxDist) dist = maxDist;
  359.    return(dist);
  360. }
  361. //============================================================
  362. // 紧急离场:CCI极值清仓(主网格+对冲一起平)
  363. //============================================================
  364. void CheckCCIEmergencyExit()
  365. {
  366.    double cci = iCCI(NULL, CCI_Timeframe, CCI_Period, PRICE_CLOSE, 0);
  367.    if (MathAbs(cci) >= CCI_Limit)
  368.    {
  369.       Print("CCI emergency exit. cci=", DoubleToStr(cci,2));
  370.       CloseAllTrades();
  371.    }
  372. }
  373. //============================================================
  374. // 主网格:首单信号与加仓逻辑
  375. //============================================================
  376. void CheckFirstEntry()
  377. {
  378.    if (CountOpenTrades() > 0) return;
  379.    double close2 = iClose(Symbol(), 0, 2);
  380.    double close1 = iClose(Symbol(), 0, 1);
  381.    double rsi    = iRSI(NULL, RSI_Timeframe, RSI_Period, PRICE_CLOSE, 1);
  382.    double lots = CalcBaseLots();
  383.    if (lots <= 0.0) return;
  384.    double pip = GetPipSize();
  385.    if (close2 < close1 && rsi > RSI_SellLevel && onlyshort)
  386.    {
  387.       int type = OP_SELL;
  388.       double entry = Bid;
  389.       double sl = entry + StopLossPips * pip;
  390.       double tp = entry - TakeProfitPips * pip;
  391.       AdjustStopsToBrokerRules(type, entry, sl, tp);
  392.       int ticket = OrderSend(Symbol(), type, lots, entry, Slip, sl, tp,
  393.                              "GridEA SELL 0", MagicNumber, 0, clrRed);
  394.       if (ticket < 0) Print("First SELL failed err=", GetLastError());
  395.    }
  396.    else if (close2 > close1 && rsi < RSI_BuyLevel && onlylong)
  397.    {
  398.       int type = OP_BUY;
  399.       double entry = Ask;
  400.       double sl = entry - StopLossPips * pip;
  401.       double tp = entry + TakeProfitPips * pip;
  402.       AdjustStopsToBrokerRules(type, entry, sl, tp);
  403.       int ticket = OrderSend(Symbol(), type, lots, entry, Slip, sl, tp,
  404.                              "GridEA BUY 0", MagicNumber, 0, clrLime);
  405.       if (ticket < 0) Print("First BUY failed err=", GetLastError());
  406.    }
  407. }
  408. void CheckAddPositions()
  409. {
  410.    // 达到权益亏损阈值后停止同向加仓
  411.    if (gNoMoreAdd) return;
  412.    int total = CountOpenTrades();
  413.    if (total <= 0) return;
  414.    if (total >= MaxTrades) return;
  415.    int dir = GetCurrentDirection();
  416.    if (dir == 0) return;
  417.    double pip      = GetPipSize();
  418.    double distPips = CalcDynamicPips();
  419.    int    level = total;
  420.    double lots  = CalcLotForLevel(level);
  421.    if (lots <= 0.0) return;
  422.    if (dir == 1)
  423.    {
  424.       double lastBuyPrice = GetLastOpenPrice(OP_BUY);
  425.       if (lastBuyPrice <= 0.0) return;
  426.       double movePips = (lastBuyPrice - Bid) / pip;
  427.       if (movePips >= distPips)
  428.       {
  429.          int type = OP_BUY;
  430.          double entry = Ask;
  431.          double sl = entry - StopLossPips * pip;
  432.          double tp = entry + TakeProfitPips * pip;
  433.          AdjustStopsToBrokerRules(type, entry, sl, tp);
  434.          int ticket = OrderSend(Symbol(), type, lots, entry, Slip, sl, tp,
  435.                                 "GridEA BUY " + IntegerToString(level),
  436.                                 MagicNumber, 0, clrLime);
  437.          if (ticket < 0) Print("Add BUY failed err=", GetLastError());
  438.       }
  439.    }
  440.    else if (dir == -1)
  441.    {
  442.       double lastSellPrice = GetLastOpenPrice(OP_SELL);
  443.       if (lastSellPrice <= 0.0) return;
  444.       double movePips = (Ask - lastSellPrice) / pip;
  445.       if (movePips >= distPips)
  446.       {
  447.          int type = OP_SELL;
  448.          double entry = Bid;
  449.          double sl = entry + StopLossPips * pip;
  450.          double tp = entry - TakeProfitPips * pip;
  451.          AdjustStopsToBrokerRules(type, entry, sl, tp);
  452.          int ticket = OrderSend(Symbol(), type, lots, entry, Slip, sl, tp,
  453.                                 "GridEA SELL " + IntegerToString(level),
  454.                                 MagicNumber, 0, clrRed);
  455.          if (ticket < 0) Print("Add SELL failed err=", GetLastError());
  456.       }
  457.    }
  458. }
  459. //============================================================
  460. // 对冲逻辑:触发条件 = 浮亏达到 Balance 的 20%(可调)
  461. // 对冲分三段触发:第1段立即;第2/3段按 HedgeStepPips 分段
  462. //============================================================
  463. int GetHedgeStage()
  464. {
  465.    int hedgeMagic = MagicNumber + 1;
  466.    int count = CountOpenTradesByMagic(hedgeMagic);
  467.    if (count <= 0) return 0;
  468.    if (count == 1) return 1;
  469.    if (count == 2) return 2;
  470.    return 3;
  471. }
  472. void CheckHedge()
  473. {
  474.    if (!UseHedge) return;
  475.    // 必须有主网格持仓
  476.    if (CountOpenTrades() <= 0) return;
  477.    // 触发条件:主网格浮亏达到 Balance 的 HedgeTriggerBalancePercent%
  478.    if (!IsHedgeTriggerOverLimit()) return;
  479.    // 主网格方向必须明确
  480.    int dir = GetCurrentDirection();
  481.    if (dir == 0) return;
  482.    int hedgeMagic = MagicNumber + 1;
  483.    int stage = GetHedgeStage();
  484.    if (stage >= 3) return;
  485.    // 主网格当前方向总手数
  486.    double basketLots = GetBasketLotsByMagicAndDirection(MagicNumber, dir);
  487.    if (basketLots <= 0.0) return;
  488.    // 本次对冲手数比例
  489.    double pct = (stage == 0 ? HedgePct1 : (stage == 1 ? HedgePct2 : HedgePct3));
  490.    double lots = NormalizeLots(basketLots * pct);
  491.    if (lots <= 0.0) return;
  492.    double pip = GetPipSize();
  493.    // 分段触发:第1段立刻开;第2/3段需要继续不利方向移动 HedgeStepPips
  494.    bool allowOpen = false;
  495.    if (stage == 0)
  496.    {
  497.       allowOpen = true;
  498.    }
  499.    else
  500.    {
  501.       int hedgeTypePrev = (dir == 1) ? OP_SELL : OP_BUY;
  502.       double lastHedgePrice = GetLastOpenPriceByMagic(hedgeTypePrev, hedgeMagic);
  503.       if (lastHedgePrice <= 0.0) return;
  504.       if (dir == 1)
  505.       {
  506.          // 主仓BUY:下跌不利
  507.          double move = (lastHedgePrice - Bid) / pip;
  508.          if (move >= HedgeStepPips) allowOpen = true;
  509.       }
  510.       else
  511.       {
  512.          // 主仓SELL:上涨不利
  513.          double move = (Ask - lastHedgePrice) / pip;
  514.          if (move >= HedgeStepPips) allowOpen = true;
  515.       }
  516.    }
  517.    if (!allowOpen) return;
  518.    // 对冲方向:与主网格相反
  519.    int type = (dir == 1) ? OP_SELL : OP_BUY;
  520.    double entry = (type == OP_BUY) ? Ask : Bid;
  521.    double sl = 0.0, tp = 0.0;
  522.    if (HedgeUseSL)
  523.    {
  524.       if (type == OP_BUY)  sl = entry - HedgeSLPips * pip;
  525.       if (type == OP_SELL) sl = entry + HedgeSLPips * pip;
  526.    }
  527.    if (HedgeUseTP)
  528.    {
  529.       if (type == OP_BUY)  tp = entry + HedgeTPPips * pip;
  530.       if (type == OP_SELL) tp = entry - HedgeTPPips * pip;
  531.    }
  532.    AdjustStopsToBrokerRules(type, entry, sl, tp);
  533.    string comment = "HEDGE " + IntegerToString(stage + 1);
  534.    int ticket = OrderSend(Symbol(), type, lots, entry, Slip,
  535.                           (HedgeUseSL ? sl : 0.0),
  536.                           (HedgeUseTP ? tp : 0.0),
  537.                           comment, hedgeMagic, 0, clrOrange);
  538.    if (ticket < 0)
  539.    {
  540.       Print("Hedge OrderSend failed stage=", stage+1,
  541.             " lots=", DoubleToStr(lots,2),
  542.             " err=", GetLastError());
  543.    }
  544. }
  545. //============================================================
  546. // EA 生命周期函数
  547. //============================================================
  548. int init()
  549. {
  550.    gInitialEquity  = AccountEquity();
  551.    gTradingStopped = false;
  552.    gNoMoreAdd      = false;
  553.    return(0);
  554. }
  555. int deinit()
  556. {
  557.    Comment("");
  558.    return(0);
  559. }
  560. int start()
  561. {
  562.    if (gTradingStopped) return(0);
  563.    // 1) 回撤超限:清仓并停机
  564.    CheckEquityStop();
  565.    if (gTradingStopped) return(0);
  566.    // 2) 主网格浮亏达到权益阈值:停止同向加仓
  567.    CheckBasketLossStopAdding();
  568.    // 3) 主网格跟踪止损
  569.    ApplyTrailingStop();
  570.    // 4) CCI 紧急离场:清仓
  571.    CheckCCIEmergencyExit();
  572.    // 5) 首单
  573.    CheckFirstEntry();
  574.    // 6) 同向加仓(可能被停止)
  575.    CheckAddPositions();
  576.    // 7) 对冲:当主网格浮亏达到余额20%触发(分段加码)
  577.    CheckHedge();
  578.    return(0);
  579. }
复制代码
ScreenShot_2026-04-27_201346_905.png ScreenShot_2026-04-27_201401_985.png
举报

评论 使用道具

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