//+------------------------------------------------------------------+ //| Universal_Trading_EA.mq5 | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include //=== TRADING LEVELS === input group "=== TRADING LEVELS ===" input double EntryBuyLevel = 1.3600; // Buy entry level input double TargetLevel = 1.3641; // Target level for buys input double SupportLevel = 1.3566; // Support/resistance level input double SellTarget = 1.3521; // Target level for sells //=== SCENARIO CONTROLS === input group "=== SCENARIO CONTROLS ===" input bool EnableBuyScenario1 = true; // Enable Buy Scenario 1 input bool EnableBuyScenario2 = true; // Enable Buy Scenario 2 input bool EnableSellScenario1 = true; // Enable Sell Scenario 1 input bool EnableSellScenario2 = true; // Enable Sell Scenario 2 //=== REVERSAL TRADING === input group "=== REVERSAL TRADING ===" input bool EnableAutoReversals = true; // Enable automatic reversals input double BuyReversalPips = 35; // Expected pullback pips after buy target input double SellReversalPips = 25; // Expected rebound pips after sell target //=== MACD SETTINGS === input group "=== MACD SETTINGS ===" input int MACD_Fast = 12; // MACD Fast EMA period input int MACD_Slow = 26; // MACD Slow EMA period input int MACD_Signal = 9; // MACD Signal period input double MACD_OversoldThreshold = -0.0005; // MACD oversold threshold input double MACD_OverboughtThreshold = 0.0005; // MACD overbought threshold //=== ENTRY CONDITIONS === input group "=== ENTRY CONDITIONS ===" input double EntryTolerancePips = 5; // Price tolerance for entry (pips) input double LevelTestTolerancePips = 3; // Price tolerance for level tests (pips) input double BreakoutTolerancePips = 2; // Price tolerance for breakouts (pips) input int TestTimeoutHours = 4; // Hours between level tests input int LevelResetPips = 20; // Pips away to reset test counters //=== RISK MANAGEMENT === input group "=== RISK MANAGEMENT ===" input double LotSize = 0.1; // Fixed lot size (0 = auto calculation) input double RiskPercent = 2.0; // Risk per trade as % of account input double MaxDailyLoss = 5.0; // Maximum daily loss % input double MaxSpread = 3.0; // Maximum allowed spread in points input double StopLossPips = 30; // Stop loss in pips (0 = based on levels) //=== GENERAL SETTINGS === input group "=== GENERAL SETTINGS ===" input int MagicNumber = 123456; // Magic number for orders input string TradeComment = "Universal EA"; // Trade comment prefix // Global variables CTrade trade; int macdHandle; double macdMain[], macdSignal[]; int testCount_Support = 0; int testCount_Entry = 0; datetime lastTestTime_Support = 0; datetime lastTestTime_Entry = 0; bool inLongPosition = false; bool inShortPosition = false; double dailyStartBalance; datetime dailyStartTime; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Validate inputs if(!ValidateInputs()) return INIT_PARAMETERS_INCORRECT; // Initialize MACD indicator macdHandle = iMACD(_Symbol, PERIOD_CURRENT, MACD_Fast, MACD_Slow, MACD_Signal, PRICE_CLOSE); if(macdHandle == INVALID_HANDLE) { Print("Error creating MACD indicator for ", _Symbol); return INIT_FAILED; } // Set array properties ArraySetAsSeries(macdMain, true); ArraySetAsSeries(macdSignal, true); // Set trade parameters trade.SetExpertMagicNumber(MagicNumber); trade.SetDeviationInPoints(10); // Initialize daily tracking dailyStartBalance = AccountInfoDouble(ACCOUNT_BALANCE); dailyStartTime = TimeCurrent(); // Print configuration PrintConfiguration(); Print("Universal Trading EA initialized successfully for ", _Symbol); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Validate input parameters | //+------------------------------------------------------------------+ bool ValidateInputs() { if(EntryBuyLevel <= 0 || TargetLevel <= 0 || SupportLevel <= 0 || SellTarget <= 0) { Print("Error: All price levels must be positive"); return false; } if(TargetLevel <= EntryBuyLevel) { Print("Error: Target level must be above entry buy level"); return false; } if(SupportLevel >= EntryBuyLevel) { Print("Error: Support level must be below entry buy level"); return false; } if(SellTarget >= SupportLevel) { Print("Error: Sell target must be below support level"); return false; } if(RiskPercent <= 0 || RiskPercent > 10) { Print("Error: Risk percent must be between 0 and 10"); return false; } return true; } //+------------------------------------------------------------------+ //| Print EA configuration | //+------------------------------------------------------------------+ void PrintConfiguration() { Print("=== UNIVERSAL TRADING EA CONFIGURATION ==="); Print("Instrument: ", _Symbol); Print("Entry Buy Level: ", EntryBuyLevel); Print("Target Level: ", TargetLevel); Print("Support Level: ", SupportLevel); Print("Sell Target: ", SellTarget); Print("Buy Reversal Pips: ", BuyReversalPips); Print("Sell Reversal Pips: ", SellReversalPips); Print("MACD Oversold: ", MACD_OversoldThreshold); Print("MACD Overbought: ", MACD_OverboughtThreshold); Print("Risk per Trade: ", RiskPercent, "%"); Print("========================================="); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { IndicatorRelease(macdHandle); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Check daily risk management if(!CheckDailyRiskManagement()) return; // Check spread if(!CheckSpread()) return; // Update MACD values if(!UpdateMACDValues()) return; // Update position status UpdatePositionStatus(); // Check trading scenarios if(EnableBuyScenario1) CheckBuyScenario1(); if(EnableBuyScenario2) CheckBuyScenario2(); if(EnableSellScenario1) CheckSellScenario1(); if(EnableSellScenario2) CheckSellScenario2(); // Check exit conditions CheckExitConditions(); } //+------------------------------------------------------------------+ //| Check daily risk management | //+------------------------------------------------------------------+ bool CheckDailyRiskManagement() { // Reset daily tracking at new day MqlDateTime timeStruct; TimeToStruct(TimeCurrent(), timeStruct); MqlDateTime startStruct; TimeToStruct(dailyStartTime, startStruct); if(timeStruct.day != startStruct.day) { dailyStartBalance = AccountInfoDouble(ACCOUNT_BALANCE); dailyStartTime = TimeCurrent(); Print("New trading day started. Daily balance reset for ", _Symbol); } // Check if daily loss limit exceeded double currentBalance = AccountInfoDouble(ACCOUNT_BALANCE); double dailyLoss = (dailyStartBalance - currentBalance) / dailyStartBalance * 100; if(dailyLoss >= MaxDailyLoss) { Print("Daily loss limit exceeded: ", dailyLoss, "%. Trading stopped for ", _Symbol); return false; } return true; } //+------------------------------------------------------------------+ //| Check spread | //+------------------------------------------------------------------+ bool CheckSpread() { double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); double spread = (ask - bid) / point; return spread <= MaxSpread; } //+------------------------------------------------------------------+ //| Calculate lot size based on risk management | //+------------------------------------------------------------------+ double CalculateLotSize(double entryPrice, double stopLoss) { // Use fixed lot size if specified if(LotSize > 0) return LotSize; double balance = AccountInfoDouble(ACCOUNT_BALANCE); double riskAmount = balance * RiskPercent / 100; double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); double stopLossPoints = MathAbs(entryPrice - stopLoss) / point; if(stopLossPoints == 0) return minLot; double lotSize = riskAmount / (stopLossPoints * tickValue); // Round to nearest lot step lotSize = MathFloor(lotSize / lotStep) * lotStep; // Apply limits if(lotSize < minLot) lotSize = minLot; if(lotSize > maxLot) lotSize = maxLot; return lotSize; } //+------------------------------------------------------------------+ //| Calculate stop loss level | //+------------------------------------------------------------------+ double CalculateStopLoss(bool isBuy, double entryPrice) { double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); if(StopLossPips > 0) { // Use fixed stop loss pips if(isBuy) return entryPrice - StopLossPips * point; else return entryPrice + StopLossPips * point; } else { // Use level-based stop loss if(isBuy) return SupportLevel - 5 * point; else return EntryBuyLevel + 5 * point; } } //+------------------------------------------------------------------+ //| Update MACD indicator values | //+------------------------------------------------------------------+ bool UpdateMACDValues() { if(CopyBuffer(macdHandle, 0, 0, 3, macdMain) < 0 || CopyBuffer(macdHandle, 1, 0, 3, macdSignal) < 0) { Print("Error copying MACD buffers for ", _Symbol); return false; } return true; } //+------------------------------------------------------------------+ //| Update position status | //+------------------------------------------------------------------+ void UpdatePositionStatus() { inLongPosition = false; inShortPosition = false; for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber) { if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) inLongPosition = true; else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) inShortPosition = true; } } } //+------------------------------------------------------------------+ //| Check Buy Scenario 1: Buy at entry level with MACD above zero | //+------------------------------------------------------------------+ void CheckBuyScenario1() { if(inLongPosition) return; double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); // Check if price is near entry level if(MathAbs(ask - EntryBuyLevel) <= EntryTolerancePips * point) { // Check MACD conditions: above zero and rising (starting to rise) if(macdMain[0] > 0 && macdMain[0] > macdMain[1]) { double stopLoss = CalculateStopLoss(true, ask); double lotSize = CalculateLotSize(ask, stopLoss); // Execute buy order if(trade.Buy(lotSize, _Symbol, ask, stopLoss, TargetLevel, TradeComment + " - Buy Scenario 1")) { Print("Buy Scenario 1 executed for ", _Symbol, " at ", ask, " Lot: ", lotSize, " MACD: ", macdMain[0], " (rising from ", macdMain[1], ")"); } } } } //+------------------------------------------------------------------+ //| Check Buy Scenario 2: Two tests of support with MACD oversold | //+------------------------------------------------------------------+ void CheckBuyScenario2() { if(inLongPosition) return; double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); // Check if price is testing support level if(MathAbs(bid - SupportLevel) <= LevelTestTolerancePips * point) { // Check if this is a new test if(TimeCurrent() - lastTestTime_Support > TestTimeoutHours * 3600) { testCount_Support++; lastTestTime_Support = TimeCurrent(); Print("Test #", testCount_Support, " of support level detected for ", _Symbol); // If second test and MACD is oversold if(testCount_Support >= 2 && macdMain[0] < MACD_OversoldThreshold) { double stopLoss = CalculateStopLoss(true, ask); double lotSize = CalculateLotSize(ask, stopLoss); // Execute buy order if(trade.Buy(lotSize, _Symbol, ask, stopLoss, TargetLevel, TradeComment + " - Buy Scenario 2")) { Print("Buy Scenario 2 executed for ", _Symbol, " at ", ask, " Lot: ", lotSize, " MACD oversold: ", macdMain[0], " (threshold: ", MACD_OversoldThreshold, ")"); testCount_Support = 0; // Reset counter } } } } // Reset counter if price moves away from level if(MathAbs(bid - SupportLevel) > LevelResetPips * point) { testCount_Support = 0; } } //+------------------------------------------------------------------+ //| Check Sell Scenario 1: Sell below support with MACD below zero | //+------------------------------------------------------------------+ void CheckSellScenario1() { if(inShortPosition) return; double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); // Check if price breaks below support level if(bid < SupportLevel - BreakoutTolerancePips * point) { // Check MACD conditions: below zero and declining (starting to decline) if(macdMain[0] < 0 && macdMain[0] < macdMain[1]) { double stopLoss = CalculateStopLoss(false, bid); double lotSize = CalculateLotSize(bid, stopLoss); // Execute sell order if(trade.Sell(lotSize, _Symbol, bid, stopLoss, SellTarget, TradeComment + " - Sell Scenario 1")) { Print("Sell Scenario 1 executed for ", _Symbol, " at ", bid, " Lot: ", lotSize, " MACD: ", macdMain[0], " (declining from ", macdMain[1], ")"); } } } } //+------------------------------------------------------------------+ //| Check Sell Scenario 2: Two tests of entry with MACD overbought | //+------------------------------------------------------------------+ void CheckSellScenario2() { if(inShortPosition) return; double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); // Check if price is testing entry level if(MathAbs(ask - EntryBuyLevel) <= LevelTestTolerancePips * point) { // Check if this is a new test if(TimeCurrent() - lastTestTime_Entry > TestTimeoutHours * 3600) { testCount_Entry++; lastTestTime_Entry = TimeCurrent(); Print("Test #", testCount_Entry, " of entry level detected for ", _Symbol); // If second test and MACD is overbought if(testCount_Entry >= 2 && macdMain[0] > MACD_OverboughtThreshold) { double stopLoss = CalculateStopLoss(false, bid); double lotSize = CalculateLotSize(bid, stopLoss); // Execute sell order if(trade.Sell(lotSize, _Symbol, bid, stopLoss, SellTarget, TradeComment + " - Sell Scenario 2")) { Print("Sell Scenario 2 executed for ", _Symbol, " at ", bid, " Lot: ", lotSize, " MACD overbought: ", macdMain[0], " (threshold: ", MACD_OverboughtThreshold, ")"); testCount_Entry = 0; // Reset counter } } } } // Reset counter if price moves away from level if(MathAbs(ask - EntryBuyLevel) > LevelResetPips * point) { testCount_Entry = 0; } } //+------------------------------------------------------------------+ //| Check exit conditions and automatic reversals | //+------------------------------------------------------------------+ void CheckExitConditions() { double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber) { ulong ticket = PositionGetInteger(POSITION_TICKET); double currentPrice = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ? bid : ask; // Check long position exit at target if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { if(currentPrice >= TargetLevel) { if(trade.PositionClose(ticket)) { Print("Long position closed at target for ", _Symbol, ": ", currentPrice); // Open automatic reversal if enabled if(EnableAutoReversals && BuyReversalPips > 0) { double reversalTarget = TargetLevel - BuyReversalPips * point; double stopLoss = TargetLevel + 15 * point; double lotSize = CalculateLotSize(bid, stopLoss); if(trade.Sell(lotSize, _Symbol, bid, stopLoss, reversalTarget, TradeComment + " - Auto Reversal Short")) { Print("Auto reversal short opened for ", _Symbol, " expecting ", BuyReversalPips, " pip pullback"); } } } } } // Check short position exit at target else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { if(currentPrice <= SellTarget) { if(trade.PositionClose(ticket)) { Print("Short position closed at target for ", _Symbol, ": ", currentPrice); // Open automatic reversal if enabled if(EnableAutoReversals && SellReversalPips > 0) { double reversalTarget = SellTarget + SellReversalPips * point; double stopLoss = SellTarget - 15 * point; double lotSize = CalculateLotSize(ask, stopLoss); if(trade.Buy(lotSize, _Symbol, ask, stopLoss, reversalTarget, TradeComment + " - Auto Reversal Long")) { Print("Auto reversal long opened for ", _Symbol, " expecting ", SellReversalPips, " pip rebound"); } } } } } } } } //+------------------------------------------------------------------+ //| Trade transaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { if(trans.type == TRADE_TRANSACTION_DEAL_ADD) { Print("Trade executed: ", trans.symbol, " Volume: ", trans.volume, " Price: ", trans.price, " Type: ", (trans.deal_type == DEAL_TYPE_BUY ? "BUY" : "SELL")); } }