//+------------------------------------------------------------------+ //| RiskManager.mqh - Risk and position sizing management | //| Calculates lot sizes, validates parameters, checks trading hours | //+------------------------------------------------------------------+ #ifndef __RISKMANAGER_MQH__ #define __RISKMANAGER_MQH__ #include "Config.mqh" #include "Logger.mqh" #include "MarketData.mqh" #include "Utilities.mqh" class CRiskManager { private: CMarketData *mp_market_data; CLogger *mp_logger; public: // Constructor CRiskManager(CMarketData *market_data, CLogger *logger) { mp_market_data = market_data; mp_logger = logger; } // Calculate lot size based on configuration double CalculateLotSize(int stop_loss_points) { double lot = 0.0; if(g_lot_mode == LOT_MODE_FIXED) { lot = g_fixed_lot; } else if(g_lot_mode == LOT_MODE_RISK) { lot = CalculateLotByRisk(stop_loss_points); } return ValidateLotSize(lot); } // Calculate lot size based on risk percent double CalculateLotByRisk(int stop_loss_points) { double account_balance = AccountInfoDouble(ACCOUNT_BALANCE); double stop_loss_distance = CUtilities::PointsToPrice(mp_market_data.GetSymbol(), stop_loss_points); double contract_size = CUtilities::GetContractSize(mp_market_data.GetSymbol()); if(stop_loss_distance == 0 || contract_size == 0) return g_min_lot; // Risk = Account Balance * Risk Percent / 100 double risk_amount = account_balance * (g_risk_percent / 100.0); // Lot = Risk Amount / (SL Distance * Contract Size * Point) double point = mp_market_data.GetPoint(); double lot = risk_amount / (stop_loss_distance * contract_size); return lot; } // Validate and normalize lot size double ValidateLotSize(double lot) { // Apply global limits first if(lot < g_min_lot) lot = g_min_lot; if(lot > g_max_lot) lot = g_max_lot; // Normalize to broker's lot step lot = CUtilities::NormalizeLot(mp_market_data.GetSymbol(), lot); if(mp_logger && g_debug_mode) mp_logger.Info(StringFormat("Lot size calculated: %.2f", lot)); return lot; } // Check if spread is acceptable bool IsSpreadAcceptable() { return mp_market_data.IsSpreadAcceptable(g_max_spread_points); } // Check if trading is allowed by time filter bool IsTradingHourValid() { if(!g_use_trading_hours) return true; MqlDateTime time_struct; TimeToStruct(TimeCurrent(), time_struct); int current_hour = time_struct.hour; if(g_trade_start_hour <= g_trade_end_hour) { // Normal case: e.g., 8:00 to 20:00 if(current_hour < g_trade_start_hour || current_hour >= g_trade_end_hour) { if(mp_logger && g_debug_mode) mp_logger.Info(StringFormat("Outside trading hours: %d (allowed: %d-%d)", current_hour, g_trade_start_hour, g_trade_end_hour)); return false; } } else { // Overnight case: e.g., 20:00 to 8:00 if(current_hour < g_trade_start_hour && current_hour >= g_trade_end_hour) { if(mp_logger && g_debug_mode) mp_logger.Info(StringFormat("Outside trading hours: %d (allowed: %d-%d)", current_hour, g_trade_start_hour, g_trade_end_hour)); return false; } } return true; } // Calculate stop loss price in absolute terms double CalculateStopLossPrice(bool buy) { double bid = mp_market_data.GetBid(); double ask = mp_market_data.GetAsk(); double entry_price = buy ? ask : bid; double sl_distance = CUtilities::PointsToPrice(mp_market_data.GetSymbol(), g_stop_loss_points); double sl_price = buy ? (entry_price - sl_distance) : (entry_price + sl_distance); return CUtilities::NormalizePrice(mp_market_data.GetSymbol(), sl_price); } // Calculate take profit price in absolute terms double CalculateTakeProfitPrice(bool buy) { double bid = mp_market_data.GetBid(); double ask = mp_market_data.GetAsk(); double entry_price = buy ? ask : bid; double tp_distance = CUtilities::PointsToPrice(mp_market_data.GetSymbol(), g_take_profit_points); double tp_price = buy ? (entry_price + tp_distance) : (entry_price - tp_distance); return CUtilities::NormalizePrice(mp_market_data.GetSymbol(), tp_price); } // Get market data reference CMarketData* GetMarketData() { return mp_market_data; } }; #endif //__RISKMANAGER_MQH__