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