//+------------------------------------------------------------------+ //| Risk Management Overlay EA v6.7 | //| Modular Institutional Grade System | //| Main Coordinator File - Updated | //| Properly Integrated Risk Management | //+-----0 -------------------------------------------------------------+ #property copyright "Institutional Risk Management System v6.7" #property version "6.70" #property strict #property description "Modular risk management system with advanced monitoring" //--- Include system modules #include #include "Modules/DataTypes.mqh" #include "Modules/Utilities.mqh" #include "Modules/RiskManager.mqh" #include "Modules/TradeManager.mqh" #include "Modules/TechnicalAnalysis.mqh" #include "Modules/EntrySystem.mqh" #include "Modules/Dashboard.mqh" //+------------------------------------------------------------------+ //| INPUT PARAMETERS - GROUPED BY MODULE | //+------------------------------------------------------------------+ //--- System Configuration input group "=== SYSTEM CONFIGURATION ===" input bool SystemEnabled = true; // Enable System input int TradingMagic = 12345; // EA Magic Number input bool ManageExternalTrades = true; // Manage External Trades input int MagicNumberFilter = 0; // External Magic Filter (0=all) input ENUM_LOG_LEVEL LogLevel = LOG_INFO; // Logging Level //--- Entry System Parameters input group "=== ENTRY SYSTEM ===" input ENUM_ENTRY_MODE EntryMode = ENTRY_MA_CROSS; // Entry System Type input bool EnableMultipleEntries = false; // Allow Multiple Entries input int MaxPositions = 3; // Maximum Positions input double MinTimeBetweenTrades = 60; // Min Minutes Between Trades //--- Risk Management Parameters input group "=== RISK MANAGEMENT ===" input ENUM_POSITION_SIZING SizingMode = PS_RISK_PERCENT; // Position Sizing Mode input double RiskPercent = 2.5; // Risk Per Trade (%) input double MaxRiskPercent = 5.0; // Maximum Risk (%) input double MaxDailyDrawdown = 5.0; // Max Daily Drawdown (%) input double MaxTotalExposure = 10.0; // Max Total Exposure (%) input bool UseKellyCriterion = false; // Use Kelly Criterion input double KellyFraction = 0.25; // Kelly Fraction input double MinLotSize = 0.01; // Minimum Lot Size input double MaxLotSize = 10.0; // Maximum Lot Size //--- Trade Management Parameters input group "=== TRADE MANAGEMENT ===" input ENUM_TP_MODE TPMode = TP_ADAPTIVE; // Take Profit Mode input ENUM_SL_MODE SLMode = SL_ATR; // Stop Loss Mode input bool EnableBreakeven = true; // Enable Breakeven input double BreakevenTrigger = 1.0; // Breakeven at X:1 input bool EnableTrailing = true; // Enable Trailing Stop input double TrailingStart = 1.5; // Start Trailing at X:1 input bool EnablePartialClose = true; // Enable Partial Closes input string PartialLevels = "1.0:33,2.0:33,3.0:20"; // Partial Close Levels //--- External Trade Management input group "=== EXTERNAL TRADE RULES ===" input bool ForceRiskRules = true; // Force Risk Rules input bool ForceStopLoss = true; // Force Stop Loss input bool ForceTakeProfit = true; // Force Take Profit input bool OverrideExternalSL = false; // Override Existing SL input bool OverrideExternalTP = false; // Override Existing TP input double DefaultExternalRisk = 1.0; // Default External Risk (%) input bool CloseExcessiveRisk = true; // Close Excessive Risk //--- Technical Analysis Parameters input group "=== TECHNICAL ANALYSIS ===" input bool UseSupportResistance = true; // Use S/R Levels input bool UseFibonacci = true; // Use Fibonacci input bool UsePivotPoints = true; // Use Pivot Points input bool UseMarketStructure = true; // Use Market Structure input bool EnableLogging = true; // Enable Detailed Logging input int TechnicalLookback = 200; // Technical Lookback Bars //--- Dashboard Parameters input group "=== DASHBOARD SETTINGS ===" input bool ShowDashboard = true; // Show Dashboard input int DashboardX = 20; // Dashboard X Position input int DashboardY = 30; // Dashboard Y Position input color DashboardColor = clrWhiteSmoke; // Dashboard Text Color input color ProfitColor = clrLime; // Profit Color input color LossColor = clrRed; // Loss Color input int UpdateFrequency = 1; // Update Frequency (seconds) //+------------------------------------------------------------------+ //| GLOBAL OBJECTS | //+------------------------------------------------------------------+ //--- Module instances CRiskManager *RiskMgr; CTradeManager *TradeMgr; CEntrySystem *EntrySys; CTechnicalAnalysis *TechAnalysis; CDashboard *Dashboard; CUtilities *Utils; //--- System state SystemState g_SystemState; PerformanceMetrics g_Performance; MarketConditions g_MarketConditions; //--- Trade tracking ManagedTrade g_ManagedTrades[]; int g_TotalTrades = 0; //--- Timing variables datetime g_LastUpdateTime = 0; datetime g_LastTradeTime = 0; datetime g_LastReportTime = 0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Initialize system state g_SystemState.is_active = SystemEnabled; g_SystemState.magic_number = TradingMagic; g_SystemState.start_balance = AccountInfoDouble(ACCOUNT_BALANCE); g_SystemState.peak_balance = g_SystemState.start_balance; g_SystemState.session_start = TimeCurrent(); //--- Create module instances RiskMgr = new CRiskManager(); TradeMgr = new CTradeManager(); EntrySys = new CEntrySystem(); TechAnalysis = new CTechnicalAnalysis(); Dashboard = new CDashboard(); Utils = new CUtilities(); //--- Initialize modules if(!InitializeModules()) { Print("Failed to initialize modules"); return(INIT_FAILED); } //--- Wait for indicators to be ready if(MQLInfoInteger(MQL_TESTER)) { Print("Backtest mode: Waiting for indicators to initialize..."); // Wait for sufficient bars int required_bars = MathMax(200, TechnicalLookback); if(Bars(_Symbol, PERIOD_CURRENT) < required_bars) { Print("Insufficient bars for indicator calculation. Need ", required_bars, ", have ", Bars(_Symbol, PERIOD_CURRENT)); return(INIT_FAILED); } // Validate ATR is working double test_atr = (*TechAnalysis).GetATR(14); if(test_atr <= 0) { Print("ATR indicator not ready. Please ensure sufficient historical data."); return(INIT_FAILED); } Print("Indicators initialized successfully. ATR: ", test_atr); } //--- Initialize performance metrics ZeroMemory(g_Performance); //--- Load existing positions LoadExistingPositions(); //--- Create dashboard if(ShowDashboard) { (*Dashboard).Create(DashboardX, DashboardY, DashboardColor, ProfitColor, LossColor); (*Dashboard).Update(g_Performance, g_ManagedTrades, g_MarketConditions); } //--- Set timer for periodic updates EventSetTimer(UpdateFrequency); //--- Log initialization (*Utils).Log(LOG_INFO, StringFormat("Risk Management Overlay v6.7 initialized. Balance: %.2f", g_SystemState.start_balance)); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Kill timer EventKillTimer(); //--- Save final report if(Utils != NULL) (*Utils).GenerateReport(g_Performance, g_ManagedTrades); //--- Cleanup modules if(Dashboard != NULL && ShowDashboard) (*Dashboard).Destroy(); //--- Delete module instances delete RiskMgr; delete TradeMgr; delete EntrySys; delete TechAnalysis; delete Dashboard; delete Utils; //--- Log shutdown Print("Risk Management Overlay shutdown: ", reason); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Check if system is enabled if(!g_SystemState.is_active || !SystemEnabled) return; //--- Check terminal connection if(!TerminalInfoInteger(TERMINAL_CONNECTED)) return; //--- Check terminal connection and auto trading if(Utils != NULL) { if(!(*Utils).CheckTerminalConnection()) return; if(!(*Utils).CheckAutoTrading()) return; } //--- Update market conditions UpdateMarketConditions(); //--- Process existing trades ProcessManagedTrades(); //--- Check for new external trades if(ManageExternalTrades) CheckForNewExternalTrades(); //--- Check entry signals (if enabled) if(EntryMode != ENTRY_DISABLED) CheckEntrySignals(); //--- Update dashboard if(ShowDashboard && TimeCurrent() - g_LastUpdateTime >= UpdateFrequency) { (*Dashboard).Update(g_Performance, g_ManagedTrades, g_MarketConditions); g_LastUpdateTime = TimeCurrent(); } //--- Periodic tasks PerformPeriodicTasks(); } //+------------------------------------------------------------------+ //| Initialize all modules | //+------------------------------------------------------------------+ bool InitializeModules() { //--- Initialize Risk Manager RiskManagerConfig riskConfig; riskConfig.sizing_mode = SizingMode; riskConfig.risk_percent = RiskPercent; riskConfig.max_risk_percent = MaxRiskPercent; riskConfig.max_daily_dd = MaxDailyDrawdown; riskConfig.max_exposure = MaxTotalExposure; riskConfig.use_kelly = UseKellyCriterion; riskConfig.kelly_fraction = KellyFraction; riskConfig.min_lot_size = MinLotSize; riskConfig.max_lot_size = MaxLotSize; if(!(*RiskMgr).Initialize(riskConfig)) return false; //--- Initialize Trade Manager TradeManagerConfig tradeConfig; tradeConfig.tp_mode = TPMode; tradeConfig.sl_mode = SLMode; tradeConfig.enable_breakeven = EnableBreakeven; tradeConfig.breakeven_trigger = BreakevenTrigger; tradeConfig.enable_trailing = EnableTrailing; tradeConfig.trailing_start = TrailingStart; tradeConfig.enable_partial = EnablePartialClose; tradeConfig.partial_levels = PartialLevels; tradeConfig.force_sl = ForceStopLoss; tradeConfig.force_tp = ForceTakeProfit; tradeConfig.override_sl = OverrideExternalSL; tradeConfig.override_tp = OverrideExternalTP; if(!(*TradeMgr).Initialize(tradeConfig, TradingMagic)) return false; //--- Initialize Entry System EntrySystemConfig entryConfig; entryConfig.entry_mode = EntryMode; entryConfig.max_positions = MaxPositions; entryConfig.min_time_between = MinTimeBetweenTrades; entryConfig.allow_multiple = EnableMultipleEntries; if(!(*EntrySys).Initialize(entryConfig)) return false; //--- Initialize Technical Analysis TechnicalConfig techConfig; techConfig.use_sr = UseSupportResistance; techConfig.use_fib = UseFibonacci; techConfig.use_pivot = UsePivotPoints; techConfig.use_structure = UseMarketStructure; techConfig.lookback = TechnicalLookback; if(!(*TechAnalysis).Initialize(techConfig)) return false; //--- Initialize Utilities if(!(*Utils).Initialize(LogLevel)) return false; return true; } //+------------------------------------------------------------------+ //| Load existing positions into management system | //+------------------------------------------------------------------+ void LoadExistingPositions() { int total = PositionsTotal(); for(int i = 0; i < total; i++) { string symbol = PositionGetSymbol(i); if(symbol != _Symbol) continue; if(PositionSelect(symbol)) { ulong magic = PositionGetInteger(POSITION_MAGIC); bool should_manage = false; //--- Check if we should manage this position if(magic == TradingMagic) should_manage = true; else if(ManageExternalTrades && (MagicNumberFilter == 0 || magic == MagicNumberFilter)) should_manage = true; if(should_manage) { ManagedTrade trade; if(CreateManagedTrade(trade, PositionGetInteger(POSITION_TICKET))) { AddManagedTrade(trade); (*Utils).Log(LOG_INFO, StringFormat("Loaded position #%d into management", trade.ticket)); } } } } } //+------------------------------------------------------------------+ //| Update market conditions - ENHANCED VERSION | //+------------------------------------------------------------------+ void UpdateMarketConditions() { // Get fresh market analysis g_MarketConditions = (*TechAnalysis).AnalyzeMarket(); // Additional validation and fallback if(g_MarketConditions.volatility <= 0) { Print("UpdateMarketConditions: Invalid volatility from TechAnalysis, calculating fallback"); // Direct ATR calculation double atr = (*TechAnalysis).GetATR(14); if(atr > 0) { g_MarketConditions.volatility = atr; Print("UpdateMarketConditions: Set volatility to ", atr); } else { // Emergency fallback double price = SymbolInfoDouble(_Symbol, SYMBOL_BID); g_MarketConditions.volatility = price * 0.001; // 0.1% of price Print("UpdateMarketConditions: Using emergency volatility: ", g_MarketConditions.volatility); } } g_MarketConditions.spread = (double)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); //--- Update risk manager with market conditions (*RiskMgr).UpdateMarketConditions(g_MarketConditions); } //+------------------------------------------------------------------+ //| Process all managed trades | //+------------------------------------------------------------------+ void ProcessManagedTrades() { for(int i = ArraySize(g_ManagedTrades) - 1; i >= 0; i--) { //--- Check if position still exists if(!PositionSelectByTicket(g_ManagedTrades[i].ticket)) { //--- Position closed, update performance UpdatePerformanceMetrics(g_ManagedTrades[i]); RemoveManagedTrade(i); continue; } //--- Update trade information UpdateTradeInfo(g_ManagedTrades[i]); //--- Apply risk management - PROPERLY CALLING MODULE METHOD (*RiskMgr).ValidateAndAdjustRisk(g_ManagedTrades[i], MaxRiskPercent); //--- Apply trade management (*TradeMgr).ManageTrade(g_ManagedTrades[i], g_MarketConditions); } } //+------------------------------------------------------------------+ //| Check for new external trades | //+------------------------------------------------------------------+ void CheckForNewExternalTrades() { int total = PositionsTotal(); for(int i = 0; i < total; i++) { string symbol = PositionGetSymbol(i); if(symbol != _Symbol) continue; if(PositionSelect(symbol)) { ulong ticket = PositionGetInteger(POSITION_TICKET); ulong magic = PositionGetInteger(POSITION_MAGIC); //--- Skip if already managed or wrong magic if(IsTradeManaged(ticket)) continue; if(magic == TradingMagic) continue; // Our own trade if(MagicNumberFilter != 0 && magic != MagicNumberFilter) continue; //--- Create managed trade entry ManagedTrade trade; if(CreateManagedTrade(trade, ticket)) { //--- Apply risk rules to external trade if(ForceRiskRules) { bool keep_trade = (*RiskMgr).EnforceRiskRules(trade, DefaultExternalRisk, CloseExcessiveRisk); if(!keep_trade && CloseExcessiveRisk) { // Close the excessive risk trade (*TradeMgr).ClosePosition(ticket); (*Utils).Log(LOG_WARNING, StringFormat("Closed excessive risk external trade #%d", ticket)); continue; } } AddManagedTrade(trade); (*Utils).Log(LOG_INFO, StringFormat("Added external trade #%d to management", ticket)); } } } } //+------------------------------------------------------------------+ //| Check entry signals - ENHANCED VERSION WITH DEBUGGING | //+------------------------------------------------------------------+ void CheckEntrySignals() { //--- Check position limits if(ArraySize(g_ManagedTrades) >= MaxPositions) { if(EnableLogging) (*Utils).Log(LOG_DEBUG, "Max positions reached"); return; } //--- Check time restriction if(TimeCurrent() - g_LastTradeTime < MinTimeBetweenTrades * 60) { if(EnableLogging) (*Utils).Log(LOG_DEBUG, "Time restriction active"); return; } //--- Debug market conditions BEFORE signal check if(EnableLogging) { Print("=== Market Conditions Debug ==="); Print("Market Volatility (ATR): ", g_MarketConditions.volatility); Print("Market Trend Strength: ", g_MarketConditions.trend_strength); Print("Market Condition: ", EnumToString(g_MarketConditions.condition)); } // Force update market conditions if ATR is invalid if(g_MarketConditions.volatility <= 0) { Print("WARNING: Market volatility is 0, forcing update"); UpdateMarketConditions(); Print("After update - Market Volatility: ", g_MarketConditions.volatility); } //--- Get entry signal EntrySignal signal = (*EntrySys).CheckSignal(g_MarketConditions); if(EnableLogging && signal.signal_type != SIGNAL_NONE) { (*Utils).Log(LOG_DEBUG, StringFormat("Signal found: Type=%d, Confidence=%.1f, Comment=%s", signal.signal_type, signal.confidence, signal.comment)); } if(signal.signal_type != SIGNAL_NONE) { // Debug logging if(EnableLogging) { Print("=== Signal Debug Info ==="); Print("Signal Type: ", signal.comment); Print("Market Volatility (ATR): ", g_MarketConditions.volatility); Print("Stop Distance: ", signal.stop_loss_distance, " (", signal.stop_loss_distance / _Point, " points)"); Print("TP Distance: ", signal.take_profit_distance, " (", signal.take_profit_distance / _Point, " points)"); Print("Current Spread: ", SymbolInfoInteger(_Symbol, SYMBOL_SPREAD), " points"); Print("Min Stop Level: ", SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL), " points"); Print("======================"); } // Additional safety check if(signal.stop_loss_distance <= 0) { (*Utils).Log(LOG_ERROR, StringFormat("Zero stop distance detected! Signal: %s, ATR: %.5f", signal.comment, g_MarketConditions.volatility)); // Emergency fallback double emergency_stop = 50 * SymbolInfoDouble(_Symbol, SYMBOL_POINT); // 50 points signal.stop_loss_distance = emergency_stop; signal.take_profit_distance = emergency_stop * 2; // 2:1 RR (*Utils).Log(LOG_WARNING, StringFormat("Applied emergency stop: %.5f", emergency_stop)); } //--- Calculate position size using RiskManager double lotSize = (*RiskMgr).CalculatePositionSize(signal.stop_loss_distance); if(lotSize > 0 && (*RiskMgr).ValidateNewPosition(lotSize, signal.stop_loss_distance)) { //--- Prepare trade request TradeRequest request; request.symbol = _Symbol; request.volume = lotSize; request.type = (signal.signal_type == SIGNAL_BUY) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL; request.sl_distance = signal.stop_loss_distance; request.tp_distance = signal.take_profit_distance; request.comment = signal.comment; //--- Execute trade ulong ticket = (*TradeMgr).OpenPosition(request); if(ticket > 0) { g_LastTradeTime = TimeCurrent(); //--- Add to managed trades ManagedTrade trade; if(CreateManagedTrade(trade, ticket)) { trade.entry_reason = signal.comment; trade.entry_signal = signal; AddManagedTrade(trade); (*Utils).Log(LOG_INFO, StringFormat("Opened %s position #%d, Lots: %.2f, Risk: %.2f%%", (signal.signal_type == SIGNAL_BUY) ? "BUY" : "SELL", ticket, lotSize, RiskPercent)); } } else if(EnableLogging) { (*Utils).Log(LOG_WARNING, "Failed to open position - check logs for details"); } } else if(EnableLogging && lotSize <= 0) { (*Utils).Log(LOG_WARNING, "Invalid lot size calculated - risk limits may be exceeded"); } } } //+------------------------------------------------------------------+ //| Perform periodic maintenance tasks | //+------------------------------------------------------------------+ void PerformPeriodicTasks() { //--- Daily reset check MqlDateTime current_time; TimeToStruct(TimeCurrent(), current_time); static int last_day = -1; if(current_time.day != last_day) { last_day = current_time.day; ResetDailyMetrics(); } //--- Update risk manager performance metrics if(g_Performance.total_trades > 0) { double avg_win = (g_Performance.winning_trades > 0) ? g_Performance.gross_profit / g_Performance.winning_trades : 0; double avg_loss = (g_Performance.losing_trades > 0) ? g_Performance.gross_loss / g_Performance.losing_trades : 0; (*RiskMgr).UpdatePerformanceMetrics(g_Performance.winning_trades, g_Performance.losing_trades, avg_win, avg_loss); } //--- Generate periodic reports if(TimeCurrent() - g_LastReportTime > 3600) // Hourly { (*Utils).GenerateReport(g_Performance, g_ManagedTrades); g_LastReportTime = TimeCurrent(); } //--- Check for news events if((*Utils).IsNewsTime()) { g_MarketConditions.news_event = true; } } //+------------------------------------------------------------------+ //| Create managed trade structure | //+------------------------------------------------------------------+ bool CreateManagedTrade(ManagedTrade &trade, ulong ticket) { if(!PositionSelectByTicket(ticket)) return false; trade.ticket = ticket; trade.symbol = PositionGetString(POSITION_SYMBOL); trade.type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); trade.open_time = (datetime)PositionGetInteger(POSITION_TIME); trade.open_price = PositionGetDouble(POSITION_PRICE_OPEN); trade.volume = PositionGetDouble(POSITION_VOLUME); trade.current_volume = trade.volume; trade.sl = PositionGetDouble(POSITION_SL); trade.tp = PositionGetDouble(POSITION_TP); trade.magic = PositionGetInteger(POSITION_MAGIC); trade.profit = PositionGetDouble(POSITION_PROFIT); trade.commission = 0; // Will be calculated from deal history if needed trade.swap = PositionGetDouble(POSITION_SWAP); //--- Initialize management fields trade.be_activated = false; trade.trailing_activated = false; trade.partial_count = 0; trade.risk_amount = (*RiskMgr).CalculateTradeRisk(trade); // PROPERLY CALLING MODULE METHOD trade.max_profit = 0; trade.max_drawdown = 0; trade.management_start = TimeCurrent(); //--- Set entry reason if(trade.magic == TradingMagic) trade.entry_reason = "EA Signal"; else trade.entry_reason = "External Trade"; return true; } //+------------------------------------------------------------------+ //| Add trade to managed array | //+------------------------------------------------------------------+ void AddManagedTrade(ManagedTrade &trade) { int size = ArraySize(g_ManagedTrades); ArrayResize(g_ManagedTrades, size + 1); g_ManagedTrades[size] = trade; g_TotalTrades++; } //+------------------------------------------------------------------+ //| Remove trade from managed array | //+------------------------------------------------------------------+ void RemoveManagedTrade(int index) { int size = ArraySize(g_ManagedTrades); if(index < 0 || index >= size) return; for(int i = index; i < size - 1; i++) { g_ManagedTrades[i] = g_ManagedTrades[i + 1]; } ArrayResize(g_ManagedTrades, size - 1); } //+------------------------------------------------------------------+ //| Check if trade is already managed | //+------------------------------------------------------------------+ bool IsTradeManaged(ulong ticket) { for(int i = 0; i < ArraySize(g_ManagedTrades); i++) { if(g_ManagedTrades[i].ticket == ticket) return true; } return false; } //+------------------------------------------------------------------+ //| Update trade information | //+------------------------------------------------------------------+ void UpdateTradeInfo(ManagedTrade &trade) { if(!PositionSelectByTicket(trade.ticket)) return; trade.current_volume = PositionGetDouble(POSITION_VOLUME); trade.sl = PositionGetDouble(POSITION_SL); trade.tp = PositionGetDouble(POSITION_TP); trade.profit = PositionGetDouble(POSITION_PROFIT); trade.commission = (*Utils).CalculatePositionCommission(trade.ticket); trade.swap = PositionGetDouble(POSITION_SWAP); //--- Update max profit/drawdown double total_profit = trade.profit + trade.commission + trade.swap; if(total_profit > trade.max_profit) trade.max_profit = total_profit; if(total_profit < trade.max_drawdown) trade.max_drawdown = total_profit; } //+------------------------------------------------------------------+ //| Update performance metrics | //+------------------------------------------------------------------+ void UpdatePerformanceMetrics(ManagedTrade &trade) { double final_profit = trade.profit + trade.commission + trade.swap; //--- Update counts g_Performance.total_trades++; if(final_profit > 0) { g_Performance.winning_trades++; g_Performance.gross_profit += final_profit; g_Performance.consecutive_wins++; g_Performance.consecutive_losses = 0; } else { g_Performance.losing_trades++; g_Performance.gross_loss += MathAbs(final_profit); g_Performance.consecutive_losses++; g_Performance.consecutive_wins = 0; } //--- Update max consecutive if(g_Performance.consecutive_wins > g_Performance.max_consecutive_wins) g_Performance.max_consecutive_wins = g_Performance.consecutive_wins; if(g_Performance.consecutive_losses > g_Performance.max_consecutive_losses) g_Performance.max_consecutive_losses = g_Performance.consecutive_losses; //--- Update profit factor and win rate if(g_Performance.gross_loss > 0) g_Performance.profit_factor = g_Performance.gross_profit / g_Performance.gross_loss; else g_Performance.profit_factor = (g_Performance.gross_profit > 0) ? 999.99 : 0; g_Performance.win_rate = (g_Performance.total_trades > 0) ? (double)g_Performance.winning_trades / g_Performance.total_trades * 100 : 0; //--- Update drawdown double current_balance = AccountInfoDouble(ACCOUNT_BALANCE); if(current_balance > g_SystemState.peak_balance) g_SystemState.peak_balance = current_balance; double drawdown = (g_SystemState.peak_balance - current_balance) / g_SystemState.peak_balance * 100; if(drawdown > g_Performance.max_drawdown_percent) g_Performance.max_drawdown_percent = drawdown; } //+------------------------------------------------------------------+ //| Reset daily metrics | //+------------------------------------------------------------------+ void ResetDailyMetrics() { g_Performance.daily_profit = 0; g_Performance.daily_trades = 0; g_Performance.daily_volume = 0; //--- Reset risk manager daily metrics (*RiskMgr).ResetDailyMetrics(); (*Utils).Log(LOG_INFO, "Daily metrics reset"); } //+------------------------------------------------------------------+ //| Timer function for periodic updates | //+------------------------------------------------------------------+ void OnTimer() { //--- Update dashboard if needed if(ShowDashboard) { (*Dashboard).Update(g_Performance, g_ManagedTrades, g_MarketConditions); } } //+------------------------------------------------------------------+ //| Trade transaction event | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { //--- Handle trade events if(trans.type == TRADE_TRANSACTION_DEAL_ADD) { if(trans.deal_type == DEAL_TYPE_BUY || trans.deal_type == DEAL_TYPE_SELL) { (*Utils).Log(LOG_DEBUG, StringFormat("New deal: #%d, Volume: %.2f", trans.deal, trans.volume)); } } }