//+------------------------------------------------------------------+ //| XAUUSD_M5_Professional_Scalper_V3.mq5 | //| Professional-style XAUUSD M5 scalper with risk controls | //| No Martingale / No Grid / No guaranteed-profit logic | //+------------------------------------------------------------------+ #property strict #property version "3.00" #property description "XAUUSD M5 professional scalper. Test on DEMO before live use." #include CTrade trade; //--------------------------- Inputs --------------------------------- input group "GENERAL" input ulong InpMagic = 26092403; input ENUM_TIMEFRAMES InpTF = PERIOD_M5; input bool InpOnlyGold = true; input bool InpOneTradePerBar = true; input group "RISK" input double InpRiskPercent = 1.00; // risk per trade input double InpMaxDailyLossPercent = 5.00; // closed P/L + floating loss protection input int InpMaxConsecutiveLosses = 3; input int InpMaxPositions = 2; input double InpMaxTotalRiskPercent = 2.00; input group "ENTRY" input int InpFastEMA = 20; input int InpSlowEMA = 50; input int InpRSIPeriod = 14; input double InpBuyRSIMin = 52.0; input double InpBuyRSIMax = 72.0; input double InpSellRSIMin = 28.0; input double InpSellRSIMax = 48.0; input int InpADXPeriod = 14; input double InpMinADX = 18.0; input int InpATRPeriod = 14; input double InpSL_ATR = 1.35; input double InpMinBodyATR = 0.12; input double InpBreakoutATR = 0.05; input group "HIGHER TIMEFRAME FILTER" input bool InpUseHTFFilter = true; input int InpHTFFastEMA = 50; input int InpHTFSlowEMA = 200; input group "TRADE MANAGEMENT" input double InpRR = 2.20; input double InpBreakEvenR = 0.90; input double InpBreakEvenOffsetR = 0.08; input double InpTrailStartR = 1.25; input double InpTrailATR = 1.00; input bool InpPartialClose = true; input double InpPartialAtR = 1.00; input double InpPartialPercent = 50.0; input group "MARKET FILTERS" input int InpMaxSpreadPoints = 70; input int InpMaxSlippagePoints = 30; input bool InpUseSession = true; input int InpSessionStartHour = 7; input int InpSessionEndHour = 22; input int InpCooldownMinutes = 5; input int InpMaxTradesPerDay = 12; input bool InpAvoidFridayLate = true; input int InpFridayStopHour = 19; input group "DASHBOARD" input bool InpShowDashboard = true; //------------------------- Indicator handles ------------------------ int hFastEMA = INVALID_HANDLE; int hSlowEMA = INVALID_HANDLE; int hRSI = INVALID_HANDLE; int hATR = INVALID_HANDLE; int hADX = INVALID_HANDLE; int hHTFFast = INVALID_HANDLE; int hHTFSlow = INVALID_HANDLE; datetime g_lastBar = 0; datetime g_lastEntry = 0; int g_dayOfYear = -1; int g_year = -1; //------------------------- Utility ---------------------------------- bool IsGoldSymbol() { if(!InpOnlyGold) return true; string s = _Symbol; StringToUpper(s); return (StringFind(s,"XAU") >= 0 || StringFind(s,"GOLD") >= 0); } bool GetTick(MqlTick &tick) { return SymbolInfoTick(_Symbol,tick); } double PointValue() { return SymbolInfoDouble(_Symbol,SYMBOL_POINT); } int DigitsValue() { return (int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); } double NormalizePrice(double p) { return NormalizeDouble(p,DigitsValue()); } double NormalizeVolumeDown(double vol) { double minv = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); double maxv = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX); double step = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP); if(step <= 0.0) return 0.0; if(vol > maxv) vol = maxv; double n = MathFloor(vol / step + 1e-9); double out = n * step; if(out < minv) return 0.0; return NormalizeDouble(out,2); } bool GetBufferValue(int handle,int buffer,int shift,double &value) { double a[]; ArraySetAsSeries(a,true); if(CopyBuffer(handle,buffer,shift,1,a) != 1) return false; value = a[0]; return true; } bool GetRates(MqlRates &r1,MqlRates &r2,MqlRates &r3) { MqlRates rates[]; ArraySetAsSeries(rates,true); if(CopyRates(_Symbol,InpTF,0,4,rates) < 4) return false; r1=rates[1]; // completed candle r2=rates[2]; r3=rates[3]; return true; } bool IsNewBar() { datetime t=iTime(_Symbol,InpTF,0); if(t==0) return false; if(t!=g_lastBar) { g_lastBar=t; return true; } return false; } void ResetDayIfNeeded() { MqlDateTime dt; TimeToStruct(TimeCurrent(),dt); if(dt.day_of_year!=g_dayOfYear || dt.year!=g_year) { g_dayOfYear=dt.day_of_year; g_year=dt.year; } } bool InSession() { if(!InpUseSession) return true; MqlDateTime dt; TimeToStruct(TimeCurrent(),dt); int h=dt.hour; if(InpSessionStartHour <= InpSessionEndHour) return (h>=InpSessionStartHour && h=InpSessionStartHour || h=InpFridayStopHour); } double CurrentSpreadPoints() { MqlTick tick; if(!GetTick(tick)) return 999999.0; double pt=PointValue(); if(pt<=0) return 999999.0; return (tick.ask-tick.bid)/pt; } bool SpreadOK() { return CurrentSpreadPoints() <= InpMaxSpreadPoints; } int CountOpenPositions() { int count=0; for(int i=PositionsTotal()-1;i>=0;i--) { ulong ticket=PositionGetTicket(i); if(ticket==0) continue; if(!PositionSelectByTicket(ticket)) continue; if(PositionGetString(POSITION_SYMBOL)!=_Symbol) continue; if((ulong)PositionGetInteger(POSITION_MAGIC)!=InpMagic) continue; count++; } return count; } double CurrentOpenRiskMoney() { double total=0.0; double tickSize=SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE); double tickValue=SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE); if(tickSize<=0 || tickValue<=0) return 0.0; for(int i=PositionsTotal()-1;i>=0;i--) { ulong ticket=PositionGetTicket(i); if(ticket==0) continue; if(!PositionSelectByTicket(ticket)) continue; if(PositionGetString(POSITION_SYMBOL)!=_Symbol) continue; if((ulong)PositionGetInteger(POSITION_MAGIC)!=InpMagic) continue; double open=PositionGetDouble(POSITION_PRICE_OPEN); double sl=PositionGetDouble(POSITION_SL); double vol=PositionGetDouble(POSITION_VOLUME); if(sl<=0 || vol<=0) continue; double dist=MathAbs(open-sl); total += (dist/tickSize)*tickValue*vol; } return total; } datetime DayStart() { MqlDateTime dt; TimeToStruct(TimeCurrent(),dt); dt.hour=0; dt.min=0; dt.sec=0; return StructToTime(dt); } double ClosedProfitToday() { datetime from=DayStart(); datetime to=TimeCurrent(); if(!HistorySelect(from,to)) return 0.0; double result=0.0; int deals=HistoryDealsTotal(); for(int i=0;i=0;i--) { ulong ticket=PositionGetTicket(i); if(ticket==0) continue; if(!PositionSelectByTicket(ticket)) continue; if(PositionGetString(POSITION_SYMBOL)!=_Symbol) continue; if((ulong)PositionGetInteger(POSITION_MAGIC)!=InpMagic) continue; result += PositionGetDouble(POSITION_PROFIT); } return result; } int TradesToday() { datetime from=DayStart(); if(!HistorySelect(from,TimeCurrent())) return 0; int count=0; int deals=HistoryDealsTotal(); for(int i=0;i=0;i--) { ulong ticket=HistoryDealGetTicket(i); if(ticket==0) continue; if(HistoryDealGetString(ticket,DEAL_SYMBOL)!=_Symbol) continue; if((ulong)HistoryDealGetInteger(ticket,DEAL_MAGIC)!=InpMagic) continue; long entry=HistoryDealGetInteger(ticket,DEAL_ENTRY); if(entry!=DEAL_ENTRY_OUT && entry!=DEAL_ENTRY_OUT_BY) continue; double p=HistoryDealGetDouble(ticket,DEAL_PROFIT) +HistoryDealGetDouble(ticket,DEAL_SWAP) +HistoryDealGetDouble(ticket,DEAL_COMMISSION); if(p < -0.01) losses++; else if(p > 0.01) break; } return losses; } bool DailyLossReached() { double balance=AccountInfoDouble(ACCOUNT_BALANCE); if(balance<=0) return true; double closed=ClosedProfitToday(); double floating=FloatingProfit(); double lossMoney=-(closed+floating); double maxLoss=balance*InpMaxDailyLossPercent/100.0; return (lossMoney >= maxLoss); } double CalculateLot(double stopDistancePrice) { if(stopDistancePrice<=0) return 0.0; double balance=AccountInfoDouble(ACCOUNT_BALANCE); double riskMoney=balance*InpRiskPercent/100.0; double tickSize=SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE); double tickValue=SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE_LOSS); if(tickValue<=0) tickValue=SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE); if(tickSize<=0 || tickValue<=0) return 0.0; double lossPerLot=(stopDistancePrice/tickSize)*tickValue; if(lossPerLot<=0) return 0.0; double lots=riskMoney/lossPerLot; return NormalizeVolumeDown(lots); } bool CooldownOK() { if(g_lastEntry==0) return true; return (TimeCurrent()-g_lastEntry >= InpCooldownMinutes*60); } bool HTFTrendOK(bool buy) { if(!InpUseHTFFilter) return true; double fast,slow; if(!GetBufferValue(hHTFFast,0,1,fast)) return false; if(!GetBufferValue(hHTFSlow,0,1,slow)) return false; if(buy) return fast>slow; return fastr1.open; bool bearish=r1.closeemaSlow)) return false; if(!HTFTrendOK(true)) return false; if(!bullish) return false; if(rsiInpBuyRSIMax) return false; if(r1.close <= r2.high + atr*InpBreakoutATR) return false; return true; } else { if(!(emaFastInpSellRSIMax) return false; if(r1.close >= r2.low - atr*InpBreakoutATR) return false; return true; } } bool ModifyPosition(ulong ticket,double sl,double tp) { if(!PositionSelectByTicket(ticket)) return false; string sym=PositionGetString(POSITION_SYMBOL); if(sym!=_Symbol) return false; return trade.PositionModify(ticket,NormalizePrice(sl),NormalizePrice(tp)); } bool PartialDone(ulong ticket) { string key="V3P_"+(string)ticket; return GlobalVariableCheck(key); } void MarkPartialDone(ulong ticket) { string key="V3P_"+(string)ticket; GlobalVariableSet(key,(double)TimeCurrent()); } void ManagePositions() { double atr; if(!GetBufferValue(hATR,0,1,atr)) return; MqlTick tick; if(!GetTick(tick)) return; double pt=PointValue(); int stopsLevel=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); double minStop=stopsLevel*pt; for(int i=PositionsTotal()-1;i>=0;i--) { ulong ticket=PositionGetTicket(i); if(ticket==0) continue; if(!PositionSelectByTicket(ticket)) continue; if(PositionGetString(POSITION_SYMBOL)!=_Symbol) continue; if((ulong)PositionGetInteger(POSITION_MAGIC)!=InpMagic) continue; long type=PositionGetInteger(POSITION_TYPE); double open=PositionGetDouble(POSITION_PRICE_OPEN); double sl=PositionGetDouble(POSITION_SL); double tp=PositionGetDouble(POSITION_TP); double volume=PositionGetDouble(POSITION_VOLUME); if(sl<=0) continue; double current=(type==POSITION_TYPE_BUY ? tick.bid : tick.ask); double initialRisk=MathAbs(open-sl); // If SL has already moved beyond the original area, use a conservative // proxy based on ATR for management. if(initialRisk < pt*2) initialRisk=atr*InpSL_ATR; double profitDist=(type==POSITION_TYPE_BUY ? current-open : open-current); double R=profitDist/initialRisk; // Partial close if(InpPartialClose && !PartialDone(ticket) && R>=InpPartialAtR) { double closeVol=NormalizeVolumeDown(volume*InpPartialPercent/100.0); double minv=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); if(closeVol>=minv && closeVol=InpBreakEvenR) { double beOffset=initialRisk*InpBreakEvenOffsetR; double newSL=(type==POSITION_TYPE_BUY ? open+beOffset : open-beOffset); bool better=(type==POSITION_TYPE_BUY ? (slnewSL)); bool valid=(type==POSITION_TYPE_BUY ? (newSLcurrent+minStop)); if(better && valid) ModifyPosition(ticket,newSL,tp); } // ATR trailing if(R>=InpTrailStartR) { double trail=atr*InpTrailATR; double newSL=(type==POSITION_TYPE_BUY ? current-trail : current+trail); bool better=(type==POSITION_TYPE_BUY ? (newSL>sl) : (newSLcurrent+minStop)); if(better && valid) ModifyPosition(ticket,newSL,tp); } } } bool CanTradeNow() { if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) return false; if(!MQLInfoInteger(MQL_TRADE_ALLOWED)) return false; if(!IsGoldSymbol()) return false; if(!InSession()) return false; if(FridayLate()) return false; if(!SpreadOK()) return false; if(DailyLossReached()) return false; if(ConsecutiveLosses()>=InpMaxConsecutiveLosses) return false; if(TradesToday()>=InpMaxTradesPerDay) return false; if(CountOpenPositions()>=InpMaxPositions) return false; if(!CooldownOK()) return false; double balance=AccountInfoDouble(ACCOUNT_BALANCE); if(balance<=0) return false; double maxRiskMoney=balance*InpMaxTotalRiskPercent/100.0; if(CurrentOpenRiskMoney()>=maxRiskMoney) return false; return true; } void OpenTrade(bool buy,double atr) { MqlTick tick; if(!GetTick(tick)) return; double entry=(buy ? tick.ask : tick.bid); double slDist=atr*InpSL_ATR; double tpDist=slDist*InpRR; int stopsLevel=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); double minStop=stopsLevel*PointValue(); if(slDist0 && tickValue>0) newRisk=(slDist/tickSize)*tickValue*lots; double maxRiskMoney=balance*InpMaxTotalRiskPercent/100.0; if(currentRisk+newRisk>maxRiskMoney+0.01) return; double sl=(buy ? entry-slDist : entry+slDist); double tp=(buy ? entry+tpDist : entry-tpDist); sl=NormalizePrice(sl); tp=NormalizePrice(tp); trade.SetExpertMagicNumber(InpMagic); trade.SetDeviationInPoints(InpMaxSlippagePoints); trade.SetTypeFillingBySymbol(_Symbol); bool ok=false; if(buy) ok=trade.Buy(lots,_Symbol,0.0,sl,tp,"V3_SCALP_BUY"); else ok=trade.Sell(lots,_Symbol,0.0,sl,tp,"V3_SCALP_SELL"); if(ok) g_lastEntry=TimeCurrent(); } string StatusText() { if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) return "TERMINAL TRADE OFF"; if(!MQLInfoInteger(MQL_TRADE_ALLOWED)) return "EA TRADE OFF"; if(!IsGoldSymbol()) return "WRONG SYMBOL"; if(DailyLossReached()) return "DAILY STOP"; if(ConsecutiveLosses()>=InpMaxConsecutiveLosses) return "LOSS STOP"; if(!InSession()) return "OUT OF SESSION"; if(FridayLate()) return "FRIDAY STOP"; if(!SpreadOK()) return "HIGH SPREAD"; if(TradesToday()>=InpMaxTradesPerDay) return "DAILY TRADE LIMIT"; if(CountOpenPositions()>=InpMaxPositions) return "MAX POSITIONS"; return "READY"; } void UpdateDashboard() { if(!InpShowDashboard) return; double balance=AccountInfoDouble(ACCOUNT_BALANCE); double equity=AccountInfoDouble(ACCOUNT_EQUITY); double closed=ClosedProfitToday(); double floating=FloatingProfit(); double daily=closed+floating; double dd=(balance>0 ? MathMax(0.0,(balance-equity)/balance*100.0) : 0.0); string text= "XAUUSD M5 SCALPER V3\n"+ "Status: "+StatusText()+"\n"+ "Balance: $"+DoubleToString(balance,2)+"\n"+ "Equity: $"+DoubleToString(equity,2)+"\n"+ "Daily P/L: $"+DoubleToString(daily,2)+"\n"+ "Drawdown: "+DoubleToString(dd,2)+"%\n"+ "Trades today: "+IntegerToString(TradesToday())+"\n"+ "Open positions: "+IntegerToString(CountOpenPositions())+"\n"+ "Loss streak: "+IntegerToString(ConsecutiveLosses())+"\n"+ "Spread: "+DoubleToString(CurrentSpreadPoints(),1)+" pts"; Comment(text); } //------------------------- Lifecycle -------------------------------- int OnInit() { if(!IsGoldSymbol()) Print("Warning: EA is designed for XAUUSD/GOLD symbols."); hFastEMA=iMA(_Symbol,InpTF,InpFastEMA,0,MODE_EMA,PRICE_CLOSE); hSlowEMA=iMA(_Symbol,InpTF,InpSlowEMA,0,MODE_EMA,PRICE_CLOSE); hRSI=iRSI(_Symbol,InpTF,InpRSIPeriod,PRICE_CLOSE); hATR=iATR(_Symbol,InpTF,InpATRPeriod); hADX=iADX(_Symbol,InpTF,InpADXPeriod); if(InpUseHTFFilter) { hHTFFast=iMA(_Symbol,PERIOD_M15,InpHTFFastEMA,0,MODE_EMA,PRICE_CLOSE); hHTFSlow=iMA(_Symbol,PERIOD_M15,InpHTFSlowEMA,0,MODE_EMA,PRICE_CLOSE); } if(hFastEMA==INVALID_HANDLE || hSlowEMA==INVALID_HANDLE || hRSI==INVALID_HANDLE || hATR==INVALID_HANDLE || hADX==INVALID_HANDLE) { Print("Failed to create indicator handles."); return INIT_FAILED; } if(InpUseHTFFilter && (hHTFFast==INVALID_HANDLE || hHTFSlow==INVALID_HANDLE)) { Print("Failed to create HTF indicator handles."); return INIT_FAILED; } ResetDayIfNeeded(); trade.SetExpertMagicNumber(InpMagic); trade.SetDeviationInPoints(InpMaxSlippagePoints); trade.SetTypeFillingBySymbol(_Symbol); Print("XAUUSD M5 Professional Scalper V3 initialized."); return INIT_SUCCEEDED; } void OnDeinit(const int reason) { if(hFastEMA!=INVALID_HANDLE) IndicatorRelease(hFastEMA); if(hSlowEMA!=INVALID_HANDLE) IndicatorRelease(hSlowEMA); if(hRSI!=INVALID_HANDLE) IndicatorRelease(hRSI); if(hATR!=INVALID_HANDLE) IndicatorRelease(hATR); if(hADX!=INVALID_HANDLE) IndicatorRelease(hADX); if(hHTFFast!=INVALID_HANDLE) IndicatorRelease(hHTFFast); if(hHTFSlow!=INVALID_HANDLE) IndicatorRelease(hHTFSlow); Comment(""); } void OnTick() { ResetDayIfNeeded(); ManagePositions(); UpdateDashboard(); if(!IsNewBar()) return; if(!CanTradeNow()) return; double atr=0.0; if(EntrySignal(true,atr)) { OpenTrade(true,atr); return; } if(EntrySignal(false,atr)) { OpenTrade(false,atr); return; } } //+------------------------------------------------------------------+