//+------------------------------------------------------------------+ //| 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 //+------------------------------------------------------------------+ //| 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; }