750 lines
No EOL
56 KiB
MQL5
750 lines
No EOL
56 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| RiskManager.mqh |
|
|
//| Fixed Risk Management Implementation |
|
|
//+------------------------------------------------------------------+
|
|
#ifndef RISK_MANAGER_MQH
|
|
#define RISK_MANAGER_MQH
|
|
|
|
#include "DataTypes.mqh"
|
|
#include "Utilities.mqh"
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Risk Manager Class |
|
|
//+------------------------------------------------------------------+
|
|
class CRiskManager
|
|
{
|
|
private:
|
|
//--- Member Variables - Complete Declaration
|
|
CUtilities* m_pUtils; // Utilities pointer
|
|
RiskParameters m_params; // Risk parameters
|
|
bool m_isInitialized; // Initialization flag
|
|
|
|
//--- Risk Limits
|
|
double m_maxRiskPerTrade; // Max risk per trade (%)
|
|
double m_maxDailyLoss; // Max daily loss (%)
|
|
double m_maxDrawdown; // Max drawdown (%)
|
|
double m_maxLeverage; // Max leverage
|
|
int m_maxPositions; // Max concurrent positions
|
|
double m_maxCorrelation; // Max correlation threshold
|
|
|
|
//--- Current State
|
|
double m_currentDailyLoss; // Today's loss
|
|
double m_currentDrawdown; // Current drawdown
|
|
double m_peakBalance; // Peak balance for DD calc
|
|
datetime m_lastResetTime; // Last daily reset
|
|
|
|
//--- Position Tracking
|
|
PositionRisk m_positions[]; // Array of position risks
|
|
int m_positionCount; // Current position count
|
|
|
|
//--- Risk Metrics Cache
|
|
RiskMetrics m_currentMetrics; // Current risk metrics
|
|
datetime m_lastUpdateTime; // Last metrics update
|
|
|
|
//--- Private Methods
|
|
void UpdateDailyMetrics();
|
|
void UpdateDrawdown();
|
|
void CalculatePositionRisks();
|
|
double CalculateCorrelation(const double &prices1[], const double &prices2[]);
|
|
bool CheckRiskLimits(double proposedRisk);
|
|
void LogRiskEvent(ENUM_RISK_EVENT event, string details);
|
|
double InternalCalculateLotSize(double riskAmount, double stopLossPoints, string symbol);
|
|
|
|
public:
|
|
//--- Constructor/Destructor
|
|
CRiskManager();
|
|
~CRiskManager();
|
|
|
|
//--- Initialization
|
|
bool Initialize(CUtilities* utils, const RiskParameters ¶ms);
|
|
void Deinitialize();
|
|
|
|
//--- Risk Calculation Methods
|
|
double CalculatePositionSize(double stopLossPoints, string symbol = NULL);
|
|
double CalculatePositionSizeByMethod(ENUM_POSITION_SIZING method,
|
|
double balance,
|
|
double stopLossPoints,
|
|
string symbol = NULL);
|
|
double CalculateRiskAmount(double balance, double riskPercent);
|
|
|
|
//--- Risk Assessment
|
|
bool CanOpenNewPosition(string symbol, double proposedVolume);
|
|
bool IsRiskLimitExceeded();
|
|
RiskMetrics GetCurrentRiskMetrics();
|
|
double GetAvailableRisk();
|
|
|
|
//--- Stop Loss Calculation
|
|
double CalculateStopLoss(double entryPrice, bool isBuy,
|
|
ENUM_SL_MODE mode, string symbol = NULL);
|
|
double CalculateTakeProfit(double entryPrice, double stopLoss,
|
|
bool isBuy, double riskRewardRatio);
|
|
|
|
//--- Risk Monitoring
|
|
void UpdateRiskMetrics();
|
|
ENUM_RISK_ACTION EvaluateRiskAction();
|
|
void ExecuteRiskAction(ENUM_RISK_ACTION action);
|
|
|
|
//--- Utility Methods
|
|
double GetATR(string symbol, ENUM_TIMEFRAMES timeframe, int period);
|
|
double GetVolatility(string symbol);
|
|
bool IsHighVolatility(string symbol);
|
|
|
|
//--- Getters
|
|
bool IsInitialized() { return m_isInitialized; }
|
|
double GetMaxRiskPerTrade() { return m_maxRiskPerTrade; }
|
|
double GetCurrentDrawdown() { return m_currentDrawdown; }
|
|
int GetOpenPositionCount() { return m_positionCount; }
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor |
|
|
//+------------------------------------------------------------------+
|
|
CRiskManager::CRiskManager()
|
|
{
|
|
m_pUtils = NULL;
|
|
m_isInitialized = false;
|
|
m_maxRiskPerTrade = 1.0;
|
|
m_maxDailyLoss = 5.0;
|
|
m_maxDrawdown = 20.0;
|
|
m_maxLeverage = 10.0;
|
|
m_maxPositions = 5;
|
|
m_maxCorrelation = 0.7;
|
|
m_currentDailyLoss = 0;
|
|
m_currentDrawdown = 0;
|
|
m_peakBalance = 0;
|
|
m_lastResetTime = 0;
|
|
m_positionCount = 0;
|
|
m_lastUpdateTime = 0;
|
|
|
|
ArrayResize(m_positions, 0);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CRiskManager::~CRiskManager()
|
|
{
|
|
Deinitialize();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize Risk Manager |
|
|
//+------------------------------------------------------------------+
|
|
bool CRiskManager::Initialize(CUtilities* utils, const RiskParameters ¶ms)
|
|
{
|
|
if(utils == NULL)
|
|
{
|
|
Print("RiskManager: Invalid utilities pointer");
|
|
return false;
|
|
}
|
|
|
|
m_pUtils = utils;
|
|
m_params = params;
|
|
|
|
// Store parameters
|
|
m_maxRiskPerTrade = params.maxRiskPerTrade;
|
|
m_maxDailyLoss = params.maxDailyRisk;
|
|
m_maxDrawdown = params.maxDrawdown;
|
|
m_maxLeverage = params.maxLeverage;
|
|
m_maxPositions = params.maxPositions;
|
|
m_maxCorrelation = params.maxCorrelation;
|
|
|
|
// Initialize tracking
|
|
m_peakBalance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
m_lastResetTime = TimeCurrent();
|
|
m_currentDailyLoss = 0;
|
|
m_currentDrawdown = 0;
|
|
|
|
// Log initialization
|
|
CLogger::Log(LOG_INFO, "Risk Manager initialized");
|
|
CLogger::Log(LOG_INFO, StringFormat("Max Risk Per Trade: %.2f%%", m_maxRiskPerTrade));
|
|
CLogger::Log(LOG_INFO, StringFormat("Max Daily Loss: %.2f%%", m_maxDailyLoss));
|
|
CLogger::Log(LOG_INFO, StringFormat("Max Drawdown: %.2f%%", m_maxDrawdown));
|
|
CLogger::Log(LOG_INFO, StringFormat("Max Positions: %d", m_maxPositions));
|
|
|
|
m_isInitialized = true;
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Deinitialize |
|
|
//+------------------------------------------------------------------+
|
|
void CRiskManager::Deinitialize()
|
|
{
|
|
ArrayFree(m_positions);
|
|
m_isInitialized = false;
|
|
CLogger::Log(LOG_INFO, "Risk Manager deinitialized");
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate Position Size |
|
|
//+------------------------------------------------------------------+
|
|
double CRiskManager::CalculatePositionSize(double stopLossPoints, string symbol)
|
|
{
|
|
if(!m_isInitialized) return 0;
|
|
if(symbol == NULL) symbol = _Symbol;
|
|
|
|
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
double riskAmount = CalculateRiskAmount(balance, m_maxRiskPerTrade);
|
|
|
|
return InternalCalculateLotSize(riskAmount, stopLossPoints, symbol);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Internal Calculate Lot Size |
|
|
//+------------------------------------------------------------------+
|
|
double CRiskManager::InternalCalculateLotSize(double riskAmount, double stopLossPoints, string symbol)
|
|
{
|
|
if(stopLossPoints <= 0) return 0;
|
|
|
|
double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
|
|
double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
|
|
double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
|
|
double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
|
|
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
|
|
|
|
if(tickSize <= 0 || tickValue <= 0) return minLot;
|
|
|
|
double lotSize = (riskAmount * tickSize) / (stopLossPoints * tickValue);
|
|
|
|
// Round to lot step
|
|
lotSize = MathFloor(lotSize / lotStep) * lotStep;
|
|
|
|
// Apply limits
|
|
if(lotSize < minLot) lotSize = minLot;
|
|
if(lotSize > maxLot) lotSize = maxLot;
|
|
|
|
return NormalizeDouble(lotSize, 2);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate Position Size by Method |
|
|
//+------------------------------------------------------------------+
|
|
double CRiskManager::CalculatePositionSizeByMethod(ENUM_POSITION_SIZING method,
|
|
double balance,
|
|
double stopLossPoints,
|
|
string symbol)
|
|
{
|
|
if(!m_isInitialized) return 0;
|
|
if(symbol == NULL) symbol = _Symbol;
|
|
if(stopLossPoints <= 0) return 0;
|
|
|
|
double lotSize = 0;
|
|
double riskAmount = CalculateRiskAmount(balance, m_maxRiskPerTrade);
|
|
|
|
switch(method)
|
|
{
|
|
case POSITION_SIZE_FIXED:
|
|
lotSize = 0.01;
|
|
break;
|
|
|
|
case POSITION_SIZE_RISK:
|
|
lotSize = InternalCalculateLotSize(riskAmount, stopLossPoints, symbol);
|
|
break;
|
|
|
|
case POSITION_SIZE_VOLATILITY:
|
|
{
|
|
double atr = GetATR(symbol, PERIOD_CURRENT, 14);
|
|
if(atr > 0)
|
|
{
|
|
double adjustedStop = atr * 2.0;
|
|
double adjustedRisk = riskAmount * 0.75;
|
|
lotSize = InternalCalculateLotSize(adjustedRisk, adjustedStop, symbol);
|
|
}
|
|
else
|
|
{
|
|
lotSize = 0.01;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case POSITION_SIZE_KELLY:
|
|
{
|
|
double winRate = 0.55;
|
|
double avgWinLoss = 1.5;
|
|
double kellyPercent = (winRate * avgWinLoss - (1 - winRate)) / avgWinLoss;
|
|
kellyPercent = MathMax(0, MathMin(kellyPercent, 0.25));
|
|
|
|
double kellyRisk = balance * kellyPercent;
|
|
lotSize = InternalCalculateLotSize(kellyRisk, stopLossPoints, symbol);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
lotSize = 0.01;
|
|
break;
|
|
}
|
|
|
|
// Apply limits
|
|
double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
|
|
double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
|
|
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
|
|
|
|
lotSize = MathFloor(lotSize / lotStep) * lotStep;
|
|
lotSize = MathMax(minLot, MathMin(maxLot, lotSize));
|
|
|
|
return NormalizeDouble(lotSize, 2);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate Risk Amount |
|
|
//+------------------------------------------------------------------+
|
|
double CRiskManager::CalculateRiskAmount(double balance, double riskPercent)
|
|
{
|
|
return balance * (riskPercent / 100.0);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if Can Open New Position |
|
|
//+------------------------------------------------------------------+
|
|
bool CRiskManager::CanOpenNewPosition(string symbol, double proposedVolume)
|
|
{
|
|
if(!m_isInitialized) return false;
|
|
|
|
// Check position count
|
|
if(m_positionCount >= m_maxPositions)
|
|
{
|
|
LogRiskEvent(RISK_OVERLEVERAGED, "Max positions reached");
|
|
return false;
|
|
}
|
|
|
|
// Check daily loss
|
|
if(m_currentDailyLoss >= m_maxDailyLoss)
|
|
{
|
|
LogRiskEvent(RISK_DAILY_LOSS_LIMIT, "Daily loss limit reached");
|
|
return false;
|
|
}
|
|
|
|
// Check drawdown
|
|
if(m_currentDrawdown >= m_maxDrawdown)
|
|
{
|
|
LogRiskEvent(RISK_DRAWDOWN_LIMIT, "Drawdown limit reached");
|
|
return false;
|
|
}
|
|
|
|
// Check margin
|
|
double requiredMargin = 0;
|
|
if(!OrderCalcMargin(ORDER_TYPE_BUY, symbol, proposedVolume,
|
|
SymbolInfoDouble(symbol, SYMBOL_ASK), requiredMargin))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
double freeMargin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
|
|
if(requiredMargin > freeMargin * 0.9) // Keep 10% buffer
|
|
{
|
|
LogRiskEvent(RISK_MARGIN_CALL, "Insufficient margin");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if Risk Limit Exceeded |
|
|
//+------------------------------------------------------------------+
|
|
bool CRiskManager::IsRiskLimitExceeded()
|
|
{
|
|
UpdateRiskMetrics();
|
|
|
|
if(m_currentMetrics.totalRisk > m_maxRiskPerTrade) return true;
|
|
if(m_currentMetrics.currentDrawdown > m_maxDrawdown) return true;
|
|
if(m_currentMetrics.dailyLossPercent > m_maxDailyLoss) return true;
|
|
if(m_currentMetrics.leverage > m_maxLeverage) return true;
|
|
if(m_currentMetrics.marginLevel < 100 && m_currentMetrics.marginLevel > 0) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get Current Risk Metrics |
|
|
//+------------------------------------------------------------------+
|
|
RiskMetrics CRiskManager::GetCurrentRiskMetrics()
|
|
{
|
|
UpdateRiskMetrics();
|
|
return m_currentMetrics;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get Available Risk |
|
|
//+------------------------------------------------------------------+
|
|
double CRiskManager::GetAvailableRisk()
|
|
{
|
|
double usedRisk = m_currentMetrics.totalRisk;
|
|
double availableRisk = m_maxRiskPerTrade - usedRisk;
|
|
return MathMax(0, availableRisk);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate Stop Loss |
|
|
//+------------------------------------------------------------------+
|
|
double CRiskManager::CalculateStopLoss(double entryPrice, bool isBuy,
|
|
ENUM_SL_MODE mode, string symbol)
|
|
{
|
|
if(symbol == NULL) symbol = _Symbol;
|
|
|
|
double sl = 0;
|
|
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
|
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
|
|
|
|
switch(mode)
|
|
{
|
|
case SL_FIXED:
|
|
sl = isBuy ? entryPrice - (100 * point) : entryPrice + (100 * point);
|
|
break;
|
|
|
|
case SL_ATR:
|
|
{
|
|
double atr = GetATR(symbol, PERIOD_CURRENT, 14);
|
|
if(atr > 0)
|
|
{
|
|
sl = isBuy ? entryPrice - (atr * 2.0) : entryPrice + (atr * 2.0);
|
|
}
|
|
else
|
|
{
|
|
sl = isBuy ? entryPrice - (100 * point) : entryPrice + (100 * point);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SL_PERCENTAGE:
|
|
sl = isBuy ? entryPrice * 0.98 : entryPrice * 1.02;
|
|
break;
|
|
|
|
default:
|
|
sl = isBuy ? entryPrice - (100 * point) : entryPrice + (100 * point);
|
|
break;
|
|
}
|
|
|
|
return NormalizeDouble(sl, digits);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate Take Profit |
|
|
//+------------------------------------------------------------------+
|
|
double CRiskManager::CalculateTakeProfit(double entryPrice, double stopLoss,
|
|
bool isBuy, double riskRewardRatio)
|
|
{
|
|
double risk = MathAbs(entryPrice - stopLoss);
|
|
double reward = risk * riskRewardRatio;
|
|
|
|
double tp = isBuy ? entryPrice + reward : entryPrice - reward;
|
|
|
|
string symbol = _Symbol;
|
|
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
|
|
|
|
return NormalizeDouble(tp, digits);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Update Risk Metrics |
|
|
//+------------------------------------------------------------------+
|
|
void CRiskManager::UpdateRiskMetrics()
|
|
{
|
|
// Update daily metrics if new day
|
|
UpdateDailyMetrics();
|
|
|
|
// Update drawdown
|
|
UpdateDrawdown();
|
|
|
|
// Calculate position risks
|
|
CalculatePositionRisks();
|
|
|
|
// Calculate total metrics
|
|
double totalRisk = 0;
|
|
double totalExposure = 0;
|
|
|
|
for(int i = 0; i < m_positionCount; i++)
|
|
{
|
|
totalRisk += m_positions[i].riskPercent;
|
|
totalExposure += m_positions[i].volume * m_positions[i].marginUsed;
|
|
}
|
|
|
|
// Update metrics structure
|
|
m_currentMetrics.totalRisk = totalRisk;
|
|
m_currentMetrics.maxRisk = m_maxRiskPerTrade;
|
|
m_currentMetrics.currentDrawdown = m_currentDrawdown;
|
|
m_currentMetrics.maxDrawdown = m_maxDrawdown;
|
|
m_currentMetrics.dailyLoss = m_currentDailyLoss;
|
|
m_currentMetrics.dailyLossPercent = (AccountInfoDouble(ACCOUNT_BALANCE) > 0) ?
|
|
(m_currentDailyLoss / AccountInfoDouble(ACCOUNT_BALANCE)) * 100 : 0;
|
|
m_currentMetrics.marginLevel = AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
|
|
m_currentMetrics.freeMargin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
|
|
m_currentMetrics.leverage = (AccountInfoDouble(ACCOUNT_EQUITY) > 0) ?
|
|
totalExposure / AccountInfoDouble(ACCOUNT_EQUITY) : 0;
|
|
m_currentMetrics.openPositions = m_positionCount;
|
|
|
|
m_lastUpdateTime = TimeCurrent();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Update Daily Metrics |
|
|
//+------------------------------------------------------------------+
|
|
void CRiskManager::UpdateDailyMetrics()
|
|
{
|
|
MqlDateTime current, last;
|
|
TimeToStruct(TimeCurrent(), current);
|
|
TimeToStruct(m_lastResetTime, last);
|
|
|
|
// Reset daily metrics if new day
|
|
if(current.day != last.day || current.mon != last.mon || current.year != last.year)
|
|
{
|
|
m_currentDailyLoss = 0;
|
|
m_lastResetTime = TimeCurrent();
|
|
CLogger::Log(LOG_INFO, "Daily risk metrics reset");
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Update Drawdown |
|
|
//+------------------------------------------------------------------+
|
|
void CRiskManager::UpdateDrawdown()
|
|
{
|
|
double currentBalance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
|
|
if(currentBalance > m_peakBalance)
|
|
{
|
|
m_peakBalance = currentBalance;
|
|
}
|
|
|
|
if(m_peakBalance > 0)
|
|
{
|
|
m_currentDrawdown = ((m_peakBalance - currentBalance) / m_peakBalance) * 100;
|
|
}
|
|
else
|
|
{
|
|
m_currentDrawdown = 0;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate Position Risks |
|
|
//+------------------------------------------------------------------+
|
|
void CRiskManager::CalculatePositionRisks()
|
|
{
|
|
ArrayResize(m_positions, 0);
|
|
m_positionCount = 0;
|
|
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(ticket > 0 && PositionSelectByTicket(ticket))
|
|
{
|
|
PositionRisk risk;
|
|
risk.ticket = ticket;
|
|
risk.symbol = PositionGetString(POSITION_SYMBOL);
|
|
risk.volume = PositionGetDouble(POSITION_VOLUME);
|
|
|
|
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
|
|
double stopLoss = PositionGetDouble(POSITION_SL);
|
|
|
|
// Calculate risk amount
|
|
if(stopLoss > 0)
|
|
{
|
|
double riskPoints = MathAbs(openPrice - stopLoss);
|
|
double tickValue = SymbolInfoDouble(risk.symbol, SYMBOL_TRADE_TICK_VALUE);
|
|
double tickSize = SymbolInfoDouble(risk.symbol, SYMBOL_TRADE_TICK_SIZE);
|
|
|
|
if(tickSize > 0)
|
|
{
|
|
risk.riskAmount = (riskPoints / tickSize) * tickValue * risk.volume;
|
|
}
|
|
}
|
|
|
|
risk.riskPercent = (AccountInfoDouble(ACCOUNT_BALANCE) > 0) ?
|
|
(risk.riskAmount / AccountInfoDouble(ACCOUNT_BALANCE)) * 100 : 0;
|
|
risk.currentLoss = PositionGetDouble(POSITION_PROFIT);
|
|
|
|
// Calculate margin used (MQL5 doesn't have POSITION_MARGIN directly)
|
|
double marginInit = 0;
|
|
double marginMaint = 0;
|
|
if(OrderCalcMargin(ORDER_TYPE_BUY, risk.symbol, risk.volume,
|
|
openPrice, marginInit))
|
|
{
|
|
risk.marginUsed = marginInit;
|
|
}
|
|
else
|
|
{
|
|
risk.marginUsed = 0;
|
|
}
|
|
|
|
ArrayResize(m_positions, m_positionCount + 1);
|
|
m_positions[m_positionCount] = risk;
|
|
m_positionCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Evaluate Risk Action |
|
|
//+------------------------------------------------------------------+
|
|
ENUM_RISK_ACTION CRiskManager::EvaluateRiskAction()
|
|
{
|
|
if(!m_isInitialized) return ACTION_NONE;
|
|
|
|
UpdateRiskMetrics();
|
|
|
|
// Critical - close all
|
|
if(m_currentMetrics.marginLevel < 50 && m_currentMetrics.marginLevel > 0)
|
|
return ACTION_CLOSE_ALL;
|
|
|
|
if(m_currentMetrics.currentDrawdown > m_maxDrawdown)
|
|
return ACTION_CLOSE_ALL;
|
|
|
|
// High risk - close partial
|
|
if(m_currentMetrics.dailyLossPercent > m_maxDailyLoss * 0.8)
|
|
return ACTION_CLOSE_PARTIAL;
|
|
|
|
if(m_currentMetrics.totalRisk > m_maxRiskPerTrade * 0.9)
|
|
return ACTION_REDUCE_SIZE;
|
|
|
|
// Warning only
|
|
if(m_currentMetrics.leverage > m_maxLeverage * 0.8)
|
|
return ACTION_WARN;
|
|
|
|
return ACTION_NONE;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Execute Risk Action |
|
|
//+------------------------------------------------------------------+
|
|
void CRiskManager::ExecuteRiskAction(ENUM_RISK_ACTION action)
|
|
{
|
|
switch(action)
|
|
{
|
|
case ACTION_WARN:
|
|
Alert("Risk Warning: Approaching risk limits");
|
|
break;
|
|
|
|
case ACTION_REDUCE_SIZE:
|
|
CLogger::Log(LOG_WARNING, "Reducing position sizes due to high risk");
|
|
break;
|
|
|
|
case ACTION_CLOSE_PARTIAL:
|
|
CLogger::Log(LOG_WARNING, "Closing partial positions due to risk limits");
|
|
break;
|
|
|
|
case ACTION_CLOSE_ALL:
|
|
CLogger::Log(LOG_ERROR, "EMERGENCY: Closing all positions");
|
|
break;
|
|
|
|
case ACTION_STOP_TRADING:
|
|
CLogger::Log(LOG_ERROR, "Trading suspended due to risk limits");
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get ATR Value |
|
|
//+------------------------------------------------------------------+
|
|
double CRiskManager::GetATR(string symbol, ENUM_TIMEFRAMES timeframe, int period)
|
|
{
|
|
int atrHandle = iATR(symbol, timeframe, period);
|
|
if(atrHandle == INVALID_HANDLE) return 0;
|
|
|
|
double atr[];
|
|
ArraySetAsSeries(atr, true);
|
|
|
|
if(CopyBuffer(atrHandle, 0, 0, 1, atr) <= 0)
|
|
{
|
|
IndicatorRelease(atrHandle);
|
|
return 0;
|
|
}
|
|
|
|
IndicatorRelease(atrHandle);
|
|
return atr[0];
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get Volatility |
|
|
//+------------------------------------------------------------------+
|
|
double CRiskManager::GetVolatility(string symbol)
|
|
{
|
|
return GetATR(symbol, PERIOD_H1, 14);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if High Volatility |
|
|
//+------------------------------------------------------------------+
|
|
bool CRiskManager::IsHighVolatility(string symbol)
|
|
{
|
|
double currentVol = GetVolatility(symbol);
|
|
double avgVol = GetATR(symbol, PERIOD_D1, 20);
|
|
|
|
return (avgVol > 0 && currentVol > avgVol * 1.5);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate Correlation |
|
|
//+------------------------------------------------------------------+
|
|
double CRiskManager::CalculateCorrelation(const double &prices1[], const double &prices2[])
|
|
{
|
|
int n = MathMin(ArraySize(prices1), ArraySize(prices2));
|
|
if(n < 2) return 0;
|
|
|
|
double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0, sumY2 = 0;
|
|
|
|
for(int i = 0; i < n; i++)
|
|
{
|
|
sumX += prices1[i];
|
|
sumY += prices2[i];
|
|
sumXY += prices1[i] * prices2[i];
|
|
sumX2 += prices1[i] * prices1[i];
|
|
sumY2 += prices2[i] * prices2[i];
|
|
}
|
|
|
|
double numerator = n * sumXY - sumX * sumY;
|
|
double denominator = MathSqrt((n * sumX2 - sumX * sumX) * (n * sumY2 - sumY * sumY));
|
|
|
|
if(denominator == 0) return 0;
|
|
|
|
return numerator / denominator;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check Risk Limits |
|
|
//+------------------------------------------------------------------+
|
|
bool CRiskManager::CheckRiskLimits(double proposedRisk)
|
|
{
|
|
double totalRisk = m_currentMetrics.totalRisk + proposedRisk;
|
|
return (totalRisk <= m_maxRiskPerTrade);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Log Risk Event |
|
|
//+------------------------------------------------------------------+
|
|
void CRiskManager::LogRiskEvent(ENUM_RISK_EVENT event, string details)
|
|
{
|
|
// Log the risk event directly using CUtilities static method
|
|
string eventName = "";
|
|
switch(event)
|
|
{
|
|
case RISK_OVERLEVERAGED: eventName = "OVERLEVERAGED"; break;
|
|
case RISK_CORRELATION_HIGH: eventName = "HIGH_CORRELATION"; break;
|
|
case RISK_DRAWDOWN_LIMIT: eventName = "DRAWDOWN_LIMIT"; break;
|
|
case RISK_DAILY_LOSS_LIMIT: eventName = "DAILY_LOSS_LIMIT"; break;
|
|
case RISK_MARGIN_CALL: eventName = "MARGIN_CALL"; break;
|
|
case RISK_VOLATILITY_SPIKE: eventName = "VOLATILITY_SPIKE"; break;
|
|
case RISK_LIQUIDITY_ISSUE: eventName = "LIQUIDITY_ISSUE"; break;
|
|
default: eventName = "UNKNOWN"; break;
|
|
}
|
|
|
|
// Log the event
|
|
CLogger::Log(LOG_WARNING, "Risk Event: " + eventName);
|
|
|
|
// Log details if provided
|
|
if(details != "")
|
|
{
|
|
CLogger::Log(LOG_WARNING, "Risk Event Details: " + details);
|
|
}
|
|
|
|
// Also call the static CUtilities method if you want to use it
|
|
// Note: CUtilities::LogRiskEvent is a static method that takes only the event parameter
|
|
CUtilities::LogRiskEvent(event);
|
|
}
|
|
|
|
#endif // RISK_MANAGER_MQH |