356 lines
12 KiB
MQL5
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;
| |||
}
|