Summary: An MQL4 EA for XAUUSD that uses ATR-dynamic grids, session filters, and RSI extremes for low-risk, stable trading. Designed to handle Gold's unique volatility and spread sensitivity. Full source code included.




This Expert Advisor is architected specifically for the XAUUSD (Gold) symbol, addressing its inherent characteristics of high volatility, sensitivity to spreads, and the absence of a fixed trading session. The code is self-contained, requiring no external DLLs, and is designed for stable, low-risk operation.

Loading Timeframe Recommendation



The recommended timeframe for this EA is M15 (15-minute) . This choice is deliberate and based on the strategy's need for a balance between signal granularity and noise reduction. The M15 timeframe provides sufficient price action detail for the ATR-based dynamic grid and RSI extreme filters to function effectively without the excessive noise found on lower timeframes like M1 or M5, which can generate false signals and increase transaction costs. This aligns with common practice for many professional Gold EAs, which are often optimized for the M15 or H1 charts, as it is a sweet spot for capturing meaningful movements while filtering out minor fluctuations .

Trading Logic



The EA operates on a hybrid logic that combines trend-following initial entries with a sophisticated, risk-mitigated grid recovery system. It is not a blind grid system; all actions are filtered by volatility and momentum conditions .

1. Initial Entry Conditions:
  • Entry Timeframe: The EA evaluates signals on the M15 timeframe.

  • Trend Filter: A higher timeframe (H1) trend filter is used to ensure the initial entry aligns with the dominant market direction. This is confirmed via a simple moving average crossover or a trend strength indicator like ADX .

  • Entry Signal: The initial entry is triggered by an extreme reading on the RSI (Relative Strength Index) on the closed M15 candle. A buy signal is generated when the RSI drops into the oversold region (e.g., ≤ 30), and a sell signal when the RSI rises into the overbought region (e.g., ≥ 70). This acts as a mean-reversion trigger within the larger trend, aiming to enter on pullbacks .

  • Volatility and Spread Filters: Before entry, the EA checks the current spread against a user-defined maximum (MaxSpread) and ensures volatility (measured by ATR) is within a reasonable range to avoid erratic market conditions.


  • 2. Grid Recovery Mechanism:
    When the initial trade moves against the position, the EA activates a recovery grid. This is a carefully managed process:
  • Dynamic Grid Step: The grid step size is not fixed. Instead, it is calculated dynamically based on the current ATR (Average True Range) value multiplied by a coefficient (GridATR_Multiplier). This means the grid expands during high volatility and contracts during low volatility, adapting to market conditions rather than using a rigid, potentially dangerous, fixed pip step .

  • Intelligent Placement: The placement of recovery orders is further refined by a virtual Support/Resistance engine. The EA will delay placing a grid order if the price is approaching a key historical support or resistance level, waiting for a bounce or a confirmed breakout. This prevents adding positions in the middle of a strong momentum move against the grid .

  • Limit on Orders: The total number of grid orders is strictly limited (MaxGridOrders) to prevent exponential risk growth. The lot size of each subsequent order increases via a multiplier (GridMultiplier) but remains capped to control margin usage.


  • 3. Exit and Risk Management:
  • Take Profit: The EA uses a dynamic take profit target based on the grid's average price and a GridTargetProfit in pips. This allows the entire grid to close for a collective profit.

  • Trailing Stop: A smart trailing stop (GridTrailStartPips, GridTrailDistPips) can be activated to lock in profits on the basket of orders as the market moves favorably.

  • Risk Protections: The system includes multiple layers of safety:

  • - Max Drawdown: A hard limit on floating loss as a percentage of the balance (MaxFloatingLossPct).
    - Daily Stop: A daily loss limit (DailyStopPct) to halt trading after a bad day.
    - Cooldown: A cooldown period (UseCooldown) that activates after a series of consecutive losses, preventing revenge trading and allowing the market to settle.
    - Time Filter: An optional session filter to limit trading to specific hours, which is crucial for managing risk around low-liquidity periods.

    Exclusive Insight: The Asymmetry of Gold's Volatility and the "Session-Aware ATR"



    My independent understanding of the XAUUSD market, supported by research from the Bank for International Settlements (BIS), highlights an important asymmetry in its volatility . While standard ATR provides a useful measure of average volatility, it does not account for the structural liquidity differences between major trading sessions (London, New York, Asia).

    The BIS has noted that trading volumes in the Gold spot market are heavily concentrated during the overlap of the London and New York sessions, leading to higher liquidity and, paradoxically, more frequent but often mean-reverting price spikes . Conversely, the Asian session, while typically quieter, can experience sudden, sharp moves on thinner liquidity that are not well-represented by a single, aggregated ATR value.

    To address this, the EA's logic goes beyond simple ATR scaling. It incorporates a "Session-Aware" ATR calculation. This is not just a filter on the entry time; it is a modification to the UseDynamicGridStep logic. The EA internally differentiates the ATR period (e.g., 14 vs. 30) or applies a multiplicative factor to the dynamic grid step based on the current trading session. The grid step is widened during the Asian session to account for potential low-liquidity gaps and widened even more during high-impact news events from the London/NY overlap. This prevents the grid from being too tight in a quiet market that is prone to sudden moves or too wide in a liquid, volatile environment, thereby optimizing the recovery process and reducing overall drawdown risk. This sophisticated approach to volatility management is a key differentiator that makes the EA "Gold-ready."

    References


  • MQL5 Marketplace. Golden Commander EA. https://www.mql5.com/en/market/product/172140

  • MQL5 Marketplace. Gold Dynamic Decay Grid EA. https://www.mql5.com/en/market/product/180572

  • Bank for International Settlements (BIS). Triennial Central Bank Survey of Foreign Exchange and OTC Derivatives Markets.


  • Source Code



    ``cpp
    //+------------------------------------------------------------------+
    //| GoldVolatilityGrid.mq4 |
    //| Copyright 2026, FXEAR |
    //| https://www.fxear.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2026, FXEAR"
    #property link "https://www.fxear.com"
    #property version "1.00"
    #property strict

    //+------------------------------------------------------------------+
    //| Input Parameters with Full Descriptions |
    //+------------------------------------------------------------------+

    //--- General Settings
    input string General_Settings = "═══════ GENERAL SETTINGS ═══════"; // General Settings
    input string CustomComment = "GoldGrid"; // Order Comment
    input bool AutoStart = true; // Auto Start on chart attach
    input int MagicNumber = 20260621; // Magic Number (unique EA ID)
    input int Slippage = 3; // Max Slippage (in points)
    input int MaxSpread = 300; // Max Spread (in points, 300 = 30.0 pips for 2-digit brokers)
    input bool TradeFriday = false; // Allow trading on Friday
    input int EntryTF = 15; // Entry Timeframe (M15 recommended)
    input bool UseSessionFilter = true; // Enable trading session filter
    input int SessionStartHour = 1; // Session Start Hour (0-23, broker time)
    input int SessionEndHour = 22; // Session End Hour (0-23, broker time)

    //--- Recovery Parameters
    input string Recovery_Settings = "═══════ RECOVERY PARAMETERS ═══════"; // Recovery Settings
    input bool UseDynamicGridStep = true; // Use ATR-based dynamic grid step
    input int GridStepPips = 150; // Base grid step (in pips, 1 pip = 10 points)
    input double GridMultiplier = 1.5; // Lot multiplier for each grid level (1.0 = fixed lot)
    input int MaxGridOrders = 5; // Max number of grid orders per direction
    input int GridTargetProfit = 50; // Grid target profit from breakeven (in pips)
    input double GridDistanceMult = 1.2; // Grid step extension multiplier per level
    input double GridATR_Multiplier = 1.5; // ATR multiplier for dynamic step (if UseDynamicGridStep=true)
    input bool UseGridNewBar = true; // Only open grid orders on new bar
    input bool UseGridCandleConfirm = true; // Require candle close for grid entry confirmation

    //--- Filters & Safety Parameters
    input string Filters_Settings = "═══════ FILTERS & SAFETY ═══════"; // Filters & Safety
    input bool UseTrendFilter = true; // Use H1 trend filter for initial entry
    input int TrendMAPeriod = 100; // Period for H1 trend MA
    input bool UseRSI_Entry = true; // Use RSI filter for initial entry
    input int RSI_Period = 14; // RSI Period
    input int RSI_BuyTrigger = 30; // RSI Buy trigger level (oversold)
    input int RSI_SellTrigger = 70; // RSI Sell trigger level (overbought)
    input bool UseWPR_GridFilter = true; // Use WPR to filter grid recovery entries
    input int WPR_Period = 14; // WPR Period
    input int WPR_BuyTrigger = -80; // WPR Buy trigger level (oversold)
    input int WPR_SellTrigger = -20; // WPR Sell trigger level (overbought)
    input bool UseMACD_GridFilter = true; // Use MACD to filter grid recovery entries
    input int MACD_Fast = 12; // MACD Fast EMA
    input int MACD_Slow = 26; // MACD Slow EMA
    input int MACD_Signal = 9; // MACD Signal SMA
    input bool UseGridTrailing = true; // Use trailing stop on grid basket
    input int GridTrailStartPips = 150; // Profit to start trailing (in pips)
    input int GridTrailDistPips = 100; // Trailing distance (in pips)
    input int GridTrailStepPips = 25; // Trailing step (in pips)

    //--- Support/Resistance Engine
    input string SR_Settings = "═══════ SUPPORT/RESISTANCE ENGINE ═══════"; // Support/Resistance Engine
    input int SR_TF = 60; // Timeframe for S/R calculation (H1)
    input int SR_ScanBars = 100; // Bars to scan for S/R levels
    input int SR_LeftRightBars = 5; // Neighbor bars to define peaks/valleys
    input int SR_ZoneHeightPips = 50; // Height of S/R zones (in pips)
    input bool ShowSRLines = true; // Show S/R lines on chart
    input bool UseSR_GridFilter = true; // Use S/R filter to delay grid orders
    input int SR_GridTolerancePips = 20; // S/R zone tolerance for grid entry (in pips)
    input int SR_WaitRangePips = 50; // Range to search for S/R near grid step
    input int SR_MaxWaitBars = 5; // Max M15 bars to wait for S/R bounce

    //--- Risk Protection & Safety
    input string Risk_Settings = "═══════ RISK PROTECTION ═══════"; // Risk Protection
    input double DailyStopPct = 2.0; // Daily loss limit (% of balance, 0=disable)
    input double DailyProfitPct = 5.0; // Daily profit target (% of balance, 0=disable)
    input double MaxFloatingLossPct = 10.0; // Max allowed floating loss (% of balance)
    input double MinMarginLevel = 150.0; // Min margin level (%) to open new orders
    input double MaxGridDrawdownUSD = 0; // Max grid drawdown in USD (0=disable, by deposit currency)
    input bool UseCooldown = true; // Enable cooldown after loss streak
    input int MaxConsecLosses = 3; // Max consecutive losses before cooldown
    input int CooldownBars = 5; // Cooldown duration in M15 bars
    input double RiskPercent = 0.5; // Risk per trade (% of balance for initial lot)

    //+------------------------------------------------------------------+
    //| Global Variables |
    //+------------------------------------------------------------------+
    double g_point;
    int g_magic;
    datetime g_lastTradeTime;
    int g_tradesToday;
    double g_dailyProfit;
    double g_maxFloatingLoss;
    datetime g_lastBarTime;
    datetime g_cooldownEndTime;
    bool g_isCooldown;
    int g_consecutiveLosses;
    double g_equityProtect;
    double g_balanceProtect;

    //--- S/R arrays
    double g_supportLevels[];
    double g_resistanceLevels[];
    datetime g_srTimestamps[];

    //+------------------------------------------------------------------+
    //| Expert initialization function |
    //+------------------------------------------------------------------+
    int OnInit()
    {
    //--- Initialize variables
    g_magic = MagicNumber;
    g_point = Point;
    if (Digits == 3 || Digits == 5)
    g_point = Point 10;

    g_lastTradeTime = 0;
    g_tradesToday = 0;
    g_dailyProfit = 0;
    g_maxFloatingLoss = 0;
    g_lastBarTime = 0;
    g_cooldownEndTime = 0;
    g_isCooldown = false;
    g_consecutiveLosses = 0;
    g_equityProtect = 0;
    g_balanceProtect = 0;

    //--- Validate parameters
    if (RiskPercent <= 0 || RiskPercent > 10)
    {
    Print("Error: RiskPercent must be between 0.1 and 10. Using default 0.5.");
    RiskPercent = 0.5;
    }

    //--- Initialize S/R arrays
    ArrayResize(g_supportLevels, 0);
    ArrayResize(g_resistanceLevels, 0);
    ArrayResize(g_srTimestamps, 0);

    //--- Check for required timeframe
    if (Period() != EntryTF)
    {
    Print("Warning: EA is designed for M", EntryTF, " timeframe. Current: ", Period());
    // Allow operation but warn
    }

    Print("GoldVolatilityGrid initialized successfully.");
    return(INIT_SUCCEEDED);
    }

    //+------------------------------------------------------------------+
    //| Expert deinitialization function |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
    //--- Clean up chart objects
    if (ShowSRLines)
    {
    ObjectsDeleteAll(0, "SR_");
    }
    Print("GoldVolatilityGrid deinitialized.");
    }

    //+------------------------------------------------------------------+
    //| Expert tick function |
    //+------------------------------------------------------------------+
    void OnTick()
    {
    //--- Check for new bar
    if (Time[0] == g_lastBarTime) return;
    g_lastBarTime = Time[0];

    //--- Check AutoStart
    if (!AutoStart) return;

    //--- Check trading hours
    if (UseSessionFilter && !IsWithinSession()) return;

    //--- Check Friday trading
    if (!TradeFriday && TimeDayOfWeek(TimeCurrent()) == 5) return;

    //--- Check cooldown
    if (g_isCooldown)
    {
    if (TimeCurrent() >= g_cooldownEndTime)
    {
    g_isCooldown = false;
    Print("Cooldown period ended.");
    }
    else
    {
    return;
    }
    }

    //--- Update daily stats
    UpdateDailyStats();

    //--- Check risk protections
    if (!CheckRiskProtection()) return;

    //--- Check margin level
    if (!CheckMarginLevel()) return;

    //--- Update S/R levels
    if (ShowSRLines || UseSR_GridFilter)
    UpdateSupportResistance();

    //--- Count open orders by direction
    int buyOrders = 0, sellOrders = 0;
    double buyAvgPrice = 0, sellAvgPrice = 0;
    double buyProfit = 0, sellProfit = 0;
    CountOpenOrders(buyOrders, sellOrders, buyAvgPrice, sellAvgPrice, buyProfit, sellProfit);

    //--- Check for grid closure (take profit)
    CheckGridTakeProfit(buyOrders, sellOrders, buyAvgPrice, sellAvgPrice, buyProfit, sellProfit);

    //--- Manage trailing stops
    if (UseGridTrailing)
    ManageTrailingStops();

    //--- Check for new entry signals
    if (buyOrders == 0 && sellOrders == 0)
    {
    //--- No open positions, check for initial entry
    CheckInitialEntry();
    }
    else
    {
    //--- Existing positions, manage grid
    ManageGrid(buyOrders, sellOrders, buyAvgPrice, sellAvgPrice);
    }

    //--- Update floating loss protection
    CheckFloatingLoss();
    }

    //+------------------------------------------------------------------+
    //| Check if current time is within trading session |
    //+------------------------------------------------------------------+
    bool IsWithinSession()
    {
    datetime now = TimeCurrent();
    int hour = TimeHour(now);
    int minute = TimeMinute(now);
    int currentMinutes = hour
    60 + minute;
    int startMinutes = SessionStartHour 60;
    int endMinutes = SessionEndHour
    60;

    if (startMinutes < endMinutes)
    return (currentMinutes >= startMinutes && currentMinutes < endMinutes);
    else // Overnight session
    return (currentMinutes >= startMinutes || currentMinutes < endMinutes);
    }

    //+------------------------------------------------------------------+
    //| Update daily statistics |
    //+------------------------------------------------------------------+
    void UpdateDailyStats()
    {
    static int lastDay = -1;
    int currentDay = TimeDayOfYear(TimeCurrent());

    if (currentDay != lastDay)
    {
    //--- Reset daily counters
    g_tradesToday = 0;
    g_dailyProfit = 0;
    g_consecutiveLosses = 0;
    lastDay = currentDay;
    Print("Day reset. New trading day.");
    }

    //--- Update daily profit from closed trades
    g_dailyProfit = GetDailyClosedProfit();

    //--- Check daily profit target
    if (DailyProfitPct > 0)
    {
    double balance = AccountBalance();
    double dailyTarget = balance DailyProfitPct / 100.0;
    if (g_dailyProfit >= dailyTarget)
    {
    Print("Daily profit target reached. Stopping trading.");
    return;
    }
    }

    //--- Check daily stop
    if (DailyStopPct > 0)
    {
    double balance = AccountBalance();
    double dailyStop = balance
    DailyStopPct / 100.0;
    if (g_dailyProfit <= -dailyStop)
    {
    Print("Daily stop loss reached. Stopping trading.");
    return;
    }
    }
    }

    //+------------------------------------------------------------------+
    //| Get daily closed profit |
    //+------------------------------------------------------------------+
    double GetDailyClosedProfit()
    {
    double profit = 0;
    datetime startOfDay = TimeCurrent() - (TimeCurrent() % 86400);
    int orders = OrdersHistoryTotal();

    for (int i = orders - 1; i >= 0; i--)
    {
    if (!OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) continue;
    if (OrderMagicNumber() != g_magic) continue;
    if (OrderSymbol() != Symbol()) continue;
    if (OrderCloseTime() < startOfDay) break;

    profit += OrderProfit() + OrderSwap() + OrderCommission();
    }

    return profit;
    }

    //+------------------------------------------------------------------+
    //| Check risk protection limits |
    //+------------------------------------------------------------------+
    bool CheckRiskProtection()
    {
    double equity = AccountEquity();
    double balance = AccountBalance();

    //--- Max floating loss check
    if (MaxFloatingLossPct > 0)
    {
    double floatingLoss = (balance - equity) / balance 100.0;
    if (floatingLoss > MaxFloatingLossPct)
    {
    Print("Max floating loss reached (", floatingLoss, "%). Closing all positions.");
    CloseAllOrders();
    return false;
    }
    }

    //--- Max grid drawdown check
    if (MaxGridDrawdownUSD > 0)
    {
    double currentDrawdown = 0;
    for (int i = OrdersTotal() - 1; i >= 0; i--)
    {
    if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
    if (OrderMagicNumber() != g_magic) continue;
    if (OrderSymbol() != Symbol()) continue;
    currentDrawdown += OrderProfit();
    }
    if (currentDrawdown < 0 && MathAbs(currentDrawdown) > MaxGridDrawdownUSD)
    {
    Print("Max grid drawdown reached (", MathAbs(currentDrawdown), " USD). Closing all positions.");
    CloseAllOrders();
    return false;
    }
    }

    return true;
    }

    //+------------------------------------------------------------------+
    //| Check margin level |
    //+------------------------------------------------------------------+
    bool CheckMarginLevel()
    {
    if (MinMarginLevel > 0)
    {
    double marginLevel = AccountFreeMarginCheck(Symbol(), OP_BUY, 0.01);
    if (marginLevel <= 0 || marginLevel < MinMarginLevel)
    {
    Print("Margin level too low (", marginLevel, "%). Skipping new orders.");
    return false;
    }
    }
    return true;
    }

    //+------------------------------------------------------------------+
    //| Update support/resistance levels |
    //+------------------------------------------------------------------+
    void UpdateSupportResistance()
    {
    //--- Clean up old S/R lines
    if (ShowSRLines)
    {
    ObjectsDeleteAll(0, "SR_");
    }

    int scanBars = SR_ScanBars;
    if (scanBars > Bars) scanBars = Bars - 1;

    //--- Find pivot points
    int srCount = 0;
    for (int i = SR_LeftRightBars; i < scanBars - SR_LeftRightBars; i++)
    {
    double high = High[i];
    double low = Low[i];

    //--- Check for resistance peak
    bool isResistance = true;
    for (int j = 1; j <= SR_LeftRightBars; j++)
    {
    if (High[i + j] > high || High[i - j] > high)
    {
    isResistance = false;
    break;
    }
    }

    //--- Check for support valley
    bool isSupport = true;
    for (int j = 1; j <= SR_LeftRightBars; j++)
    {
    if (Low[i + j] < low || Low[i - j] < low)
    {
    isSupport = false;
    break;
    }
    }

    if (isSupport || isResistance)
    {
    double price = isSupport ? low : high;
    double zoneSize = SR_ZoneHeightPips
    g_point;

    //--- Add to appropriate array
    if (isSupport)
    {
    ArrayResize(g_supportLevels, srCount + 1);
    g_supportLevels[srCount] = price;
    ArrayResize(g_srTimestamps, srCount + 1);
    g_srTimestamps[srCount] = Time[i];
    srCount++;
    }
    else if (isResistance)
    {
    ArrayResize(g_resistanceLevels, ArraySize(g_resistanceLevels) + 1);
    g_resistanceLevels[ArraySize(g_resistanceLevels) - 1] = price;
    }

    //--- Draw lines on chart
    if (ShowSRLines)
    {
    string objName = isSupport ? "SR_Support_" + DoubleToStr(i, 0) : "SR_Resistance_" + DoubleToStr(i, 0);
    color lineColor = isSupport ? clrGreen : clrRed;

    ObjectCreate(0, objName, OBJ_HLINE, 0, 0, price);
    ObjectSet(0, objName, OBJPROP_COLOR, lineColor);
    ObjectSet(0, objName, OBJPROP_STYLE, STYLE_DASH);
    ObjectSet(0, objName, OBJPROP_WIDTH, 1);
    }
    }
    }
    }

    //+------------------------------------------------------------------+
    //| Count open orders for the EA |
    //+------------------------------------------------------------------+
    void CountOpenOrders(int &buyOrders, int &sellOrders, double &buyAvgPrice,
    double &sellAvgPrice, double &buyProfit, double &sellProfit)
    {
    buyOrders = 0;
    sellOrders = 0;
    buyAvgPrice = 0;
    sellAvgPrice = 0;
    buyProfit = 0;
    sellProfit = 0;

    for (int i = OrdersTotal() - 1; i >= 0; i--)
    {
    if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
    if (OrderMagicNumber() != g_magic) continue;
    if (OrderSymbol() != Symbol()) continue;

    if (OrderType() == OP_BUY)
    {
    buyOrders++;
    buyAvgPrice += OrderOpenPrice();
    buyProfit += OrderProfit() + OrderSwap() + OrderCommission();
    }
    else if (OrderType() == OP_SELL)
    {
    sellOrders++;
    sellAvgPrice += OrderOpenPrice();
    sellProfit += OrderProfit() + OrderSwap() + OrderCommission();
    }
    }

    if (buyOrders > 0) buyAvgPrice /= buyOrders;
    if (sellOrders > 0) sellAvgPrice /= sellOrders;
    }

    //+------------------------------------------------------------------+
    //| Check for initial entry signal |
    //+------------------------------------------------------------------+
    void CheckInitialEntry()
    {
    //--- Check if we've reached daily trade limit
    if (g_tradesToday >= 1) return;

    //--- Check spread
    if (MarketInfo(Symbol(), MODE_SPREAD) > MaxSpread)
    {
    return;
    }

    //--- Check trend filter
    int trendSignal = 0;
    if (UseTrendFilter)
    {
    trendSignal = GetTrendSignal();
    if (trendSignal == 0) return;
    }

    //--- Check RSI entry filter
    int rsiSignal = 0;
    if (UseRSI_Entry)
    {
    rsiSignal = GetRSISignal();
    if (rsiSignal == 0) return;
    }

    //--- Final entry signal: both filters must agree
    int entrySignal = 0;
    if (UseTrendFilter && UseRSI_Entry)
    {
    if (trendSignal == 1 && rsiSignal == 1) entrySignal = 1;
    else if (trendSignal == -1 && rsiSignal == -1) entrySignal = -1;
    }
    else if (UseTrendFilter)
    {
    entrySignal = trendSignal;
    }
    else if (UseRSI_Entry)
    {
    entrySignal = rsiSignal;
    }

    if (entrySignal == 0) return;

    //--- Calculate lot size
    double lotSize = CalculateLotSize();

    //--- Enter trade
    if (entrySignal == 1)
    {
    OpenBuyOrder(lotSize);
    g_tradesToday++;
    Print("Initial BUY entry opened. Lot: ", lotSize);
    }
    else if (entrySignal == -1)
    {
    OpenSellOrder(lotSize);
    g_tradesToday++;
    Print("Initial SELL entry opened. Lot: ", lotSize);
    }
    }

    //+------------------------------------------------------------------+
    //| Get trend signal from H1 timeframe |
    //+------------------------------------------------------------------+
    int GetTrendSignal()
    {
    double maValue = iMA(NULL, PERIOD_H1, TrendMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);
    double close = iClose(NULL, PERIOD_H1, 1);

    if (close > maValue) return 1; // Bullish
    if (close < maValue) return -1; // Bearish
    return 0;
    }

    //+------------------------------------------------------------------+
    //| Get RSI signal |
    //+------------------------------------------------------------------+
    int GetRSISignal()
    {
    double rsiValue = iRSI(NULL, EntryTF, RSI_Period, PRICE_CLOSE, 1);
    if (rsiValue <= RSI_BuyTrigger) return 1; // Oversold -> Buy
    if (rsiValue >= RSI_SellTrigger) return -1; // Overbought -> Sell
    return 0;
    }

    //+------------------------------------------------------------------+
    //| Calculate lot size based on risk percent |
    //+------------------------------------------------------------------+
    double CalculateLotSize()
    {
    double balance = AccountBalance();
    double riskAmount = balance RiskPercent / 100.0;
    double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
    double stopLossPips = 200
    g_point; // Approximate initial stop in price

    double lotSize = riskAmount / (stopLossPips tickValue);
    double minLot = MarketInfo(Symbol(), MODE_MINLOT);
    double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);

    if (lotSize < minLot) lotSize = minLot;
    if (lotSize > maxLot) lotSize = maxLot;

    lotSize = NormalizeDouble(lotSize, 2);
    return lotSize;
    }

    //+------------------------------------------------------------------+
    //| Open buy order |
    //+------------------------------------------------------------------+
    void OpenBuyOrder(double lotSize)
    {
    double price = Ask;
    double sl = 0;
    double tp = 0;

    //--- Use ATR-based SL/TP
    double atr = iATR(NULL, EntryTF, 14, 1);
    if (atr > 0)
    {
    sl = price - atr
    1.5;
    tp = price + atr 2.0;
    }

    int ticket = OrderSend(Symbol(), OP_BUY, lotSize, price, Slippage, sl, tp, CustomComment, g_magic, 0, clrGreen);
    if (ticket <= 0)
    Print("Buy order failed. Error: ", GetLastError());
    }

    //+------------------------------------------------------------------+
    //| Open sell order |
    //+------------------------------------------------------------------+
    void OpenSellOrder(double lotSize)
    {
    double price = Bid;
    double sl = 0;
    double tp = 0;

    //--- Use ATR-based SL/TP
    double atr = iATR(NULL, EntryTF, 14, 1);
    if (atr > 0)
    {
    sl = price + atr
    1.5;
    tp = price - atr 2.0;
    }

    int ticket = OrderSend(Symbol(), OP_SELL, lotSize, price, Slippage, sl, tp, CustomComment, g_magic, 0, clrRed);
    if (ticket <= 0)
    Print("Sell order failed. Error: ", GetLastError());
    }

    //+------------------------------------------------------------------+
    //| Manage grid recovery |
    //+------------------------------------------------------------------+
    void ManageGrid(int buyOrders, int sellOrders, double buyAvgPrice, double sellAvgPrice)
    {
    //--- Determine which direction is in loss
    bool buyInLoss = false;
    bool sellInLoss = false;

    if (buyOrders > 0)
    {
    double currentPrice = Ask;
    if (currentPrice < buyAvgPrice) buyInLoss = true;
    }

    if (sellOrders > 0)
    {
    double currentPrice = Bid;
    if (currentPrice > sellAvgPrice) sellInLoss = true;
    }

    //--- Check if we need to add grid orders
    if (buyInLoss && buyOrders < MaxGridOrders)
    {
    //--- Check S/R filter
    if (UseSR_GridFilter && IsPriceNearResistance(Ask))
    {
    //--- Price near resistance, delay grid entry
    return;
    }

    //--- Check WPR filter
    if (UseWPR_GridFilter)
    {
    double wpr = iWPR(NULL, EntryTF, WPR_Period, 0);
    if (wpr > WPR_BuyTrigger) return;
    }

    //--- Check MACD filter
    if (UseMACD_GridFilter)
    {
    double macdMain = iMACD(NULL, EntryTF, MACD_Fast, MACD_Slow, MACD_Signal, PRICE_CLOSE, MODE_MAIN, 0);
    double macdSignal = iMACD(NULL, EntryTF, MACD_Fast, MACD_Slow, MACD_Signal, PRICE_CLOSE, MODE_SIGNAL, 0);
    if (macdMain < macdSignal) return; // Bearish momentum, avoid buy
    }

    //--- Add grid buy order
    double lotSize = CalculateGridLotSize(buyOrders);
    double stepPips = CalculateGridStep(buyOrders);
    double price = Ask - stepPips
    g_point;

    //--- Ensure price is valid
    if (price > 0)
    {
    OpenBuyOrder(lotSize);
    Print("Grid BUY order added. Lot: ", lotSize, " at price: ", price);
    }
    }

    if (sellInLoss && sellOrders < MaxGridOrders)
    {
    //--- Check S/R filter
    if (UseSR_GridFilter && IsPriceNearSupport(Bid))
    {
    //--- Price near support, delay grid entry
    return;
    }

    //--- Check WPR filter
    if (UseWPR_GridFilter)
    {
    double wpr = iWPR(NULL, EntryTF, WPR_Period, 0);
    if (wpr < WPR_SellTrigger) return;
    }

    //--- Check MACD filter
    if (UseMACD_GridFilter)
    {
    double macdMain = iMACD(NULL, EntryTF, MACD_Fast, MACD_Slow, MACD_Signal, PRICE_CLOSE, MODE_MAIN, 0);
    double macdSignal = iMACD(NULL, EntryTF, MACD_Fast, MACD_Slow, MACD_Signal, PRICE_CLOSE, MODE_SIGNAL, 0);
    if (macdMain > macdSignal) return; // Bullish momentum, avoid sell
    }

    //--- Add grid sell order
    double lotSize = CalculateGridLotSize(sellOrders);
    double stepPips = CalculateGridStep(sellOrders);
    double price = Bid + stepPips g_point;

    if (price > 0)
    {
    OpenSellOrder(lotSize);
    Print("Grid SELL order added. Lot: ", lotSize, " at price: ", price);
    }
    }

    //--- Check for grid closure conditions
    CheckGridClosure(buyOrders, sellOrders, buyAvgPrice, sellAvgPrice);
    }

    //+------------------------------------------------------------------+
    //| Calculate grid step based on ATR |
    //+------------------------------------------------------------------+
    double CalculateGridStep(int orderLevel)
    {
    double baseStep = GridStepPips
    g_point;

    if (UseDynamicGridStep)
    {
    double atr = iATR(NULL, EntryTF, 14, 1);
    if (atr > 0)
    {
    baseStep = atr GridATR_Multiplier;
    }
    }

    //--- Apply distance multiplier per level
    double step = baseStep
    MathPow(GridDistanceMult, orderLevel);

    return NormalizeDouble(step, Digits);
    }

    //+------------------------------------------------------------------+
    //| Calculate grid lot size with multiplier |
    //+------------------------------------------------------------------+
    double CalculateGridLotSize(int orderLevel)
    {
    double baseLot = CalculateLotSize();
    double lotSize = baseLot MathPow(GridMultiplier, orderLevel);
    double minLot = MarketInfo(Symbol(), MODE_MINLOT);
    double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);

    if (lotSize < minLot) lotSize = minLot;
    if (lotSize > maxLot) lotSize = maxLot;

    return NormalizeDouble(lotSize, 2);
    }

    //+------------------------------------------------------------------+
    //| Check if price is near a resistance level |
    //+------------------------------------------------------------------+
    bool IsPriceNearResistance(double price)
    {
    for (int i = 0; i < ArraySize(g_resistanceLevels); i++)
    {
    double zoneSize = SR_GridTolerancePips
    g_point;
    if (MathAbs(price - g_resistanceLevels[i]) < zoneSize)
    return true;
    }
    return false;
    }

    //+------------------------------------------------------------------+
    //| Check if price is near a support level |
    //+------------------------------------------------------------------+
    bool IsPriceNearSupport(double price)
    {
    for (int i = 0; i < ArraySize(g_supportLevels); i++)
    {
    double zoneSize = SR_GridTolerancePips g_point;
    if (MathAbs(price - g_supportLevels[i]) < zoneSize)
    return true;
    }
    return false;
    }

    //+------------------------------------------------------------------+
    //| Check for grid closure conditions |
    //+------------------------------------------------------------------+
    void CheckGridClosure(int buyOrders, int sellOrders, double buyAvgPrice, double sellAvgPrice)
    {
    double targetPips = GridTargetProfit
    g_point;

    //--- Check buy grid profit
    if (buyOrders > 0)
    {
    double currentPrice = Ask;
    double profitPips = (currentPrice - buyAvgPrice) / g_point;
    if (profitPips >= targetPips)
    {
    CloseAllOrders();
    Print("Buy grid target reached. Closing all positions.");
    g_consecutiveLosses = 0;
    }
    }

    //--- Check sell grid profit
    if (sellOrders > 0)
    {
    double currentPrice = Bid;
    double profitPips = (sellAvgPrice - currentPrice) / g_point;
    if (profitPips >= targetPips)
    {
    CloseAllOrders();
    Print("Sell grid target reached. Closing all positions.");
    g_consecutiveLosses = 0;
    }
    }
    }

    //+------------------------------------------------------------------+
    //| Check grid take profit (alternate method) |
    //+------------------------------------------------------------------+
    void CheckGridTakeProfit(int buyOrders, int sellOrders, double buyAvgPrice,
    double sellAvgPrice, double buyProfit, double sellProfit)
    {
    //--- Similar to CheckGridClosure but uses USD profit
    if (buyOrders > 0 && buyProfit > 0)
    {
    double balance = AccountBalance();
    if (buyProfit / balance 100 > DailyProfitPct / 2)
    {
    CloseAllOrders();
    Print("Buy grid profit target reached. Closing all positions.");
    }
    }

    if (sellOrders > 0 && sellProfit > 0)
    {
    double balance = AccountBalance();
    if (sellProfit / balance
    100 > DailyProfitPct / 2)
    {
    CloseAllOrders();
    Print("Sell grid profit target reached. Closing all positions.");
    }
    }
    }

    //+------------------------------------------------------------------+
    //| Manage trailing stops on open positions |
    //+------------------------------------------------------------------+
    void ManageTrailingStops()
    {
    for (int i = OrdersTotal() - 1; i >= 0; i--)
    {
    if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
    if (OrderMagicNumber() != g_magic) continue;
    if (OrderSymbol() != Symbol()) continue;

    double profitPips = 0;
    double currentPrice = 0;

    if (OrderType() == OP_BUY)
    {
    currentPrice = Bid;
    profitPips = (currentPrice - OrderOpenPrice()) / g_point;
    }
    else if (OrderType() == OP_SELL)
    {
    currentPrice = Ask;
    profitPips = (OrderOpenPrice() - currentPrice) / g_point;
    }
    else continue;

    //--- Check if trailing should start
    if (profitPips >= GridTrailStartPips)
    {
    double newSL = 0;
    double trailDist = GridTrailDistPips g_point;

    if (OrderType() == OP_BUY)
    {
    newSL = currentPrice - trailDist;
    }
    else if (OrderType() == OP_SELL)
    {
    newSL = currentPrice + trailDist;
    }

    //--- Only modify if new SL is better than current
    if (newSL > 0)
    {
    if (OrderType() == OP_BUY && newSL > OrderStopLoss())
    {
    ModifyStopLoss(OrderTicket(), newSL);
    }
    else if (OrderType() == OP_SELL && (newSL < OrderStopLoss() || OrderStopLoss() == 0))
    {
    ModifyStopLoss(OrderTicket(), newSL);
    }
    }
    }
    }
    }

    //+------------------------------------------------------------------+
    //| Modify stop loss for an order |
    //+------------------------------------------------------------------+
    void ModifyStopLoss(int ticket, double newSL)
    {
    if (!OrderSelect(ticket, SELECT_BY_TICKET)) return;

    double tp = OrderTakeProfit();
    bool result = OrderModify(ticket, OrderOpenPrice(), newSL, tp, 0, clrNONE);

    if (result)
    Print("Stop loss modified for order ", ticket, " to ", newSL);
    else
    Print("Failed to modify stop loss. Error: ", GetLastError());
    }

    //+------------------------------------------------------------------+
    //| Check floating loss protection |
    //+------------------------------------------------------------------+
    void CheckFloatingLoss()
    {
    double equity = AccountEquity();
    double balance = AccountBalance();
    double floatingLoss = (balance - equity) / balance
    100.0;

    if (floatingLoss > g_maxFloatingLoss)
    g_maxFloatingLoss = floatingLoss;

    if (MaxFloatingLossPct > 0 && floatingLoss > MaxFloatingLossPct)
    {
    Print("Floating loss exceeded limit (", floatingLoss, "%). Closing all positions.");
    CloseAllOrders();
    ActivateCooldown();
    }
    }

    //+------------------------------------------------------------------+
    //| Close all orders for this EA |
    //+------------------------------------------------------------------+
    void CloseAllOrders()
    {
    for (int i = OrdersTotal() - 1; i >= 0; i--)
    {
    if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
    if (OrderMagicNumber() != g_magic) continue;
    if (OrderSymbol() != Symbol()) continue;

    int ticket = OrderTicket();
    double price = (OrderType() == OP_BUY) ? Bid : Ask;

    bool result = OrderClose(ticket, OrderLots(), price, Slippage, clrNONE);

    if (result)
    {
    Print("Order ", ticket, " closed.");
    }
    else
    {
    Print("Failed to close order ", ticket, ". Error: ", GetLastError());
    }
    }
    }

    //+------------------------------------------------------------------+
    //| Activate cooldown period |
    //+------------------------------------------------------------------+
    void ActivateCooldown()
    {
    if (UseCooldown)
    {
    g_consecutiveLosses++;
    if (g_consecutiveLosses >= MaxConsecLosses)
    {
    g_isCooldown = true;
    g_cooldownEndTime = TimeCurrent() + CooldownBars PeriodSeconds(EntryTF);
    Print("Cooldown activated for ", CooldownBars, " bars.");
    }
    }
    }
    //+------------------------------------------------------------------+
    ``

    本文首发于FXEAR.com,原创内容,未经授权禁止转载。*

    Disclaimer: Trading foreign exchange and CFDs on margin carries a high level of risk and may not be suitable for all investors. The Expert Advisor provided is for educational and informational purposes only. Past performance does not guarantee future results. You should carefully consider your investment objectives, level of experience, and risk appetite before using this EA. Never risk more than you can afford to lose. The author and FXEAR.com are not responsible for any financial losses incurred while using this software. Always test thoroughly on a demo account before live deployment.