444 lines
14 KiB
Plaintext
444 lines
14 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| 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;
|
|
}
|
|
};
|
|
//+------------------------------------------------------------------+ |