//+------------------------------------------------------------------+ //| EURUSD Trend Pullback EA (Clean Architecture) | //| SRP - Meaningful Names - No Logic In OnTick | //| Author: Bell CW | //+------------------------------------------------------------------+ #property strict #include //================================================================== // INPUTS //================================================================== input ENUM_MA_METHOD TrendMaType = MODE_EMA; input ENUM_MA_METHOD EntryMaType = MODE_EMA; input int TrendFastMAPeriod = 100; input int TrendSlowMAPeriod = 200; input int EntryMAPeriod = 50; input ENUM_TIMEFRAMES TrendTimeframe = PERIOD_H4; input ENUM_TIMEFRAMES EntryTimeframe = PERIOD_M15; input double RiskPercent = 1.0; input double RewardRisk = 1.0; input int TradeStartHour = 14; // VN time input int TradeEndHour = 22; input int SlippagePoints = 5; input int MagicNumber = 26092025; //================================================================== // GLOBALS //================================================================== CTrade trade; int hTrendFastMA; int hTrendSlowMA; int hEntryMA; //================================================================== // INIT //================================================================== int OnInit() { InitIndicators(); return INIT_SUCCEEDED; } void InitIndicators() { hTrendFastMA = iMA(_Symbol, TrendTimeframe, TrendFastMAPeriod, 0, TrendMaType, PRICE_CLOSE); hTrendSlowMA = iMA(_Symbol, TrendTimeframe, TrendSlowMAPeriod, 0, TrendMaType, PRICE_CLOSE); hEntryMA = iMA(_Symbol, EntryTimeframe, EntryMAPeriod, 0, EntryMaType, PRICE_CLOSE); } bool IsNewEntryBarClosed() { static datetime lastBarTime = 0; datetime currentBarTime = iTime(_Symbol, EntryTimeframe, 0); if(currentBarTime != lastBarTime) { lastBarTime = currentBarTime; return true; // nến trước vừa đóng } return false; } //================================================================== // ON TICK (NO LOGIC HERE) //================================================================== void OnTick() { if(!IsNewEntryBarClosed()) return; RunTradingFlow(); } //================================================================== // MAIN FLOW //================================================================== void RunTradingFlow() { if(!IsTradeSessionAllowed()) return; if(HasOpenPosition()) return; int trendDirection = DetermineTrendDirection(); if(trendDirection == 0) return; if(!IsPricePullbackToEntryMA(trendDirection)) return; if(!IsEngulfingPattern(trendDirection)) return; ExecuteMarketTrade(trendDirection); } //================================================================== // SESSION //================================================================== bool IsTradeSessionAllowed() { datetime now = TimeCurrent(); MqlDateTime dt; TimeToStruct(now, dt); return (dt.hour >= TradeStartHour && dt.hour <= TradeEndHour); } //================================================================== // POSITION //================================================================== bool HasOpenPosition() { return PositionSelect(_Symbol); } //================================================================== // TREND //================================================================== int DetermineTrendDirection() { double fastMA = GetMAValue(hTrendFastMA, TrendTimeframe, 1); double slowMA = GetMAValue(hTrendSlowMA, TrendTimeframe, 1); if(fastMA > slowMA) return 1; if(fastMA < slowMA) return -1; return 0; } //================================================================== // ENTRY CONDITIONS //================================================================== bool IsPricePullbackToEntryMA(int direction) { double price = iClose(_Symbol, EntryTimeframe, 1); double ma = GetMAValue(hEntryMA, EntryTimeframe, 1); if(direction == 1 && price <= ma) return true; if(direction == -1 && price >= ma) return true; return false; } bool IsEngulfingPattern(int direction) { double open1 = iOpen (_Symbol, EntryTimeframe, 1); double close1 = iClose(_Symbol, EntryTimeframe, 1); double open2 = iOpen (_Symbol, EntryTimeframe, 2); double close2 = iClose(_Symbol, EntryTimeframe, 2); if(direction == 1) return (close1 > open1 && open1 < close2 && close1 > open2); if(direction == -1) return (close1 < open1 && open1 > close2 && close1 < open2); return false; } //================================================================== // TRADE EXECUTION //================================================================== void ExecuteMarketTrade(int direction) { double entryPrice = (direction == 1) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID); double stopLoss = (direction == 1) ? iLow (_Symbol, EntryTimeframe, 1) : iHigh(_Symbol, EntryTimeframe, 1); double takeProfit = (direction == 1) ? entryPrice + (entryPrice - stopLoss) * RewardRisk : entryPrice - (stopLoss - entryPrice) * RewardRisk; double slPoints = MathAbs(entryPrice - stopLoss) / _Point; double lotSize = CalculateLotSize(slPoints); trade.SetExpertMagicNumber(MagicNumber); trade.SetDeviationInPoints(SlippagePoints); if(direction == 1) trade.Buy(lotSize, _Symbol, entryPrice, stopLoss, takeProfit); else trade.Sell(lotSize, _Symbol, entryPrice, stopLoss, takeProfit); } //================================================================== // UTILITIES //================================================================== double GetMAValue(int handle, ENUM_TIMEFRAMES tf, int shift) { double buffer[]; if(CopyBuffer(handle, 0, shift, 1, buffer) <= 0) return 0.0; return buffer[0]; } double CalculateLotSize(double slPoints) { double balance = AccountInfoDouble(ACCOUNT_BALANCE); double riskMoney = balance * RiskPercent / 100.0; double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); double lot = riskMoney / (slPoints * tickValue / tickSize); double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); return MathMax(minLot, MathMin(lot, maxLot)); }