566 lines
18 KiB
MQL5
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;
| |||
}
|