//+------------------------------------------------------------------+ //| MyProEA.mq5 - Professional Expert Advisor | //| Main EA file with clean lifecycle and separation of concerns | //+------------------------------------------------------------------+ #property copyright "Educational EA - BackTesting Only" #property link "https://www.mql5.com" #property version "1.00" #property strict #property description "Professional modular EA with MA crossover strategy" #property description "For educational and backtesting purposes only" // Include all class definitions #include "Include/Config.mqh" #include "Include/Logger.mqh" #include "Include/Utilities.mqh" #include "Include/MarketData.mqh" #include "Include/Signal.mqh" #include "Include/Strategy.mqh" #include "Include/RiskManager.mqh" #include "Include/PositionManager.mqh" #include "Include/TradeManager.mqh" #include "Include/TrailingStop.mqh" //+------------------------------------------------------------------+ // Global class instances //+------------------------------------------------------------------+ CLogger *g_logger = NULL; CMarketData *g_market_data = NULL; CStrategy *g_strategy = NULL; CRiskManager *g_risk_manager = NULL; CPositionManager *g_position_manager = NULL; CTradeManager *g_trade_manager = NULL; CTrailingStop *g_trailing_stop = NULL; // Diagnostics counters for blocked entries long g_blocked_existing_position = 0; long g_blocked_spread = 0; long g_blocked_hours = 0; long g_blocked_max_positions = 0; //+------------------------------------------------------------------+ // EA Initialization //+------------------------------------------------------------------+ int OnInit() { // Create logger instance g_logger = new CLogger(g_debug_mode); g_logger.Info("===== EA INITIALIZATION START ====="); // Create market data handler g_market_data = new CMarketData(_Symbol, g_logger); if(!g_market_data.IsTradingAllowed()) { g_logger.Error("Trading not allowed for this symbol"); delete g_market_data; g_market_data = NULL; delete g_logger; g_logger = NULL; return INIT_FAILED; } // Create strategy g_strategy = new CStrategy(g_market_data, g_logger); if(!g_strategy.Init()) { g_logger.Error("Failed to initialize strategy"); delete g_strategy; g_strategy = NULL; delete g_market_data; g_market_data = NULL; delete g_logger; g_logger = NULL; return INIT_FAILED; } // Create risk manager g_risk_manager = new CRiskManager(g_market_data, g_logger); // Create position manager g_position_manager = new CPositionManager(g_logger); // Create trade manager g_trade_manager = new CTradeManager(g_logger, g_risk_manager); // Create trailing stop manager g_trailing_stop = new CTrailingStop(g_market_data, g_logger, g_trade_manager); // Log configuration g_logger.Info(StringFormat("Magic Number: %d", g_magic_number)); g_logger.Info(StringFormat("Symbol: %s", _Symbol)); g_logger.Info(StringFormat("Chart Timeframe: %s", EnumToString(PERIOD_CURRENT))); g_logger.Info(StringFormat("Strategy Evaluation: %s on %s", g_evaluate_every_tick ? "Every tick" : (g_evaluate_on_new_bar ? "New bar" : "Every tick"), EnumToString(g_strategy_entry_timeframe))); g_logger.Info(StringFormat("Lot Mode: %s", g_lot_mode == LOT_MODE_FIXED ? "FIXED" : "RISK")); g_logger.Info("Initial SL/TP source: Strategy TradeSetup"); g_logger.Info(StringFormat("Max Positions: %d, Max Spread: %d points", g_max_positions, g_max_spread_points)); g_logger.Info("===== EA INITIALIZATION COMPLETE ====="); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ // EA Deinitialization //+------------------------------------------------------------------+ void OnDeinit(const int reason) { g_logger.Info("===== EA DEINITIALIZATION START ====="); string reason_text = ""; switch(reason) { case REASON_ACCOUNT: reason_text = "Account changed"; break; case REASON_CHARTCHANGE: reason_text = "Chart changed"; break; case REASON_CHARTCLOSE: reason_text = "Chart closed"; break; case REASON_PARAMETERS: reason_text = "Parameters changed"; break; case REASON_RECOMPILE: reason_text = "EA recompiled"; break; case REASON_REMOVE: reason_text = "EA removed"; break; default: reason_text = "Unknown reason"; break; } g_logger.Info(StringFormat("Deinit reason: %s (%d)", reason_text, reason)); // Clean up strategy (release indicator handles) if(g_strategy != NULL) { delete g_strategy; g_strategy = NULL; } // Clean up all other objects if(g_trailing_stop != NULL) { delete g_trailing_stop; g_trailing_stop = NULL; } if(g_trade_manager != NULL) { delete g_trade_manager; g_trade_manager = NULL; } if(g_position_manager != NULL) { delete g_position_manager; g_position_manager = NULL; } if(g_risk_manager != NULL) { delete g_risk_manager; g_risk_manager = NULL; } if(g_market_data != NULL) { delete g_market_data; g_market_data = NULL; } if(g_logger != NULL) { g_logger.Info("===== EA DEINITIALIZATION COMPLETE ====="); delete g_logger; g_logger = NULL; } } //+------------------------------------------------------------------+ // Main EA Logic - OnTick() //+------------------------------------------------------------------+ void OnTick() { // Step 1: Update market data (always) if(!g_market_data.IsTradingAllowed()) { return; } // Step 2: Check spread - if spread is too wide, don't trade if(!g_risk_manager.IsSpreadAcceptable()) { g_blocked_spread++; if(g_debug_mode) g_logger.Info(StringFormat("Blocked entry due to spread (count=%d)", g_blocked_spread)); return; } // Step 3: Check trading hours if(!g_risk_manager.IsTradingHourValid()) { g_blocked_hours++; if(g_debug_mode) g_logger.Info(StringFormat("Blocked entry due to trading hours (count=%d)", g_blocked_hours)); return; } // Step 4: Manage existing positions (trailing stop, break-even) g_trailing_stop.UpdateAllPositions(); // Step 5: Evaluate strategy on configured entry timeframe unless every-tick mode is enabled bool should_evaluate_strategy = true; if(!g_evaluate_every_tick && g_evaluate_on_new_bar) should_evaluate_strategy = g_market_data.IsNewBar(g_strategy_entry_timeframe); if(!should_evaluate_strategy) { return; } // Log diagnostics on each new entry bar if(g_strategy != NULL) g_strategy.LogDiagnostics(); if(g_logger != NULL && g_debug_mode) g_logger.Info(StringFormat("Blocked totals: existing=%d spread=%d hours=%d maxpos=%d", g_blocked_existing_position, g_blocked_spread, g_blocked_hours, g_blocked_max_positions)); // Step 6: Get the trade setup from strategy TradeSetup setup = g_strategy.GetTradeSetup(); if(setup.signal == SIGNAL_NONE) { if(g_debug_mode && g_logger != NULL) g_logger.Info(StringFormat("No trade setup: %s", setup.reason)); return; } // Step 7: Check if we already have a position if(g_position_manager.HasOpenPosition(_Symbol)) { g_blocked_existing_position++; if(g_debug_mode) g_logger.Info(StringFormat("Already have open position, skipping entry (count=%d)", g_blocked_existing_position)); return; } // Step 8: Check if new position is allowed if(!g_position_manager.IsNewPositionAllowed(_Symbol)) { g_blocked_max_positions++; g_logger.Warning(StringFormat("New position not allowed (max positions reached) (count=%d)", g_blocked_max_positions)); return; } double lot = 0.0; if(!g_risk_manager.ValidateTradeSetup(setup, lot)) { if(g_logger) g_logger.Warning(StringFormat("Trade setup rejected: %s", setup.reason)); return; } bool trade_success = false; if(setup.signal == SIGNAL_BUY) { trade_success = g_trade_manager.OpenBuyTrade(_Symbol, lot, setup.stopLoss, setup.takeProfit, setup.entryPrice); } else if(setup.signal == SIGNAL_SELL) { trade_success = g_trade_manager.OpenSellTrade(_Symbol, lot, setup.stopLoss, setup.takeProfit, setup.entryPrice); } if(trade_success) { if(g_logger) g_logger.Info("Trade executed successfully"); } else { if(g_logger) g_logger.Error("Trade execution failed"); } }