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:
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:
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 .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:
GridTargetProfit in pips. This allows the entire grid to close for a collective profit.GridTrailStartPips, GridTrailDistPips) can be activated to lock in profits on the basket of orders as the market moves favorably.- 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
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.