forked from Princeec13/mql5
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;
|
|
}
|