//+------------------------------------------------------------------+ //| GoldXAU_Scalper_Full.mq5 | //| Gold-only scalper EA with multi-timeframe bias and trailing TP | //+------------------------------------------------------------------+ #property copyright "Assistant" #property version "1.12" #property strict #include CTrade trade; //---------------------- User inputs -------------------------------- input string TradeSymbol = "XAUUSD"; // symbol (change if your broker uses a different name) input double FixedLotSize = 0.01; // fixed lot size used when UseFixedLot=true input bool UseFixedLot = true; // always trade fixed lot (true) or dynamic sizing (false) input double RiskPercent = 0.5; // if UseFixedLot=false -> use this % of balance as risk per trade // timeframe and indicator params input ENUM_TIMEFRAMES TF = PERIOD_M5; // entry TF input int SMA_Fast = 5; input int SMA_Slow = 10; input int SMA_Trend = 50; input int ATR_Period = 14; input double ATR_Multiplier = 0.9; // SL = ATR * multiplier input double RewardRatio = 1.2; // TP = SL * RewardRatio // scalper vs normal input bool ScalperMode = false; // if true use smaller TP/SL multipliers for quick scalps input double Scalper_RR = 0.9; // R:R for scalper (tp = rr * sl) input int Scalper_MaxTrades = 2; // allow more small trades if scalper // Trailing & rally behavior input bool UseTrailingTP = true; input double TrailingStartATRMult = 1.0; // start trailing once price is this * ATR in profit input double TrailingPercent = 1.0; // percent behind market to place trailing TP (1.0 means TP = price*(1 - 0.01) for longs) input double TrailingStepPoints = 0.5; // minimum price change to update TP (in price units) // multi-timeframe bias input bool UseMultiTFBias = true; input ENUM_TIMEFRAMES BiasTF1 = PERIOD_H1; input ENUM_TIMEFRAMES BiasTF2 = PERIOD_H4; input ENUM_TIMEFRAMES BiasTF3 = PERIOD_D1; input int BiasSMA_Period = 50; // SMA for bias check (on each bias TF) input int BiasRequiredMajority = 2; // how many TFs must agree to consider bullish/bearish // trade direction enum ENUM_TRADE_DIR {DIR_BOTH=0, DIR_LONG_ONLY=1, DIR_SHORT_ONLY=2}; input ENUM_TRADE_DIR AllowedDirection = DIR_BOTH; // safety input int MagicNumber = 20251023; input int MaxOpenTrades = 3; input double MinFreeMarginBufferUSD = 5.0; // require at least this free margin (USD) to open trades // misc input int SlippagePoints = 200; // allowed slippage in points for order send // NEW: Enhanced Risk Management input double SpreadBufferMultiplier = 0.8; // add this * spread to SL for buffer input double SlippageBufferPoints = 2.0; // add this many points to SL for slippage buffer input int MaxDailyTrades = 5; // maximum trades per day input int MinCooldownMinutes = 30; // minimum minutes between trades input bool UseTimeFilters = true; // skip trading during news/gaps input int SkipStartHour = 22; // skip trading from this hour (GMT) input int SkipEndHour = 2; // until this hour (GMT) input bool UseBreakEven = true; // move SL to breakeven at +0.5R profit input double BreakEvenRMultiple = 0.5; // move SL to BE at this R profit input bool UsePartialTP = false; // close half position at smaller TP input double PartialTPRMultiple = 0.6; // close half at this R profit input double TrailingATRMultiple = 0.25; // trail TP at this ATR distance //---------------------- globals ------------------------------------ double pointSz = 0.0; int symbolDigits = 0; // NEW: Enhanced tracking variables datetime lastTradeTime = 0; int dailyTradeCount = 0; datetime lastDailyReset = 0; //---------------------- helper declarations ------------------------- double GetATR(const string symbol, ENUM_TIMEFRAMES tf, int period, int shift); double GetSMA(const string symbol, ENUM_TIMEFRAMES tf, int period, int shift); int CountMyPositions(const string symbol); void PrintSymbolInfo(const string symbol); bool IsMarketBiasBullish(); bool IsMarketBiasBearish(); double CalcLotByRisk_USD(const string symbol, double riskUSD, double entryPrice, double slPrice); double NormalizeLotToStep(const string symbol, double lot); bool CanOpenMoreTrades(); void ManageTrailing(); ulong OpenBuy(double lot, double sl, double tp); ulong OpenSell(double lot, double sl, double tp); double GetPriceAsk(const string symbol); double GetPriceBid(const string symbol); // NEW: Enhanced helper functions bool IsTimeToTrade(); bool CanTradeNow(); double CalculateSpreadBuffer(); double CalculateSlippageBuffer(); void ManageBreakEven(); void ManagePartialTP(); void ResetDailyCounter(); //+------------------------------------------------------------------+ //| Expert initialization | //+------------------------------------------------------------------+ int OnInit() { // ensure symbol exists and select it if(!SymbolSelect(TradeSymbol,true)) { Print("ERROR: cannot select symbol ", TradeSymbol); return(INIT_FAILED); } pointSz = SymbolInfoDouble(TradeSymbol,SYMBOL_POINT); symbolDigits = (int)SymbolInfoInteger(TradeSymbol,SYMBOL_DIGITS); if(pointSz<=0) pointSz = _Point; PrintFormat("EA init symbol=%s point=%g symbolDigits=%d", TradeSymbol, pointSz, symbolDigits); // print symbol trade properties for verification PrintSymbolInfo(TradeSymbol); // safety: ensure AllowedDirection valid (remove modification of input parameter) // AllowedDirection is an input parameter and cannot be modified if(AllowedDirectionDIR_SHORT_ONLY) { Print("WARNING: Invalid AllowedDirection value. Using DIR_BOTH as default."); } Print("XAU Scalp EA initialized. Magic=", MagicNumber); // Initialize enhanced tracking lastTradeTime = 0; dailyTradeCount = 0; lastDailyReset = TimeCurrent(); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinit | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { Print("EA deinitialized, reason=", reason); } //+------------------------------------------------------------------+ //| OnTick | //+------------------------------------------------------------------+ void OnTick() { // Reset daily counter if new day ResetDailyCounter(); // Check if we can trade now (time filters, cooldown, daily limits) if(!CanTradeNow()) return; // Ensure we have enough market data MqlRates rates[]; int copied = CopyRates(TradeSymbol,TF,0,3,rates); // need only last 2-3 bars if(copied<3) return; // Calculate indicators on closed bar (shift=1) double smaFastPrev = GetSMA(TradeSymbol,TF,SMA_Fast,1); double smaSlowPrev = GetSMA(TradeSymbol,TF,SMA_Slow,1); double smaTrendPrev = GetSMA(TradeSymbol,TF,SMA_Trend,1); double atrPrev = GetATR(TradeSymbol,TF,ATR_Period,1); // Validate indicators if(smaFastPrev <= 0 || smaSlowPrev <= 0 || smaTrendPrev <= 0 || atrPrev <= 0) { Print("Invalid indicator values - skipping tick"); return; } // entry conditions bool trendUp = (iClose(TradeSymbol,TF,1) > smaTrendPrev); bool trendDown = (iClose(TradeSymbol,TF,1) < smaTrendPrev); // detect cross on closed bar double smaFastPrev2 = GetSMA(TradeSymbol,TF,SMA_Fast,2); double smaSlowPrev2 = GetSMA(TradeSymbol,TF,SMA_Slow,2); bool crossUp = (smaFastPrev > smaSlowPrev) && (smaFastPrev2 <= smaSlowPrev2); bool crossDown = (smaFastPrev < smaSlowPrev) && (smaFastPrev2 >= smaSlowPrev2); // multi TF bias check bool biasBullish = true, biasBearish = true; if(UseMultiTFBias) { biasBullish = IsMarketBiasBullish(); // majority H1/H4/D1 bullish? biasBearish = IsMarketBiasBearish(); } // Count my EA positions on symbol int myPositions = CountMyPositions(TradeSymbol); // trailing modify for existing positions if(myPositions>0 && UseTrailingTP) ManageTrailing(); // NEW: Manage break-even and partial TP if(myPositions>0) { if(UseBreakEven) ManageBreakEven(); if(UsePartialTP) ManagePartialTP(); } // STOP: limit trades if(!CanOpenMoreTrades()) return; // Decide to open long? bool wantLong = false, wantShort = false; if(AllowedDirection!=DIR_SHORT_ONLY) { // only consider long side if allowed if(trendUp && crossUp) { if(!UseMultiTFBias || biasBullish) wantLong = true; } } if(AllowedDirection!=DIR_LONG_ONLY) { // consider short side if allowed if(trendDown && crossDown) { if(!UseMultiTFBias || biasBearish) wantShort = true; } } // Prepare order params if needed if(wantLong || wantShort) { double ask = GetPriceAsk(TradeSymbol); double bid = GetPriceBid(TradeSymbol); if(ask<=0 || bid<=0) return; double slPrice=0, tpPrice=0; double lot = FixedLotSize; if(!UseFixedLot) { // calculate lot by risk (risk = RiskPercent% of balance) double balanceNow = AccountInfoDouble(ACCOUNT_BALANCE); double riskUSD = balanceNow * (RiskPercent/100.0); // estimate SL using ATR double slAmount = atrPrev * ATR_Multiplier; if(ScalperMode) slAmount = atrPrev * (ATR_Multiplier*0.7); if(wantLong) slPrice = ask - slAmount; else slPrice = bid + slAmount; lot = CalcLotByRisk_USD(TradeSymbol, riskUSD, (wantLong?ask:bid), slPrice); lot = NormalizeLotToStep(TradeSymbol, lot); } else { // Use fixed lot, but ensure normalized to broker step lot = NormalizeLotToStep(TradeSymbol, FixedLotSize); } // final safety lot clamp double minLot = SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_MAX); if(minLot==0) minLot = 0.01; if(maxLot==0) maxLot = 100; if(lot < minLot) lot = minLot; if(lot > maxLot) lot = maxLot; // compute TP & SL if not already set if(!UseFixedLot) { /* slPrice already computed */ } else { // compute using ATR for fixed-lot mode double slAmount = atrPrev * ATR_Multiplier; if(ScalperMode) slAmount = atrPrev * (ATR_Multiplier*0.7); // NEW: Add spread and slippage buffers double spreadBuffer = CalculateSpreadBuffer(); double slippageBuffer = CalculateSlippageBuffer(); slAmount += spreadBuffer + slippageBuffer; if(wantLong) slPrice = ask - slAmount; else slPrice = bid + slAmount; } double rr = RewardRatio; if(ScalperMode) rr = Scalper_RR; if(wantLong) { tpPrice = ask + (ask - slPrice) * rr; } else { tpPrice = bid - (slPrice - bid) * rr; } // Validate SL/TP distances if(wantLong && (slPrice >= ask || tpPrice <= ask)) { Print("Invalid SL/TP for long trade"); return; } if(wantShort && (slPrice <= bid || tpPrice >= bid)) { Print("Invalid SL/TP for short trade"); return; } // final safety checks: free margin double freeMargin = AccountInfoDouble(ACCOUNT_MARGIN_FREE); if(freeMargin < MinFreeMarginBufferUSD) { Print("Not enough free margin to open trades. FreeMargin=", freeMargin); return; } // Open the trade(s) trade.SetExpertMagicNumber(MagicNumber); trade.SetDeviationInPoints(SlippagePoints); if(wantLong) { ulong ticket = OpenBuy(lot, slPrice, tpPrice); if(ticket>0) { PrintFormat("Opened BUY ticket=%I64u lot=%.2f sl=%.3f tp=%.3f", ticket, lot, slPrice, tpPrice); lastTradeTime = TimeCurrent(); dailyTradeCount++; } else PrintFormat("Buy open failed, err=%d", GetLastError()); } if(wantShort) { ulong ticket = OpenSell(lot, slPrice, tpPrice); if(ticket>0) { PrintFormat("Opened SELL ticket=%I64u lot=%.2f sl=%.3f tp=%.3f", ticket, lot, slPrice, tpPrice); lastTradeTime = TimeCurrent(); dailyTradeCount++; } else PrintFormat("Sell open failed, err=%d", GetLastError()); } } // end wantLong/wantShort } //+------------------------------------------------------------------+ //| Helpers: indicator wrappers | //+------------------------------------------------------------------+ double GetATR(const string symbol, ENUM_TIMEFRAMES tf, int period, int shift) { int handle = iATR(symbol, tf, period); if(handle == INVALID_HANDLE) return(0); double buffer[]; if(CopyBuffer(handle, 0, shift, 1, buffer) <= 0) return(0); return(buffer[0]); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double GetSMA(const string symbol, ENUM_TIMEFRAMES tf, int period, int shift) { int handle = iMA(symbol, tf, period, 0, MODE_SMA, PRICE_CLOSE); if(handle == INVALID_HANDLE) return(0); double buffer[]; if(CopyBuffer(handle, 0, shift, 1, buffer) <= 0) return(0); return(buffer[0]); } //+------------------------------------------------------------------+ //| Count positions opened by this EA | //+------------------------------------------------------------------+ int CountMyPositions(const string symbol) { int count = 0; for(int i=0; i h1SMA) bullishCount++; // Check H4 bias double h4SMA = GetSMA(TradeSymbol, BiasTF2, BiasSMA_Period, 1); double h4Close = iClose(TradeSymbol, BiasTF2, 1); if(h4Close > h4SMA) bullishCount++; // Check D1 bias double d1SMA = GetSMA(TradeSymbol, BiasTF3, BiasSMA_Period, 1); double d1Close = iClose(TradeSymbol, BiasTF3, 1); if(d1Close > d1SMA) bullishCount++; return(bullishCount >= BiasRequiredMajority); } //+------------------------------------------------------------------+ //| Check if market bias is bearish across multiple timeframes | //+------------------------------------------------------------------+ bool IsMarketBiasBearish() { int bearishCount = 0; // Check H1 bias double h1SMA = GetSMA(TradeSymbol, BiasTF1, BiasSMA_Period, 1); double h1Close = iClose(TradeSymbol, BiasTF1, 1); if(h1Close < h1SMA) bearishCount++; // Check H4 bias double h4SMA = GetSMA(TradeSymbol, BiasTF2, BiasSMA_Period, 1); double h4Close = iClose(TradeSymbol, BiasTF2, 1); if(h4Close < h4SMA) bearishCount++; // Check D1 bias double d1SMA = GetSMA(TradeSymbol, BiasTF3, BiasSMA_Period, 1); double d1Close = iClose(TradeSymbol, BiasTF3, 1); if(d1Close < d1SMA) bearishCount++; return(bearishCount >= BiasRequiredMajority); } //+------------------------------------------------------------------+ //| Calculate lot size based on risk in USD | //+------------------------------------------------------------------+ double CalcLotByRisk_USD(const string symbol, double riskUSD, double entryPrice, double slPrice) { double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE); double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE); if(tickValue <= 0 || tickSize <= 0) { Print("Invalid tick value/size for ", symbol); return(FixedLotSize); } double slDistance = MathAbs(entryPrice - slPrice); double slInTicks = slDistance / tickSize; double lossPerLot = slInTicks * tickValue; if(lossPerLot <= 0) { Print("Invalid loss per lot calculation"); return(FixedLotSize); } double lot = riskUSD / lossPerLot; return(lot); } //+------------------------------------------------------------------+ //| Normalize lot size to broker step | //+------------------------------------------------------------------+ double NormalizeLotToStep(const string symbol, double lot) { double step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); if(step <= 0) step = 0.01; double normalized = MathFloor(lot / step) * step; return(normalized); } //+------------------------------------------------------------------+ //| Check if we can open more trades | //+------------------------------------------------------------------+ bool CanOpenMoreTrades() { int currentTrades = CountMyPositions(TradeSymbol); int maxTrades = ScalperMode ? Scalper_MaxTrades : MaxOpenTrades; if(currentTrades >= maxTrades) { PrintFormat("Max trades reached: %d/%d", currentTrades, maxTrades); return(false); } return(true); } //+------------------------------------------------------------------+ //| Manage trailing take profit for open positions | //+------------------------------------------------------------------+ void ManageTrailing() { for(int i=0; i= trailingStart) { double newTP = 0; double trailingDistance = atr * TrailingATRMultiple; if(posType == POSITION_TYPE_BUY) { // NEW: ATR-based trailing instead of percentage newTP = currentPrice - trailingDistance; if(newTP > posTP + TrailingStepPoints) { trade.PositionModify(ticket, posSL, NormalizeDouble(newTP, symbolDigits)); PrintFormat("Trailing TP updated for BUY ticket %I64u: %.3f -> %.3f (ATR-based)", ticket, posTP, newTP); } } else { // NEW: ATR-based trailing instead of percentage newTP = currentPrice + trailingDistance; if(newTP < posTP - TrailingStepPoints) { trade.PositionModify(ticket, posSL, NormalizeDouble(newTP, symbolDigits)); PrintFormat("Trailing TP updated for SELL ticket %I64u: %.3f -> %.3f (ATR-based)", ticket, posTP, newTP); } } } } } } } //+------------------------------------------------------------------+ //| Open buy order | //+------------------------------------------------------------------+ ulong OpenBuy(double lot, double sl, double tp) { double ask = SymbolInfoDouble(TradeSymbol, SYMBOL_ASK); if(ask <= 0) return(0); bool result = trade.Buy(lot, TradeSymbol, ask, NormalizeDouble(sl, symbolDigits), NormalizeDouble(tp, symbolDigits), "XAU Scalper Buy"); return(result ? trade.ResultOrder() : 0); } //+------------------------------------------------------------------+ //| Open sell order | //+------------------------------------------------------------------+ ulong OpenSell(double lot, double sl, double tp) { double bid = SymbolInfoDouble(TradeSymbol, SYMBOL_BID); if(bid <= 0) return(0); bool result = trade.Sell(lot, TradeSymbol, bid, NormalizeDouble(sl, symbolDigits), NormalizeDouble(tp, symbolDigits), "XAU Scalper Sell"); return(result ? trade.ResultOrder() : 0); } //+------------------------------------------------------------------+ //| Get current ask price | //+------------------------------------------------------------------+ double GetPriceAsk(const string symbol) { return(SymbolInfoDouble(symbol, SYMBOL_ASK)); } //+------------------------------------------------------------------+ //| Get current bid price | //+------------------------------------------------------------------+ double GetPriceBid(const string symbol) { return(SymbolInfoDouble(symbol, SYMBOL_BID)); } //+------------------------------------------------------------------+ //| NEW: Check if it's time to trade (time filters) | //+------------------------------------------------------------------+ bool IsTimeToTrade() { if(!UseTimeFilters) return(true); MqlDateTime dt; TimeToStruct(TimeCurrent(), dt); // Skip trading during specified hours (e.g., 22:00-02:00 GMT for gaps) if(SkipStartHour > SkipEndHour) { // Cross midnight (e.g., 22:00 to 02:00) if(dt.hour >= SkipStartHour || dt.hour < SkipEndHour) return(false); } else { // Same day (e.g., 10:00 to 15:00) if(dt.hour >= SkipStartHour && dt.hour < SkipEndHour) return(false); } return(true); } //+------------------------------------------------------------------+ //| NEW: Check if we can trade now (all conditions) | //+------------------------------------------------------------------+ bool CanTradeNow() { // Check time filters if(!IsTimeToTrade()) { Print("Skipping trade - outside trading hours"); return(false); } // Check daily trade limit if(dailyTradeCount >= MaxDailyTrades) { PrintFormat("Daily trade limit reached: %d/%d", dailyTradeCount, MaxDailyTrades); return(false); } // Check cooldown period if(lastTradeTime > 0) { int minutesSinceLastTrade = (int)((TimeCurrent() - lastTradeTime) / 60); if(minutesSinceLastTrade < MinCooldownMinutes) { PrintFormat("Cooldown period active: %d/%d minutes", minutesSinceLastTrade, MinCooldownMinutes); return(false); } } return(true); } //+------------------------------------------------------------------+ //| NEW: Calculate spread buffer for SL | //+------------------------------------------------------------------+ double CalculateSpreadBuffer() { double ask = SymbolInfoDouble(TradeSymbol, SYMBOL_ASK); double bid = SymbolInfoDouble(TradeSymbol, SYMBOL_BID); double spread = ask - bid; return(spread * SpreadBufferMultiplier); } //+------------------------------------------------------------------+ //| NEW: Calculate slippage buffer for SL | //+------------------------------------------------------------------+ double CalculateSlippageBuffer() { return(SlippageBufferPoints * pointSz); } //+------------------------------------------------------------------+ //| NEW: Manage break-even functionality | //+------------------------------------------------------------------+ void ManageBreakEven() { for(int i=0; i 0) { double rMultiple = profit / slDistance; // Move SL to breakeven + small buffer if(rMultiple >= BreakEvenRMultiple && posSL != posPrice) { double newSL = posPrice + (posType == POSITION_TYPE_BUY ? pointSz * 2 : -pointSz * 2); trade.PositionModify(ticket, NormalizeDouble(newSL, symbolDigits), posTP); PrintFormat("Moved SL to breakeven for ticket %I64u: %.3f -> %.3f", ticket, posSL, newSL); } } } } } } //+------------------------------------------------------------------+ //| NEW: Manage partial take profit | //+------------------------------------------------------------------+ void ManagePartialTP() { for(int i=0; i 0) { double rMultiple = profit / slDistance; // Close half position at partial TP if(rMultiple >= PartialTPRMultiple && posVolume > 0.02) // Only if volume > 0.02 { double closeVolume = posVolume * 0.5; // Close half if(trade.PositionClosePartial(ticket, closeVolume)) { PrintFormat("Partial TP executed for ticket %I64u: closed %.2f lots at %.3f", ticket, closeVolume, currentPrice); } } } } } } } //+------------------------------------------------------------------+ //| NEW: Reset daily trade counter | //+------------------------------------------------------------------+ void ResetDailyCounter() { MqlDateTime currentDT, lastDT; TimeToStruct(TimeCurrent(), currentDT); TimeToStruct(lastDailyReset, lastDT); // If new day, reset counter if(currentDT.day != lastDT.day || currentDT.mon != lastDT.mon || currentDT.year != lastDT.year) { dailyTradeCount = 0; lastDailyReset = TimeCurrent(); Print("Daily trade counter reset - new day"); } } //+------------------------------------------------------------------+