//+------------------------------------------------------------------+ //| Risk Management Overlay EA v6.8 | //| Enhanced External Trade Management System | //| Optimized Main Coordinator File | //+------------------------------------------------------------------+ #property copyright "ERMT v6.8 - Enhanced Architecture" #property link "" #property version "6.80" #property description "Institutional-grade risk management and trade optimization" #property strict // Include all modules #include "Modules/DataTypes.mqh" #include "Modules/Utilities.mqh" #include "Modules/RiskManager.mqh" #include "Modules/TechnicalAnalysis.mqh" #include "Modules/ExternalTradeManager.mqh" #include "Modules/TradeManager.mqh" #include "Modules/Dashboard.mqh" #include "Modules/Dashboard_SelfContained.mqh" //+------------------------------------------------------------------+ //| Input Parameters | //+------------------------------------------------------------------+ // Risk Management input group "=== Risk Management ===" input double InpMaxRiskPerTrade = 2.0; // Max Risk Per Trade (%) input double InpMaxDailyLoss = 6.0; // Max Daily Loss (%) input double InpMaxDrawdown = 20.0; // Max Drawdown (%) input int InpMaxPositions = 3; // Max Concurrent Positions input double InpRiskRewardMin = 1.5; // Minimum Risk/Reward Ratio // Position Management input group "=== Position Management ===" input ENUM_POSITION_SIZING InpSizingMethod = SIZING_FIXED_PERCENT; // Position Sizing Method input double InpFixedLotSize = 0.1; // Fixed Lot Size input double InpATRMultiplierSL = 2.0; // ATR Multiplier for SL input double InpATRMultiplierTP = 3.0; // ATR Multiplier for TP input ENUM_TP_METHOD InpTPMethod = TP_FIXED_RR; // Take Profit Method input double InpTPRatio = 2.0; // TP Risk/Reward Ratio // External Trade Management input group "=== External Trade Management ===" input bool InpManageExternal = true; // Manage External Trades input ENUM_MANAGEMENT_MODE InpExternalMode = MODE_STOPS_ONLY; // Management Mode input double InpExternalRiskLimit = 5.0; // External Trade Risk Limit (%) input bool InpApplyTrailingExternal = true; // Apply Trailing to External // Trailing Stop input group "=== Trailing Stop Settings ===" input ENUM_TRAILING_METHOD InpTrailingMethod = TRAIL_ATR; // Trailing Method input double InpTrailStart = 30; // Trail Start (points) input double InpTrailStep = 10; // Trail Step (points) input double InpTrailDistance = 20; // Trail Distance (points) input bool InpMoveToBreakeven = true; // Move to Breakeven input double InpBreakevenTrigger = 20; // Breakeven Trigger (points) input double InpBreakevenOffset = 2; // Breakeven Offset (points) // Trading Settings input group "=== Trading Settings ===" input int InpMagicNumber = 68000; // Magic Number input string InpComment = "ERMT_v6.8"; // Trade Comment input double InpSlippage = 3; // Max Slippage (points) input bool InpTradeOnNewBar = false; // Trade Only on New Bar input bool InpAllowHedging = false; // Allow Hedging input bool InpCloseOnOpposite = true; // Close on Opposite Signal // Technical Indicators input group "=== Technical Settings ===" input int InpATRPeriod = 14; // ATR Period input int InpRSIPeriod = 14; // RSI Period input int InpMAPeriod = 20; // MA Period input ENUM_MA_METHOD InpMAMethod = MODE_SMA; // MA Method input double InpBBDeviation = 2.0; // Bollinger Band Deviation input int InpBBPeriod = 20; // Bollinger Band Period // Trading Hours input group "=== Trading Hours ===" input bool InpUseTradeHours = false; // Use Trading Hours input int InpStartHour = 8; // Start Hour (Server Time) input int InpEndHour = 20; // End Hour (Server Time) input bool InpFridayClose = true; // Close All on Friday input int InpFridayCloseHour = 20; // Friday Close Hour // Dashboard input group "=== Dashboard Settings ===" input bool InpShowDashboard = true; // Show Dashboard input int InpDashboardX = 20; // Dashboard X Position input int InpDashboardY = 50; // Dashboard Y Position input color InpDashboardColor = clrWhite; // Dashboard Text Color input int InpDashboardFontSize = 9; // Dashboard Font Size // Logging input group "=== Logging Settings ===" input ENUM_LOG_LEVEL InpLogLevel = LOG_INFO; // Log Level input bool InpSaveLog = true; // Save Log to File input bool InpEmailAlerts = false; // Send Email Alerts input bool InpPushAlerts = false; // Send Push Notifications //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ // Core components CUtilities* g_utils; CRiskManager* g_risk; CTechnicalAnalysis* g_tech; CExternalTradeManager* g_external; CTradeManager* g_trade; CDashboard* g_dashboard; CDashboardSC* Dashboard = NULL; // That's it! No color variables needed // State tracking datetime g_last_bar_time = 0; bool g_initialized = false; int g_tick_count = 0; double g_session_profit = 0; datetime g_session_start = 0; double g_starting_balance = 0; // Market conditions ENUM_MARKET_CONDITION g_current_market = MARKET_RANGING; double g_current_volatility = 0; double g_current_spread = 0; bool g_trade_allowed = true; // Performance tracking int g_trades_today = 0; int g_wins_today = 0; int g_losses_today = 0; datetime g_last_trade_time = 0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { Print("========================================"); Print("=== ERMT v6.8 Initializing ==="); Print("========================================"); // Validate inputs if(!ValidateInputs()) { Print("Invalid input parameters"); return INIT_PARAMETERS_INCORRECT; } // Create component instances g_utils = new CUtilities(); g_risk = new CRiskManager(); g_tech = new CTechnicalAnalysis(); g_external = new CExternalTradeManager(); g_trade = new CTradeManager(); g_dashboard = new CDashboard(); // Initialize utilities first if(!g_utils.Initialize(InpLogLevel)) { Print("Failed to initialize utilities"); Cleanup(); return INIT_FAILED; } g_utils.EnableFileLogging(InpSaveLog); // Initialize risk manager RiskParameters risk_params; risk_params.max_risk_per_trade = InpMaxRiskPerTrade; risk_params.max_daily_loss = InpMaxDailyLoss; risk_params.max_drawdown = InpMaxDrawdown; risk_params.max_positions = InpMaxPositions; risk_params.min_risk_reward = InpRiskRewardMin; risk_params.use_money_management = true; risk_params.scale_with_equity = true; risk_params.risk_multiplier = 1.0; if(!g_risk.Initialize(g_utils, risk_params)) { Print("Failed to initialize risk manager"); Cleanup(); return INIT_FAILED; } g_risk.SetMagicNumber(InpMagicNumber); // Initialize technical analysis if(!g_tech.Initialize(g_utils, InpATRPeriod, InpRSIPeriod, InpMAPeriod)) { Print("Failed to initialize technical analysis"); Cleanup(); return INIT_FAILED; } g_tech.SetBollingerParams(InpBBPeriod, InpBBDeviation); // Initialize external trade manager if(!g_external.Initialize(g_utils, InpMagicNumber)) { Print("Failed to initialize external trade manager"); Cleanup(); return INIT_FAILED; } g_external.SetManagementMode(InpExternalMode); g_external.SetExternalRiskLimit(InpExternalRiskLimit); // Initialize trade manager if(!g_trade.Initialize(g_utils, g_risk, g_tech, g_external, InpMagicNumber)) { Print("Failed to initialize trade manager"); Cleanup(); return INIT_FAILED; } // Configure trade manager g_trade.SetSizingMethod(InpSizingMethod); g_trade.SetFixedLotSize(InpFixedLotSize); g_trade.SetATRMultipliers(InpATRMultiplierSL, InpATRMultiplierTP); g_trade.SetTrailingMethod(InpTrailingMethod); g_trade.SetTrailingParams(InpTrailStart, InpTrailStep, InpTrailDistance); g_trade.SetBreakevenEnabled(InpMoveToBreakeven); g_trade.SetSlippage(InpSlippage); g_trade.SetComment(InpComment); // Initialize dashboard if(InpShowDashboard) { if(!g_dashboard.Initialize(InpDashboardX, InpDashboardY)) { g_utils.Log("Failed to initialize dashboard", LOG_WARNING); // Non-critical, continue } else { g_dashboard.SetColors(InpDashboardColor, clrRed, clrLime); g_dashboard.SetFontSize(InpDashboardFontSize); } } // Session initialization g_session_start = TimeCurrent(); g_starting_balance = AccountInfoDouble(ACCOUNT_BALANCE); g_last_bar_time = iTime(_Symbol, PERIOD_CURRENT, 0); g_initialized = true; // Log configuration LogConfiguration(); // Initial market scan UpdateMarketConditions(); // Detect any existing external trades if(InpManageExternal) { g_external.DetectExternalTrades(); int external_count = g_external.GetExternalTradeCount(); if(external_count > 0) { g_utils.Log(StringFormat("Found %d external trades to manage", external_count), LOG_INFO); } } g_utils.Log("ERMT v6.8 initialization complete", LOG_INFO); Print("=== Initialization Complete ==="); // Set timer for periodic tasks (every 5 seconds) EventSetTimer(5); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { EventKillTimer(); string reason_text = GetDeInitReasonText(reason); g_utils.Log(StringFormat("EA Deinitializing. Reason: %s", reason_text), LOG_INFO); // Generate final report if(g_trade != NULL) { PerformanceMetrics metrics; g_trade.GetPerformanceMetrics(metrics); string report = g_utils.GenerateReportEnhanced(metrics, g_external.GetExternalTradeCount(), g_external.GetExternalProfit()); g_utils.Log(report, LOG_INFO); // Save final report to file g_utils.SaveToFile(StringFormat("ERMT_FinalReport_%s.txt", TimeToString(TimeCurrent(), TIME_DATE)), report); } // Clean up Cleanup(); Comment(""); Print("=== ERMT v6.8 Shutdown Complete ==="); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(!g_initialized) return; // Check if trading is allowed if(!CheckTradingConditions()) { g_trade_allowed = false; return; } g_trade_allowed = true; g_tick_count++; g_utils.StartTimer(); // Check for new bar bool new_bar = false; datetime current_bar = iTime(_Symbol, PERIOD_CURRENT, 0); if(current_bar != g_last_bar_time) { g_last_bar_time = current_bar; new_bar = true; // New bar processing OnNewBar(); } // Skip if configured to trade only on new bar if(InpTradeOnNewBar && !new_bar) return; // Core processing ProcessExternalTrades(); ProcessManagedTrades(); // Check for new signals on new bar if(new_bar && g_trade_allowed) { CheckEntrySignals(); } // Update dashboard on every tick (if shown) if(InpShowDashboard) { UpdateDashboard(); } // Performance tracking double elapsed = g_utils.GetElapsedTime(); if(elapsed > 100) // Log if processing takes > 100ms { g_utils.Log(StringFormat("Slow tick processing: %.1f ms", elapsed), LOG_WARNING); } } //+------------------------------------------------------------------+ //| Timer Event | //+------------------------------------------------------------------+ void OnTimer() { if(!g_initialized) return; // Periodic maintenance tasks PeriodicTasks(); // Check Friday close if(InpFridayClose && CheckFridayClose()) { CloseAllPositions("Friday close"); } // Monitor system health CheckSystemHealth(); } //+------------------------------------------------------------------+ //| Trade Event | //+------------------------------------------------------------------+ void OnTrade() { if(!g_initialized) return; // Update session profit double current_balance = AccountInfoDouble(ACCOUNT_BALANCE); g_session_profit = current_balance - g_starting_balance; // Track daily trades MqlDateTime current_time; TimeToStruct(TimeCurrent(), current_time); MqlDateTime last_trade_time; TimeToStruct(g_last_trade_time, last_trade_time); if(current_time.day != last_trade_time.day) { // Reset daily counters g_trades_today = 0; g_wins_today = 0; g_losses_today = 0; } g_last_trade_time = TimeCurrent(); // Log trade event g_utils.Log("Trade event detected", LOG_DEBUG); } //+------------------------------------------------------------------+ //| New Bar Processing | //+------------------------------------------------------------------+ void OnNewBar() { // Update market analysis UpdateMarketConditions(); // Check daily reset MqlDateTime current; TimeToStruct(TimeCurrent(), current); MqlDateTime session_start; TimeToStruct(g_session_start, session_start); if(current.day != session_start.day) { // New trading day g_risk.ResetDailyCounters(); g_trades_today = 0; g_wins_today = 0; g_losses_today = 0; g_utils.Log("New trading day started", LOG_INFO); } // Log periodic status if(g_tick_count % 100 == 0) { LogStatus(); } } //+------------------------------------------------------------------+ //| Update Market Conditions | //+------------------------------------------------------------------+ void UpdateMarketConditions() { // Analyze current market state ENUM_SIGNAL_TYPE signal = g_tech.AnalyzeMarket(); // Update volatility g_current_volatility = g_utils.GetATR(InpATRPeriod); // Update spread g_current_spread = g_utils.CalculateSpread(_Symbol); // Determine market condition double trend_strength = g_tech.GetTrendStrength(); double volatility_ratio = g_tech.GetVolatilityRatio(); if(trend_strength > 0.7) { g_current_market = (signal == SIGNAL_BUY) ? MARKET_TRENDING_UP : MARKET_TRENDING_DOWN; } else if(trend_strength < 0.3) { g_current_market = MARKET_RANGING; } else if(volatility_ratio > 1.5) { g_current_market = MARKET_VOLATILE; } else if(g_tech.IsBreakout()) { g_current_market = MARKET_BREAKOUT; } else if(g_tech.CheckReversal()) { g_current_market = MARKET_REVERSAL; } else { g_current_market = MARKET_RANGING; } g_utils.Log(StringFormat("Market: %s, Trend: %.2f, Vol: %.5f, Spread: %.1f", MarketConditionToString(g_current_market), trend_strength, g_current_volatility, g_current_spread), LOG_DEBUG); } //+------------------------------------------------------------------+ //| Process External Trades | //+------------------------------------------------------------------+ void ProcessExternalTrades() { if(!InpManageExternal) return; // Detect new external trades g_external.DetectExternalTrades(); // Process existing external trades for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; if(g_external.IsTicketExternal(ticket)) { // Apply management based on mode g_external.ManageExternalTrade(ticket); // Apply trailing if enabled and profitable if(InpApplyTrailingExternal && InpTrailingMethod != TRAIL_NONE) { if(PositionGetDouble(POSITION_PROFIT) > 0) { double profit_points = PositionGetDouble(POSITION_PROFIT) / PositionGetDouble(POSITION_VOLUME); if(profit_points >= InpTrailStart) { g_external.ApplyTrailingToExternal(ticket, InpTrailDistance); } } } // Check for breakeven if(InpMoveToBreakeven) { double profit_points = PositionGetDouble(POSITION_PROFIT) / PositionGetDouble(POSITION_VOLUME); if(profit_points >= InpBreakevenTrigger) { g_external.MoveExternalToBreakeven(ticket); } } } } // Monitor and clean up g_external.MonitorExternalTrades(); } //+------------------------------------------------------------------+ //| Process Managed Trades | //+------------------------------------------------------------------+ void ProcessManagedTrades() { TradeInfo managed_trades[]; int trade_count = g_trade.GetManagedTrades(managed_trades); for(int i = 0; i < trade_count; i++) { // Check risk rules if(!g_risk.ValidateAndAdjustRisk(managed_trades[i])) { g_utils.Log(StringFormat("Risk violation for trade %d", managed_trades[i].ticket), LOG_WARNING); // Process close request if flagged if(managed_trades[i].close_requested) { g_trade.CloseTrade(managed_trades[i].ticket, managed_trades[i].close_reason); continue; } // Process partial close if flagged if(managed_trades[i].partial_close_volume > 0) { g_trade.PartialClose(managed_trades[i].ticket, managed_trades[i].partial_close_volume, managed_trades[i].partial_close_reason); } } // Apply trailing stop if(InpTrailingMethod != TRAIL_NONE && managed_trades[i].profit > 0) { g_trade.ApplyTrailingStop(managed_trades[i].ticket); } // Check for breakeven if(InpMoveToBreakeven) { g_trade.CheckBreakeven(managed_trades[i].ticket); } // Monitor for exit signals ENUM_SIGNAL_TYPE exit_signal = g_tech.CheckExitSignal(managed_trades[i]); if(exit_signal != SIGNAL_NONE) { // Check if we should close on opposite signal if(InpCloseOnOpposite) { if((managed_trades[i].type == ORDER_TYPE_BUY && exit_signal == SIGNAL_SELL) || (managed_trades[i].type == ORDER_TYPE_SELL && exit_signal == SIGNAL_BUY)) { g_trade.CloseTrade(managed_trades[i].ticket, "Opposite signal"); } } } } } //+------------------------------------------------------------------+ //| Check for Entry Signals | //+------------------------------------------------------------------+ void CheckEntrySignals() { // Check if we can open new positions if(!g_risk.CanOpenNewPosition()) { g_utils.Log("Cannot open new position - risk limits", LOG_DEBUG); return; } // Get market signal ENUM_SIGNAL_TYPE signal = g_tech.AnalyzeMarket(); if(signal == SIGNAL_NONE) return; // Check for hedging restrictions if(!InpAllowHedging) { // Check if we have opposite positions for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(PositionGetInteger(POSITION_MAGIC) == InpMagicNumber) { ENUM_ORDER_TYPE pos_type = (ENUM_ORDER_TYPE)PositionGetInteger(POSITION_TYPE); if((signal == SIGNAL_BUY && pos_type == ORDER_TYPE_SELL) || (signal == SIGNAL_SELL && pos_type == ORDER_TYPE_BUY)) { g_utils.Log("Signal rejected - hedging not allowed", LOG_DEBUG); return; } } } } // Prepare trade request MqlTradeRequest request = {}; request.symbol = _Symbol; request.type = (signal == SIGNAL_BUY) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL; request.comment = InpComment; request.magic = InpMagicNumber; // Calculate position size double atr = g_utils.GetATR(InpATRPeriod); double stop_distance = atr * InpATRMultiplierSL; double lot_size = g_risk.CalculatePositionSize(_Symbol, stop_distance, InpSizingMethod); if(lot_size == 0) { g_utils.Log("Position size calculation returned 0", LOG_WARNING); return; } request.volume = lot_size; // Calculate stops double entry_price = (signal == SIGNAL_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID); double sl = (signal == SIGNAL_BUY) ? entry_price - stop_distance : entry_price + stop_distance; double tp = 0; if(InpTPMethod == TP_FIXED_RR) { double tp_distance = stop_distance * InpTPRatio; tp = (signal == SIGNAL_BUY) ? entry_price + tp_distance : entry_price - tp_distance; } else if(InpTPMethod == TP_ATR_BASED) { double tp_distance = atr * InpATRMultiplierTP; tp = (signal == SIGNAL_BUY) ? entry_price + tp_distance : entry_price - tp_distance; } else if(InpTPMethod == TP_TECHNICAL_LEVEL) { tp = g_tech.GetNearestLevel(entry_price, signal == SIGNAL_BUY); } request.price = entry_price; request.sl = g_utils.NormalizePrice(sl, _Symbol); request.tp = g_utils.NormalizePrice(tp, _Symbol); // Final validation with risk manager if(!g_risk.ValidateNewPosition(_Symbol, lot_size, stop_distance)) { g_utils.Log("Trade rejected by risk manager", LOG_INFO); return; } // Execute trade ulong ticket = g_trade.OpenTrade(request); if(ticket > 0) { g_trades_today++; string message = StringFormat("Trade opened: #%d %s %s %.2f @ %.5f SL=%.5f TP=%.5f", ticket, (signal == SIGNAL_BUY) ? "BUY" : "SELL", _Symbol, lot_size, entry_price, sl, tp); g_utils.Log(message, LOG_INFO); // Send alerts if enabled if(InpEmailAlerts) SendMail("ERMT Trade", message); if(InpPushAlerts) SendNotification(message); } } //+------------------------------------------------------------------+ //| Periodic Maintenance Tasks | //+------------------------------------------------------------------+ void PeriodicTasks() { // Update performance metrics PerformanceMetrics metrics; g_trade.GetPerformanceMetrics(metrics); // Update risk manager with performance g_risk.UpdatePerformanceMetrics(metrics.win_rate, metrics.average_win, metrics.average_loss); // Log periodic report static int report_counter = 0; report_counter++; if(report_counter >= 120) // Every 10 minutes (5 sec timer * 120) { report_counter = 0; g_utils.LogPerformance(metrics); } // Check daily loss limit if(g_risk.IsDailyLossLimitReached()) { g_utils.Log("Daily loss limit reached - stopping trading", LOG_WARNING); g_trade_allowed = false; // Close all positions if critical if(g_risk.GetDailyLoss() > g_risk.GetCurrentDrawdown() * 1.5) { CloseAllPositions("Daily loss limit exceeded"); } } // Clean up old data g_trade.CleanupHistory(); // Save performance snapshot static int save_counter = 0; save_counter++; if(save_counter >= 720) // Every hour { save_counter = 0; SavePerformanceSnapshot(metrics); } } //+------------------------------------------------------------------+ //| Update Dashboard Display | //+------------------------------------------------------------------+ void UpdateDashboard() { if(g_dashboard == NULL) return; // Get current metrics PerformanceMetrics metrics; g_trade.GetPerformanceMetrics(metrics); // Prepare dashboard data DashboardData data; data.balance = AccountInfoDouble(ACCOUNT_BALANCE); data.equity = AccountInfoDouble(ACCOUNT_EQUITY); data.free_margin = AccountInfoDouble(ACCOUNT_MARGIN_FREE); data.profit = metrics.total_profit; data.positions = g_trade.GetOpenTradeCount(); data.win_rate = metrics.win_rate; data.profit_factor = metrics.profit_factor; data.drawdown = metrics.current_drawdown; data.daily_profit = g_session_profit; data.external_trades = g_external.GetExternalTradeCount(); data.external_profit = g_external.GetExternalProfit(); data.risk_status = g_risk.GetCurrentRiskLevel(); data.market_condition = MarketConditionToString(g_current_market); // Additional info data.spread = g_current_spread; data.daily_trades = g_trades_today; // Update dashboard g_dashboard.Update(data); // Show comment if dashboard not available if(!InpShowDashboard) { string comment = StringFormat( "ERMT v6.8 | Balance: %.2f | Equity: %.2f | DD: %.2f%% | Trades: %d | P/L: %.2f", data.balance, data.equity, data.drawdown, data.positions, data.profit ); Comment(comment); } } //+------------------------------------------------------------------+ //| Helper Functions | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Validate Input Parameters | //+------------------------------------------------------------------+ bool ValidateInputs() { if(InpMaxRiskPerTrade <= 0 || InpMaxRiskPerTrade > 10) { Print("Invalid MaxRiskPerTrade: must be between 0 and 10"); return false; } if(InpMaxDailyLoss <= 0 || InpMaxDailyLoss > 20) { Print("Invalid MaxDailyLoss: must be between 0 and 20"); return false; } if(InpMaxDrawdown <= 0 || InpMaxDrawdown > 50) { Print("Invalid MaxDrawdown: must be between 0 and 50"); return false; } if(InpMaxPositions <= 0 || InpMaxPositions > 10) { Print("Invalid MaxPositions: must be between 1 and 10"); return false; } if(InpRiskRewardMin < 0.5 || InpRiskRewardMin > 10) { Print("Invalid RiskRewardMin: must be between 0.5 and 10"); return false; } return true; } //+------------------------------------------------------------------+ //| Check Trading Conditions | //+------------------------------------------------------------------+ bool CheckTradingConditions() { // Check if auto trading is enabled if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) return false; // Check if EA trading is enabled if(!MQLInfoInteger(MQL_TRADE_ALLOWED)) return false; // Check if market is open if(!g_utils.IsMarketOpen()) return false; // Check trading hours if(InpUseTradeHours) { MqlDateTime current; TimeToStruct(TimeCurrent(), current); if(current.hour < InpStartHour || current.hour >= InpEndHour) return false; } // Check spread double max_spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * 3; if(g_current_spread > max_spread) { g_utils.Log(StringFormat("Spread too high: %.1f > %.1f", g_current_spread, max_spread), LOG_DEBUG); return false; } return true; } //+------------------------------------------------------------------+ //| Check Friday Close | //+------------------------------------------------------------------+ bool CheckFridayClose() { MqlDateTime current; TimeToStruct(TimeCurrent(), current); if(current.day_of_week == 5 && current.hour >= InpFridayCloseHour) return true; return false; } //+------------------------------------------------------------------+ //| Close All Positions | //+------------------------------------------------------------------+ void CloseAllPositions(string reason) { g_utils.Log(StringFormat("Closing all positions: %s", reason), LOG_WARNING); for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; if(PositionGetInteger(POSITION_MAGIC) == InpMagicNumber) { g_trade.CloseTrade(ticket, reason); } } } //+------------------------------------------------------------------+ //| Check System Health | //+------------------------------------------------------------------+ void CheckSystemHealth() { // Check connection if(!TerminalInfoInteger(TERMINAL_CONNECTED)) { g_utils.Log("Terminal disconnected", LOG_ERROR); return; } // Check memory usage int memory_used = TerminalInfoInteger(TERMINAL_MEMORY_USED); int memory_available = TerminalInfoInteger(TERMINAL_MEMORY_AVAILABLE); if(memory_available < 100) // Less than 100MB available { g_utils.Log(StringFormat("Low memory warning: %d MB available", memory_available), LOG_WARNING); } // Check CPU usage (simplified) if(g_tick_count > 0 && g_utils.GetElapsedTime() > 500) { g_utils.Log("High CPU usage detected", LOG_WARNING); } } //+------------------------------------------------------------------+ //| Log Configuration | //+------------------------------------------------------------------+ void LogConfiguration() { g_utils.Log("=== ERMT v6.8 Configuration ===", LOG_INFO); g_utils.Log(StringFormat("Symbol: %s", _Symbol), LOG_INFO); g_utils.Log(StringFormat("Timeframe: %s", g_utils.TimeframeToString(Period())), LOG_INFO); g_utils.Log(StringFormat("Magic Number: %d", InpMagicNumber), LOG_INFO); g_utils.Log(StringFormat("Risk per trade: %.2f%%", InpMaxRiskPerTrade), LOG_INFO); g_utils.Log(StringFormat("Max daily loss: %.2f%%", InpMaxDailyLoss), LOG_INFO); g_utils.Log(StringFormat("Max drawdown: %.2f%%", InpMaxDrawdown), LOG_INFO); g_utils.Log(StringFormat("Max positions: %d", InpMaxPositions), LOG_INFO); g_utils.Log(StringFormat("Sizing method: %d", InpSizingMethod), LOG_INFO); g_utils.Log(StringFormat("Manage external: %s", InpManageExternal ? "Yes" : "No"), LOG_INFO); g_utils.Log(StringFormat("Starting balance: %.2f", g_starting_balance), LOG_INFO); g_utils.Log("================================", LOG_INFO); } //+------------------------------------------------------------------+ //| Log Status | //+------------------------------------------------------------------+ void LogStatus() { PerformanceMetrics metrics; g_trade.GetPerformanceMetrics(metrics); g_utils.Log(StringFormat( "Status: Bal=%.2f Eq=%.2f Pos=%d/%d Risk=%.2f%% DD=%.2f%% P/L=%.2f", AccountInfoDouble(ACCOUNT_BALANCE), AccountInfoDouble(ACCOUNT_EQUITY), g_trade.GetOpenTradeCount(), InpMaxPositions, g_risk.GetCurrentRiskLevel(), g_risk.GetCurrentDrawdown(), g_session_profit ), LOG_INFO); } //+------------------------------------------------------------------+ //| Save Performance Snapshot | //+------------------------------------------------------------------+ void SavePerformanceSnapshot(const PerformanceMetrics &metrics) { string filename = StringFormat("ERMT_Snapshot_%s.csv", TimeToString(TimeCurrent(), TIME_DATE)); // Prepare CSV content string content = StringFormat( "%s,%.2f,%.2f,%.2f,%d,%d,%d,%.2f,%.2f,%.2f,%.2f,%.2f\n", TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES), AccountInfoDouble(ACCOUNT_BALANCE), AccountInfoDouble(ACCOUNT_EQUITY), metrics.total_profit, metrics.total_trades, metrics.winning_trades, metrics.losing_trades, metrics.win_rate, metrics.profit_factor, metrics.max_drawdown, metrics.sharpe_ratio, g_risk.GetCurrentRiskLevel() ); // Append to file int handle = FileOpen(filename, FILE_READ|FILE_WRITE|FILE_CSV); if(handle != INVALID_HANDLE) { FileSeek(handle, 0, SEEK_END); FileWriteString(handle, content); FileClose(handle); } } //+------------------------------------------------------------------+ //| Market Condition to String | //+------------------------------------------------------------------+ string MarketConditionToString(ENUM_MARKET_CONDITION condition) { switch(condition) { case MARKET_TRENDING_UP: return "Trending Up"; case MARKET_TRENDING_DOWN: return "Trending Down"; case MARKET_RANGING: return "Ranging"; case MARKET_VOLATILE: return "Volatile"; case MARKET_QUIET: return "Quiet"; case MARKET_BREAKOUT: return "Breakout"; case MARKET_REVERSAL: return "Reversal"; default: return "Unknown"; } } //+------------------------------------------------------------------+ //| Get Deinitialization Reason Text | //+------------------------------------------------------------------+ string GetDeInitReasonText(int reason) { switch(reason) { case REASON_PROGRAM: return "Program terminated"; case REASON_REMOVE: return "Removed from chart"; case REASON_RECOMPILE: return "Recompiled"; case REASON_CHARTCHANGE: return "Chart changed"; case REASON_CHARTCLOSE: return "Chart closed"; case REASON_PARAMETERS: return "Parameters changed"; case REASON_ACCOUNT: return "Account changed"; case REASON_TEMPLATE: return "Template applied"; case REASON_INITFAILED: return "Initialization failed"; case REASON_CLOSE: return "Terminal closed"; default: return StringFormat("Unknown (%d)", reason); } } //+------------------------------------------------------------------+ //| Cleanup Function | //+------------------------------------------------------------------+ void Cleanup() { if(g_dashboard != NULL) { delete g_dashboard; g_dashboard = NULL; } if(g_trade != NULL) { delete g_trade; g_trade = NULL; } if(g_external != NULL) { delete g_external; g_external = NULL; } if(g_tech != NULL) { delete g_tech; g_tech = NULL; } if(g_risk != NULL) { delete g_risk; g_risk = NULL; } if(g_utils != NULL) { g_utils.CloseExternalLog(); delete g_utils; g_utils = NULL; } } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+