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

566 lines
18 KiB
MQL5

//+------------------------------------------------------------------+
//| RiskMetrics.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 <Math\Stat\Stat.mqh>
//+------------------------------------------------------------------+
//| Risk Metrics Class |
//+------------------------------------------------------------------+
class CRiskMetrics
{
private:
// Market data
string m_symbol; // Symbol
ENUM_TIMEFRAMES m_timeframe; // Timeframe
// Volatility metrics
double m_atr; // Average True Range
double m_volatility; // Current volatility (standard deviation)
double m_historicalVol; // Historical volatility
// Risk metrics
double m_var95; // Value at Risk (95% confidence)
double m_es95; // Expected Shortfall (95% confidence)
double m_maxDrawdown; // Maximum drawdown
double m_sharpeRatio; // Sharpe ratio
double m_sortinoRatio; // Sortino ratio
// Technical indicators
int m_atrHandle; // ATR indicator handle
int m_stdDevHandle; // Standard Deviation handle
// Parameters
int m_lookbackPeriod; // Lookback period for metrics
int m_volatilityPeriod; // Period for volatility calculation
// Internal state
bool m_isInitialized; // Initialization flag
// Private methods
bool CalculateVolatility();
bool CalculateValueAtRisk();
bool CalculateExpectedShortfall();
bool CalculateDrawdown();
bool CalculateRatios();
public:
// Constructor/Destructor
CRiskMetrics();
~CRiskMetrics();
// Initialization
bool Initialize(const string symbol, const ENUM_TIMEFRAMES timeframe,
const int lookbackPeriod = 100, const int volatilityPeriod = 20);
void Deinitialize();
// Update methods
bool UpdateVolatility();
void UpdateAllMetrics();
// Getters
double GetCurrentVolatility() const { return m_volatility; }
double GetHistoricalVolatility() const { return m_historicalVol; }
double GetValueAtRisk() const { return m_var95; }
double GetExpectedShortfall() const { return m_es95; }
double GetMaxDrawdown() const { return m_maxDrawdown; }
double GetSharpeRatio() const { return m_sharpeRatio; }
double GetSortinoRatio() const { return m_sortinoRatio; }
double GetATR() const { return m_atr; }
// Utility methods
static double CalculateStopLossPips(const string symbol, const double price,
const double stopDistance, const bool isPips = true);
static double CalculatePositionRisk(const double entryPrice, const double stopLoss,
const double positionSize, const string symbol);
// Risk assessment
bool IsVolatilityTooHigh(const double maxVolatility = 0.02) const;
bool IsDrawdownLimitReached(const double maxDrawdownPercent) const;
// Statistical methods
static double CalculateStandardDeviation(const double &values[], const int count);
static double CalculateCorrelation(const double &x[], const double &y[], const int count);
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CRiskMetrics::CRiskMetrics() :
m_symbol(""),
m_timeframe(PERIOD_CURRENT),
m_atr(0.0),
m_volatility(0.0),
m_historicalVol(0.0),
m_var95(0.0),
m_es95(0.0),
m_maxDrawdown(0.0),
m_sharpeRatio(0.0),
m_sortinoRatio(0.0),
m_atrHandle(INVALID_HANDLE),
m_stdDevHandle(INVALID_HANDLE),
m_lookbackPeriod(100),
m_volatilityPeriod(20),
m_isInitialized(false)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CRiskMetrics::~CRiskMetrics()
{
Deinitialize();
}
//+------------------------------------------------------------------+
//| Initialize risk metrics calculator |
//+------------------------------------------------------------------+
bool CRiskMetrics::Initialize(const string symbol, const ENUM_TIMEFRAMES timeframe,
const int lookbackPeriod = 100, const int volatilityPeriod = 20)
{
if (m_isInitialized)
return true;
if (symbol == "" || lookbackPeriod <= 0 || volatilityPeriod <= 0)
return false;
m_symbol = symbol;
m_timeframe = timeframe;
m_lookbackPeriod = lookbackPeriod;
m_volatilityPeriod = volatilityPeriod;
// Initialize indicators
m_atrHandle = iATR(m_symbol, m_timeframe, 14);
m_stdDevHandle = iStdDev(m_symbol, m_timeframe, 20, 0, MODE_SMA, PRICE_CLOSE);
if (m_atrHandle == INVALID_HANDLE || m_stdDevHandle == INVALID_HANDLE)
{
Print("Failed to initialize indicators");
return false;
}
// Initial calculations
if (!UpdateAllMetrics())
{
Print("Failed to calculate initial metrics");
return false;
}
m_isInitialized = true;
return true;
}
//+------------------------------------------------------------------+
//| Deinitialize risk metrics calculator |
//+------------------------------------------------------------------+
void CRiskMetrics::Deinitialize()
{
// Release indicators
if (m_atrHandle != INVALID_HANDLE)
{
IndicatorRelease(m_atrHandle);
m_atrHandle = INVALID_HANDLE;
}
if (m_stdDevHandle != INVALID_HANDLE)
{
IndicatorRelease(m_stdDevHandle);
m_stdDevHandle = INVALID_HANDLE;
}
m_isInitialized = false;
}
//+------------------------------------------------------------------+
//| Update volatility metrics |
//+------------------------------------------------------------------+
bool CRiskMetrics::UpdateVolatility()
{
if (!m_isInitialized)
return false;
// Get ATR value
double atrBuffer[];
if (CopyBuffer(m_atrHandle, 0, 0, 1, atrBuffer) != 1)
return false;
m_atr = atrBuffer[0];
// Get standard deviation
double stdDevBuffer[];
if (CopyBuffer(m_stdDevHandle, 0, 0, 1, stdDevBuffer) != 1)
return false;
m_volatility = stdDevBuffer[0];
// Calculate historical volatility
if (!CalculateVolatility())
return false;
return true;
}
//+------------------------------------------------------------------+
//| Update all risk metrics |
//+------------------------------------------------------------------+
void CRiskMetrics::UpdateAllMetrics()
{
if (!m_isInitialized)
return;
// Update volatility first as it's used by other calculations
UpdateVolatility();
// Calculate other metrics
CalculateValueAtRisk();
CalculateExpectedShortfall();
CalculateDrawdown();
CalculateRatios();
}
//+------------------------------------------------------------------+
//| Calculate volatility metrics |
//+------------------------------------------------------------------+
bool CRiskMetrics::CalculateVolatility()
{
// Get close prices for volatility calculation
double close[];
int count = m_volatilityPeriod;
if (CopyClose(m_symbol, m_timeframe, 0, count, close) != count)
return false;
// Calculate returns
double returns[];
ArrayResize(returns, count-1);
for (int i = 0; i < count-1; i++)
{
if (close[i+1] > 0.0)
returns[i] = (close[i] - close[i+1]) / close[i+1];
else
returns[i] = 0.0;
}
// Calculate standard deviation of returns (annualized)
double sum = 0.0;
double sumSq = 0.0;
for (int i = 0; i < ArraySize(returns); i++)
{
sum += returns[i];
sumSq += returns[i] * returns[i];
}
double mean = sum / ArraySize(returns);
double variance = (sumSq / ArraySize(returns)) - (mean * mean);
// Annualize the volatility (assuming 252 trading days)
m_historicalVol = MathSqrt(variance) * MathSqrt(252.0);
return true;
}
//+------------------------------------------------------------------+
//| Calculate Value at Risk (VaR) |
//+------------------------------------------------------------------+
bool CRiskMetrics::CalculateValueAtRisk()
{
// Get close prices
double close[];
int count = m_lookbackPeriod;
if (CopyClose(m_symbol, m_timeframe, 0, count, close) != count)
return false;
// Calculate returns
double returns[];
ArrayResize(returns, count-1);
for (int i = 0; i < count-1; i++)
{
if (close[i+1] > 0.0)
returns[i] = (close[i] - close[i+1]) / close[i+1];
else
returns[i] = 0.0;
}
// Sort returns for historical simulation
ArraySort(returns);
// Calculate 95% VaR (5th percentile)
int index = (int)MathFloor(0.05 * ArraySize(returns));
m_var95 = returns[index];
return true;
}
//+------------------------------------------------------------------+
//| Calculate Expected Shortfall (ES) |
//+------------------------------------------------------------------+
bool CRiskMetrics::CalculateExpectedShortfall()
{
// This is a simplified implementation
// In practice, you might want to use a more sophisticated method
// Get close prices
double close[];
int count = m_lookbackPeriod;
if (CopyClose(m_symbol, m_timeframe, 0, count, close) != count)
return false;
// Calculate returns
double returns[];
ArrayResize(returns, count-1);
for (int i = 0; i < count-1; i++)
{
if (close[i+1] > 0.0)
returns[i] = (close[i] - close[i+1]) / close[i+1];
else
returns[i] = 0.0;
}
// Sort returns for historical simulation
ArraySort(returns);
// Calculate 95% ES (average of worst 5% returns)
int varIndex = (int)MathFloor(0.05 * ArraySize(returns));
double sum = 0.0;
for (int i = 0; i <= varIndex; i++)
{
sum += returns[i];
}
m_es95 = sum / (varIndex + 1);
return true;
}
//+------------------------------------------------------------------+
//| Calculate drawdown metrics |
//+------------------------------------------------------------------+
bool CRiskMetrics::CalculateDrawdown()
{
// This is a simplified implementation
// In practice, you might want to track drawdowns over time
// Get equity curve (simplified as close prices for this example)
double close[];
int count = m_lookbackPeriod;
if (CopyClose(m_symbol, m_timeframe, 0, count, close) != count)
return false;
// Calculate drawdown
double peak = close[0];
double maxDD = 0.0;
for (int i = 1; i < count; i++)
{
if (close[i] > peak)
{
peak = close[i];
}
else
{
double dd = (peak - close[i]) / peak;
if (dd > maxDD)
maxDD = dd;
}
}
m_maxDrawdown = maxDD;
return true;
}
//+------------------------------------------------------------------+
//| Calculate performance ratios |
//+------------------------------------------------------------------+
bool CRiskMetrics::CalculateRatios()
{
// This is a simplified implementation
// In practice, you would use actual returns and risk-free rate
// Get close prices
double close[];
int count = m_lookbackPeriod;
if (CopyClose(m_symbol, m_timeframe, 0, count, close) != count)
return false;
// Calculate returns
double returns[];
ArrayResize(returns, count-1);
for (int i = 0; i < count-1; i++)
{
if (close[i+1] > 0.0)
returns[i] = (close[i] - close[i+1]) / close[i+1];
else
returns[i] = 0.0;
}
// Calculate mean return
double sum = 0.0;
for (int i = 0; i < ArraySize(returns); i++)
{
sum += returns[i];
}
double meanReturn = sum / ArraySize(returns);
// Calculate standard deviation of returns
double sumSq = 0.0;
for (int i = 0; i < ArraySize(returns); i++)
{
double diff = returns[i] - meanReturn;
sumSq += diff * diff;
}
double stdDev = MathSqrt(sumSq / (ArraySize(returns) - 1));
// Risk-free rate (simplified as 0 for this example)
double riskFreeRate = 0.0;
// Calculate Sharpe ratio
if (stdDev > 0.0)
m_sharpeRatio = (meanReturn - riskFreeRate) / stdDev;
else
m_sharpeRatio = 0.0;
// Calculate Sortino ratio (simplified, using downside deviation = stdDev)
// In practice, you would calculate downside deviation separately
if (stdDev > 0.0)
m_sortinoRatio = (meanReturn - riskFreeRate) / stdDev;
else
m_sortinoRatio = 0.0;
return true;
}
//+------------------------------------------------------------------+
//| Calculate stop loss in pips |
//+------------------------------------------------------------------+
double CRiskMetrics::CalculateStopLossPips(const string symbol, const double price,
const double stopDistance, const bool isPips = true)
{
if (stopDistance <= 0.0)
return 0.0;
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
if (point == 0.0 || tickSize == 0.0)
return 0.0;
if (isPips)
{
// Convert pips to price
double pipValue = point * 10.0; // Assuming 5-digit broker
return stopDistance * pipValue;
}
else
{
// Already in price terms
return stopDistance;
}
}
//+------------------------------------------------------------------+
//| Calculate position risk in account currency |
//+------------------------------------------------------------------+
double CRiskMetrics::CalculatePositionRisk(const double entryPrice, const double stopLoss,
const double positionSize, const string symbol)
{
if (entryPrice <= 0.0 || positionSize <= 0.0)
return 0.0;
double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
if (tickValue <= 0.0 || tickSize <= 0.0)
return 0.0;
double stopDistance = MathAbs(entryPrice - stopLoss);
double riskPerLot = (stopDistance / tickSize) * tickValue;
return riskPerLot * positionSize;
}
//+------------------------------------------------------------------+
//| Check if volatility is too high |
//+------------------------------------------------------------------+
bool CRiskMetrics::IsVolatilityTooHigh(const double maxVolatility = 0.02) const
{
return (m_volatility > maxVolatility);
}
//+------------------------------------------------------------------+
//| Check if drawdown limit is reached |
//+------------------------------------------------------------------+
bool CRiskMetrics::IsDrawdownLimitReached(const double maxDrawdownPercent) const
{
return (m_maxDrawdown >= maxDrawdownPercent / 100.0);
}
//+------------------------------------------------------------------+
//| Calculate standard deviation |
//+------------------------------------------------------------------+
double CRiskMetrics::CalculateStandardDeviation(const double &values[], const int count)
{
if (count <= 1)
return 0.0;
double sum = 0.0;
double sumSq = 0.0;
for (int i = 0; i < count; i++)
{
sum += values[i];
sumSq += values[i] * values[i];
}
double mean = sum / count;
double variance = (sumSq / count) - (mean * mean);
return MathSqrt(variance);
}
//+------------------------------------------------------------------+
//| Calculate correlation between two arrays |
//+------------------------------------------------------------------+
double CRiskMetrics::CalculateCorrelation(const double &x[], const double &y[], const int count)
{
if (count <= 1)
return 0.0;
double sumX = 0.0, sumY = 0.0;
double sumXSq = 0.0, sumYSq = 0.0;
double sumXY = 0.0;
for (int i = 0; i < count; i++)
{
sumX += x[i];
sumY += y[i];
sumXSq += x[i] * x[i];
sumYSq += y[i] * y[i];
sumXY += x[i] * y[i];
}
double numerator = sumXY - (sumX * sumY / count);
double denominator = MathSqrt((sumXSq - (sumX * sumX / count)) * (sumYSq - (sumY * sumY / count)));
if (denominator == 0.0)
return 0.0;
return numerator / denominator;
}