//+------------------------------------------------------------------+ //| MoneyManager.mqh | //| Universal Buffer Reader EA v2.0 | //+------------------------------------------------------------------+ #property copyright "Copyright 2025" #property link "" #property version "1.00" #property strict //+------------------------------------------------------------------+ //| CMoneyManager - Calculates lot sizes and manages daily profit | //+------------------------------------------------------------------+ class CMoneyManager { private: double m_base_lot_size; bool m_use_percent_balance; double m_percent_of_balance_for_profit; double m_daily_profit_target_percent; double m_min_lot; double m_max_lot; double m_lot_step; double m_point_value; double m_tick_value; // State double m_daily_profit_accumulated; double m_daily_start_balance; // Logging bool m_enable_debug; public: //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CMoneyManager() { m_base_lot_size = 0.03; m_use_percent_balance = true; m_percent_of_balance_for_profit = 1.0; m_daily_profit_target_percent = 2.0; m_min_lot = 0.01; m_max_lot = 100.0; m_lot_step = 0.01; m_point_value = 0; m_tick_value = 0; m_daily_profit_accumulated = 0; m_daily_start_balance = 0; m_enable_debug = false; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ ~CMoneyManager() { } //+------------------------------------------------------------------+ //| Set parameters | //+------------------------------------------------------------------+ void SetParameters( double base_lot_size, bool use_percent_balance, double percent_of_balance_for_profit, double daily_profit_target_percent, double min_lot, double max_lot, double lot_step, double point_value, double tick_value, bool enable_debug = false ) { // Validate parameters if(base_lot_size <= 0) { Print("[ERROR] Invalid base lot size: ", base_lot_size, ". Using default 0.01"); m_base_lot_size = 0.01; } else { m_base_lot_size = base_lot_size; } if(percent_of_balance_for_profit <= 0) { Print("[ERROR] Invalid percent of balance for profit: ", percent_of_balance_for_profit, ". Using default 1.0"); m_percent_of_balance_for_profit = 1.0; } else { m_percent_of_balance_for_profit = percent_of_balance_for_profit; } if(daily_profit_target_percent < 0) { Print("[ERROR] Invalid daily profit target percent: ", daily_profit_target_percent, ". Using default 2.0"); m_daily_profit_target_percent = 2.0; } else { m_daily_profit_target_percent = daily_profit_target_percent; } if(min_lot <= 0) { Print("[ERROR] Invalid min lot: ", min_lot, ". Using default 0.01"); m_min_lot = 0.01; } else { m_min_lot = min_lot; } if(max_lot <= 0 || max_lot < min_lot) { Print("[ERROR] Invalid max lot: ", max_lot, ". Using default 100.0"); m_max_lot = 100.0; } else { m_max_lot = max_lot; } if(lot_step <= 0) { Print("[ERROR] Invalid lot step: ", lot_step, ". Using default 0.01"); m_lot_step = 0.01; } else { m_lot_step = lot_step; } if(point_value <= 0) { Print("[ERROR] Invalid point value: ", point_value); } m_point_value = point_value; if(tick_value <= 0) { Print("[ERROR] Invalid tick value: ", tick_value); } m_tick_value = tick_value; m_use_percent_balance = use_percent_balance; m_enable_debug = enable_debug; if(m_enable_debug) { Print("[MoneyManager] Parameters set:"); Print(" Base lot size: ", m_base_lot_size); Print(" Use % balance: ", m_use_percent_balance); Print(" % of balance for profit: ", m_percent_of_balance_for_profit); Print(" Daily profit target %: ", m_daily_profit_target_percent); Print(" Min lot: ", m_min_lot, ", Max lot: ", m_max_lot, ", Lot step: ", m_lot_step); Print(" Point value: ", m_point_value, ", Tick value: ", m_tick_value); } } //+------------------------------------------------------------------+ //| Set debug mode | //+------------------------------------------------------------------+ void SetDebugMode(bool enable_debug) { m_enable_debug = enable_debug; } //+------------------------------------------------------------------+ //| Calculate lot size (pure function) | //+------------------------------------------------------------------+ double CalculateLotSize( bool is_buy, double open_price, double tp_price, double account_balance ) { if(m_enable_debug) { Print("[MoneyManager] Calculating lot size..."); Print(" Direction: ", (is_buy ? "BUY" : "SELL")); Print(" Open price: ", open_price); Print(" TP price: ", tp_price); Print(" Account balance: ", account_balance); } // Validate inputs if(open_price <= 0) { Print("[ERROR] Invalid open price: ", open_price); return m_base_lot_size; } if(tp_price <= 0) { Print("[ERROR] Invalid TP price: ", tp_price); return m_base_lot_size; } if(account_balance <= 0) { Print("[ERROR] Invalid account balance: ", account_balance); return m_base_lot_size; } // Calculate TP points double tp_points = 0; if(is_buy) { tp_points = (tp_price - open_price) / m_point_value; } else { tp_points = (open_price - tp_price) / m_point_value; } if(m_enable_debug) { Print(" TP points: ", tp_points); } if(tp_points <= 0) { Print("[WARNING] TP points <= 0. Using base lot size: ", m_base_lot_size); return m_base_lot_size; } // Calculate base lot double base_lot = m_base_lot_size; if(m_use_percent_balance) { base_lot = CalculateBaseLot(tp_points, account_balance); if(m_enable_debug) { Print(" Base lot from % balance: ", base_lot); } } else { if(m_enable_debug) { Print(" Using fixed base lot: ", base_lot); } } // Normalize and return double normalized_lot = NormalizeLotSize(base_lot); if(m_enable_debug) { Print(" Normalized lot: ", normalized_lot); Print("[MoneyManager] Lot size calculation complete: ", normalized_lot); } return normalized_lot; } //+------------------------------------------------------------------+ //| Reset daily profit tracking | //+------------------------------------------------------------------+ void ResetDailyProfit(double current_balance) { if(current_balance <= 0) { Print("[ERROR] Invalid current balance for daily profit reset: ", current_balance); return; } m_daily_start_balance = current_balance; m_daily_profit_accumulated = 0; if(m_enable_debug) { Print("[MoneyManager] Daily profit tracking reset"); Print(" Start balance: ", m_daily_start_balance); Print(" Target profit: ", GetDailyProfitTarget()); } } //+------------------------------------------------------------------+ //| Check if daily profit target reached | //+------------------------------------------------------------------+ bool IsDailyProfitTargetReached() { if(m_daily_profit_target_percent <= 0) { if(m_enable_debug) { Print("[MoneyManager] Daily profit target disabled (0%)"); } return false; } double target_profit = GetDailyProfitTarget(); bool reached = (m_daily_profit_accumulated >= target_profit); if(m_enable_debug) { Print("[MoneyManager] Daily profit check:"); Print(" Accumulated: ", m_daily_profit_accumulated); Print(" Target: ", target_profit); Print(" Reached: ", (reached ? "YES" : "NO")); } return reached; } //+------------------------------------------------------------------+ //| Get daily profit target | //+------------------------------------------------------------------+ double GetDailyProfitTarget() { if(m_daily_start_balance <= 0) return 0; return m_daily_start_balance * m_daily_profit_target_percent / 100.0; } //+------------------------------------------------------------------+ //| Get daily profit accumulated | //+------------------------------------------------------------------+ double GetDailyProfitAccumulated() { return m_daily_profit_accumulated; } //+------------------------------------------------------------------+ //| Get daily profit percentage | //+------------------------------------------------------------------+ double GetDailyProfitPercent() { if(m_daily_start_balance <= 0) return 0; return (m_daily_profit_accumulated / m_daily_start_balance) * 100.0; } //+------------------------------------------------------------------+ //| Set daily profit accumulated (for tracking) | //+------------------------------------------------------------------+ void SetDailyProfitAccumulated(double value) { m_daily_profit_accumulated = value; if(m_enable_debug) { Print("[MoneyManager] Daily profit accumulated set to: ", value); } } private: //+------------------------------------------------------------------+ //| Calculate base lot based on % balance | //+------------------------------------------------------------------+ double CalculateBaseLot(double tp_points, double account_balance) { if(tp_points <= 0) { Print("[ERROR] Invalid TP points for base lot calculation: ", tp_points); return m_base_lot_size; } if(m_tick_value <= 0) { Print("[ERROR] Invalid tick value for base lot calculation: ", m_tick_value); return m_base_lot_size; } if(account_balance <= 0) { Print("[ERROR] Invalid account balance for base lot calculation: ", account_balance); return m_base_lot_size; } double target_profit = account_balance * (m_percent_of_balance_for_profit / 100.0); double profit_per_lot = tp_points * m_tick_value; if(m_enable_debug) { Print("[MoneyManager] Base lot calculation:"); Print(" Target profit: ", target_profit, " (", m_percent_of_balance_for_profit, "% of balance)"); Print(" Profit per lot: ", profit_per_lot); } if(profit_per_lot <= 0) { Print("[ERROR] Invalid profit per lot: ", profit_per_lot); return m_base_lot_size; } double lot = target_profit / profit_per_lot; if(m_enable_debug) { Print(" Calculated lot: ", lot); } return lot; } //+------------------------------------------------------------------+ //| Normalize lot size to broker requirements | //+------------------------------------------------------------------+ double NormalizeLotSize(double lot) { if(m_enable_debug) { Print("[MoneyManager] Normalizing lot size: ", lot); } // Round to lot step double rounded_lot = MathFloor(lot / m_lot_step) * m_lot_step; if(m_enable_debug && rounded_lot != lot) { Print(" Rounded to lot step: ", rounded_lot, " (step: ", m_lot_step, ")"); } // Ensure within min/max double min_adjusted = MathMax(rounded_lot, m_min_lot); double max_adjusted = MathMin(min_adjusted, m_max_lot); if(m_enable_debug) { if(min_adjusted != rounded_lot) { Print(" Adjusted to min lot: ", min_adjusted, " (min: ", m_min_lot, ")"); } if(max_adjusted != min_adjusted) { Print(" Adjusted to max lot: ", max_adjusted, " (max: ", m_max_lot, ")"); } } double normalized = NormalizeDouble(max_adjusted, 2); if(m_enable_debug) { Print(" Final normalized lot: ", normalized); } return normalized; } }; //+------------------------------------------------------------------+