mql5/Include/Experts/Risk/DrawdownManager.mqh
2025-08-16 12:30:04 -04:00

356 lines
12 KiB
MQL5

//+------------------------------------------------------------------+
//| DrawdownManager.mqh |
//| Copyright 2025, Your Company Name |
//| https://www.yoursite.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Your Company Name"
#property link "https://www.yoursite.com"
#property version "1.00"
#property strict
#include <Arrays\ArrayObj.mqh>
//+------------------------------------------------------------------+
//| Drawdown Period Structure |
//+------------------------------------------------------------------+
struct SDrawdownPeriod
{
datetime startTime; // Start time of drawdown
datetime endTime; // End time of drawdown (0 if ongoing)
double startEquity; // Equity at start of drawdown
double minEquity; // Minimum equity during drawdown
double maxDrawdown; // Maximum drawdown in account currency
double maxDrawdownPct; // Maximum drawdown in percentage
// Constructor
SDrawdownPeriod()
{
startTime = 0;
endTime = 0;
startEquity = 0.0;
minEquity = 0.0;
maxDrawdown = 0.0;
maxDrawdownPct = 0.0;
}
};
//+------------------------------------------------------------------+
//| Drawdown Manager Class |
//+------------------------------------------------------------------+
class CDrawdownManager
{
private:
CArrayObj* m_drawdowns; // Array of drawdown periods
SDrawdownPeriod* m_currentDD; // Current drawdown period (if any)
// Statistics
double m_equityPeak; // Peak equity value
double m_maxDrawdown; // Maximum drawdown (currency)
double m_maxDrawdownPct; // Maximum drawdown (%)
double m_avgDrawdown; // Average drawdown
int m_ddCount; // Number of drawdowns
// Parameters
double m_equity; // Current equity
bool m_isInitialized; // Initialization flag
// Private methods
void UpdateDrawdownStats();
public:
// Constructor/Destructor
CDrawdownManager();
~CDrawdownManager();
// Initialization
bool Initialize();
void Deinitialize();
// Update methods
void UpdateEquity(const double equity);
void UpdateFromHistory();
// Getters
double GetCurrentDrawdown() const;
double GetCurrentDrawdownPct() const;
double GetMaxDrawdown() const { return m_maxDrawdown; }
double GetMaxDrawdownPct() const { return m_maxDrawdownPct; }
double GetAvgDrawdown() const;
int GetDrawdownCount() const { return m_ddCount; }
bool IsInDrawdown() const { return (m_currentDD != NULL); }
bool IsInitialized() const { return m_isInitialized; }
// Risk management
bool IsDrawdownLimitReached(const double maxDrawdownPct) const;
bool IsDailyLossLimitReached(const double maxDailyLossPct) const;
// Statistics
void CalculateStatistics();
// Persistence
bool SaveToFile(const string filename);
bool LoadFromFile(const string filename);
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CDrawdownManager::CDrawdownManager() :
m_drawdowns(NULL),
m_currentDD(NULL),
m_equityPeak(0.0),
m_maxDrawdown(0.0),
m_maxDrawdownPct(0.0),
m_avgDrawdown(0.0),
m_ddCount(0),
m_equity(0.0),
m_isInitialized(false)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CDrawdownManager::~CDrawdownManager()
{
Deinitialize();
}
//+------------------------------------------------------------------+
//| Initialize drawdown manager |
//+------------------------------------------------------------------+
bool CDrawdownManager::Initialize()
{
if (m_isInitialized)
return true;
m_drawdowns = new CArrayObj();
if (m_drawdowns == NULL)
return false;
m_equity = AccountInfoDouble(ACCOUNT_EQUITY);
m_equityPeak = m_equity;
m_currentDD = NULL;
// Load historical drawdowns if available
// LoadFromFile("drawdown_history.dat");
m_isInitialized = true;
return true;
}
//+------------------------------------------------------------------+
//| Deinitialize drawdown manager |
//+------------------------------------------------------------------+
void CDrawdownManager::Deinitialize()
{
if (m_drawdowns != NULL)
{
m_drawdowns.Clear();
delete m_drawdowns;
m_drawdowns = NULL;
}
if (m_currentDD != NULL)
{
delete m_currentDD;
m_currentDD = NULL;
}
m_isInitialized = false;
}
//+------------------------------------------------------------------+
//| Update equity and check for drawdowns |
//+------------------------------------------------------------------+
void CDrawdownManager::UpdateEquity(const double equity)
{
if (!m_isInitialized)
return;
m_equity = equity;
// Check for new peak
if (m_equity > m_equityPeak)
{
// If we were in a drawdown and now reached a new peak, end the drawdown
if (m_currentDD != NULL)
{
m_currentDD.endTime = TimeCurrent();
m_drawdowns.Add(m_currentDD);
m_currentDD = NULL;
}
m_equityPeak = m_equity;
}
// Check for drawdown
else if (m_equity < m_equityPeak)
{
// Start new drawdown if not already in one
if (m_currentDD == NULL)
{
m_currentDD = new SDrawdownPeriod();
if (m_currentDD != NULL)
{
m_currentDD.startTime = TimeCurrent();
m_currentDD.startEquity = m_equityPeak;
m_currentDD.minEquity = m_equity;
m_currentDD.maxDrawdown = m_equityPeak - m_equity;
m_currentDD.maxDrawdownPct = (m_currentDD.maxDrawdown / m_equityPeak) * 100.0;
}
}
// Update existing drawdown
else
{
m_currentDD.minEquity = MathMin(m_currentDD.minEquity, m_equity);
double currentDD = m_equityPeak - m_equity;
double currentDDPct = (currentDD / m_equityPeak) * 100.0;
if (currentDD > m_currentDD.maxDrawdown)
{
m_currentDD.maxDrawdown = currentDD;
m_currentDD.maxDrawdownPct = currentDDPct;
}
}
// Update global max drawdown
if (m_currentDD != NULL && m_currentDD.maxDrawdown > m_maxDrawdown)
{
m_maxDrawdown = m_currentDD.maxDrawdown;
m_maxDrawdownPct = m_currentDD.maxDrawdownPct;
}
}
// Recalculate statistics
CalculateStatistics();
}
//+------------------------------------------------------------------+
//| Update from account history |
//+------------------------------------------------------------------+
void CDrawdownManager::UpdateFromHistory()
{
// This would load historical drawdowns from account history
// Implementation depends on how you want to track historical drawdowns
}
//+------------------------------------------------------------------+
//| Get current drawdown in account currency |
//+------------------------------------------------------------------+
double CDrawdownManager::GetCurrentDrawdown() const
{
if (m_currentDD != NULL)
return m_equityPeak - m_equity;
return 0.0;
}
//+------------------------------------------------------------------+
//| Get current drawdown as percentage of peak equity |
//+------------------------------------------------------------------
double CDrawdownManager::GetCurrentDrawdownPct() const
{
if (m_equityPeak > 0.0)
return ((m_equityPeak - m_equity) / m_equityPeak) * 100.0;
return 0.0;
}
//+------------------------------------------------------------------+
//| Calculate average drawdown |
//+------------------------------------------------------------------
double CDrawdownManager::GetAvgDrawdown() const
{
if (m_ddCount == 0)
return 0.0;
double totalDD = 0.0;
int count = m_drawdowns.Total();
for (int i = 0; i < count; i++)
{
SDrawdownPeriod* dd = (SDrawdownPeriod*)m_drawdowns.At(i);
if (dd != NULL)
{
totalDD += dd.maxDrawdownPct;
}
}
return (count > 0) ? (totalDD / count) : 0.0;
}
//+------------------------------------------------------------------+
//| Check if drawdown limit is reached |
//+------------------------------------------------------------------
bool CDrawdownManager::IsDrawdownLimitReached(const double maxDrawdownPct) const
{
if (maxDrawdownPct <= 0.0)
return false;
double currentDD = GetCurrentDrawdownPct();
return (currentDD >= maxDrawdownPct);
}
//+------------------------------------------------------------------+
//| Check if daily loss limit is reached |
//+------------------------------------------------------------------
bool CDrawdownManager::IsDailyLossLimitReached(const double maxDailyLossPct) const
{
if (maxDailyLossPct <= 0.0)
return false;
// This would check the daily loss from the start of the day
// Implementation depends on how you want to track daily P/L
return false;
}
//+------------------------------------------------------------------+
//| Calculate drawdown statistics |
//+------------------------------------------------------------------
void CDrawdownManager::CalculateStatistics()
{
int count = m_drawdowns.Total();
// Count only closed drawdowns
m_ddCount = 0;
double totalDD = 0.0;
for (int i = 0; i < count; i++)
{
SDrawdownPeriod* dd = (SDrawdownPeriod*)m_drawdowns.At(i);
if (dd != NULL && dd.endTime > 0)
{
totalDD += dd.maxDrawdownPct;
m_ddCount++;
}
}
// Calculate average drawdown
m_avgDrawdown = (m_ddCount > 0) ? (totalDD / m_ddCount) : 0.0;
// Update max drawdown if current drawdown is larger
if (m_currentDD != NULL && m_currentDD.maxDrawdown > m_maxDrawdown)
{
m_maxDrawdown = m_currentDD.maxDrawdown;
m_maxDrawdownPct = m_currentDD.maxDrawdownPct;
}
}
//+------------------------------------------------------------------+
//| Save drawdown data to file |
//+------------------------------------------------------------------
bool CDrawdownManager::SaveToFile(const string filename)
{
// Implementation depends on how you want to persist the data
return false;
}
//+------------------------------------------------------------------+
//| Load drawdown data from file |
//+------------------------------------------------------------------
bool CDrawdownManager::LoadFromFile(const string filename)
{
// Implementation depends on how you want to load the data
return false;
}