mql5/Shared Projects/ERMT-ML/Modules-ML8x/RiskManager_v71.mqh

873 lines
29 KiB
MQL5
Raw Permalink Normal View History

Module Integration Summary for External Trade Management Overview To fully integrate the enhanced external trade management system, updates are required to 5 out of 7 existing modules. The updates maintain backward compatibility while adding new functionality for external trade handling. Module Update Requirements 🟢 No Updates Required (2 modules) TechnicalAnalysis.mqh - Already provides necessary calculations EntrySystem.mqh - Only handles EA's own entry signals 🟡 Minor Updates (2 modules) DataTypes.mqh - Add external trade structures and fields Utilities.mqh - Enhanced logging for external trades 🟠 Moderate Updates (3 modules) RiskManager.mqh - Enhanced risk enforcement methods TradeManager.mqh - Improved stop management for externals Dashboard.mqh - Display external trade information Integration Steps Phase 1: Data Structures (DataTypes.mqh) Add ENUM_EXTERNAL_STATUS enumeration Extend ManagedTrade structure with external-specific fields Add ExternalTradeStats structure for metrics Update DashboardConfig with show_external flag Key additions: external_status - Track state of external trade source_name - Identify where trade came from stops_modified - Track if we modified the trade original_sl/tp - Store original values for comparison Phase 2: Risk Management (RiskManager.mqh) Add EnforceRiskRulesEnhanced() method Implement GetExternalExposure() for risk aggregation Add UpdateExternalStats() for tracking Enhance ValidateAndAdjustRiskExternal() method Key features: Separate risk calculation for external trades Cache mechanism for performance Statistical tracking of external positions Smart risk adjustment without closing trades Phase 3: Trade Management (TradeManager.mqh) Add ApplyDefaultStopsEnhanced() with better logic Implement OverrideExternalStops() with smart override Create ManageExternalTrade() with different rules Add ApplyBreakevenExternal() with wider triggers Key features: Smart stop override (only improve, never worsen) Different management rules for external trades Respect minimum broker distances Track modification success/failure rates Phase 4: User Interface (Dashboard.mqh) Add CreateExternalSection() for display area Implement UpdateExternalSection() for real-time updates Add SetCustomText() for flexible display Create ShowExternalTrades() toggle method Key features: Real-time external trade count and risk Color-coded risk warnings List of active external positions Modification statistics display Phase 5: Logging (Utilities.mqh) Add LogExternalTrade() for detailed event logging Create separate CSV log for external trades Enhance GenerateReportEnhanced() with external section Add IdentifyTradeSource() for magic number interpretation Key features: Separate CSV log for external trade events Detailed tracking of all modifications Source identification from magic numbers Enhanced reporting with external statistics
2025-08-27 14:21:02 +01:00
//+------------------------------------------------------------------+
//| RiskManager_v71.mqh |
//| Advanced Risk Management Module v7.1 |
//| VaR/CVaR, Portfolio Optimization, Kelly ML |
//+------------------------------------------------------------------+
#ifndef RISK_MANAGER_V71_MQH
#define RISK_MANAGER_V71_MQH
#include "DataTypes_v71.mqh"
#include <Math/Stat/Math.mqh>
#include <Math/Alglib/alglib.mqh>
//+------------------------------------------------------------------+
//| Risk Manager Class - Institutional Grade |
//+------------------------------------------------------------------+
class CRiskManagerV71
{
private:
//--- Configuration
RiskManagerConfigV71 m_config;
//--- Risk tracking
double m_daily_loss;
double m_daily_profit;
double m_session_drawdown;
datetime m_last_reset;
//--- Portfolio metrics
double m_portfolio_var;
double m_portfolio_cvar;
double m_portfolio_beta;
double m_max_correlation;
//--- Historical data for VaR
double m_returns_history[];
int m_history_size;
//--- Kelly criterion parameters
double m_win_rate;
double m_avg_win_loss_ratio;
double m_kelly_fraction;
//--- Optimization matrices
CMatrixDouble m_covariance_matrix;
CMatrixDouble m_correlation_matrix;
//--- Helper methods
double CalculateHistoricalVaR(double &returns[], double confidence);
double CalculateParametricVaR(double mean, double std_dev, double confidence);
double CalculateCornishFisherVaR(double &returns[], double confidence);
void UpdateCovarianceMatrix(string &symbols[], int period);
double OptimizeKellyFraction(double win_rate, double avg_win, double avg_loss);
public:
CRiskManagerV71();
~CRiskManagerV71();
//--- Initialization
bool Initialize(const RiskManagerConfigV71 &config);
void Reset();
//--- Position sizing methods
double CalculatePositionSize(string symbol, double stop_distance);
double CalculateOptimalSize(string symbol, double stop_distance,
double expected_return, CorrelationMatrix &correlations);
double CalculateKellySizeML(string symbol, double stop_distance,
double ml_confidence, double predicted_return);
double CalculateRiskParitySize(ManagedTradeV71 &trades[], string symbol);
//--- Risk validation
bool ValidateNewPosition(string symbol, double volume, double stop_loss);
bool ValidatePortfolioRisk(ManagedTradeV71 &trades[]);
bool CheckDailyDrawdown();
bool CheckVaRLimit(double position_var);
//--- Risk metrics calculation
double CalculatePortfolioVaR(ManagedTradeV71 &trades[], double confidence, int horizon);
double CalculatePortfolioCVaR(ManagedTradeV71 &trades[], double confidence, int horizon);
double CalculatePortfolioBeta(ManagedTradeV71 &trades[]);
double CalculateCorrelationRisk(ManagedTradeV71 &trade, CorrelationMatrix &matrix);
double GetCurrentExposure(ManagedTradeV71 &trades[]);
//--- Portfolio optimization
void CalculateOptimalWeights(double &expected_returns[], CorrelationMatrix &correlations,
double target_risk, double &weights[]);
void CalculateEfficientFrontier(double &expected_returns[], CorrelationMatrix &correlations,
double &risks[], double &returns[]);
//--- Risk enforcement
void EnforceRiskRulesOptimized(ManagedTradeV71 &trade);
void AdjustPositionForRisk(ManagedTradeV71 &trade);
bool ShouldReduceRisk(ManagedTradeV71 &trades[]);
//--- Stress testing
double RunStressTest(ManagedTradeV71 &trades[], double stress_factor);
double CalculateStressedVaR(ManagedTradeV71 &trades[], double confidence);
//--- Update methods
void UpdateDailyMetrics();
void UpdateKellyParameters(double win_rate, double avg_win, double avg_loss);
void UpdateHistoricalReturns(double return_value);
//--- Getters
double GetPortfolioVaR() { return m_portfolio_var; }
double GetPortfolioCVaR() { return m_portfolio_cvar; }
double GetMaxCorrelation() { return m_max_correlation; }
double GetKellyFraction() { return m_kelly_fraction; }
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CRiskManagerV71::CRiskManagerV71()
{
m_daily_loss = 0;
m_daily_profit = 0;
m_session_drawdown = 0;
m_last_reset = 0;
m_portfolio_var = 0;
m_portfolio_cvar = 0;
m_portfolio_beta = 0;
m_max_correlation = 0;
m_history_size = 0;
m_win_rate = 0.5;
m_avg_win_loss_ratio = 1.5;
m_kelly_fraction = 0.25;
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CRiskManagerV71::~CRiskManagerV71()
{
ArrayFree(m_returns_history);
}
//+------------------------------------------------------------------+
//| Initialize risk manager |
//+------------------------------------------------------------------+
bool CRiskManagerV71::Initialize(const RiskManagerConfigV71 &config)
{
m_config = config;
//--- Initialize returns history
ArrayResize(m_returns_history, m_config.var_params.lookback_period);
ArrayInitialize(m_returns_history, 0);
m_history_size = 0;
//--- Initialize matrices
m_covariance_matrix.Resize(10, 10);
m_correlation_matrix.Resize(10, 10);
//--- Reset daily metrics
Reset();
Print("RiskManagerV71 initialized with VaR limit: ", m_config.daily_var_limit, "%");
return true;
}
//+------------------------------------------------------------------+
//| Calculate position size with advanced methods |
//+------------------------------------------------------------------+
double CRiskManagerV71::CalculatePositionSize(string symbol, double stop_distance)
{
if(stop_distance <= 0) return 0;
double account_balance = AccountInfoDouble(ACCOUNT_BALANCE);
double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
if(tick_size == 0) return 0;
double position_size = 0;
switch(m_config.sizing_mode)
{
case PS_RISK_PERCENT:
{
double risk_amount = account_balance * m_config.risk_percent / 100.0;
double stop_ticks = stop_distance / tick_size;
position_size = risk_amount / (stop_ticks * tick_value);
}
break;
case PS_KELLY:
case PS_KELLY_ML:
{
double kelly_size = account_balance * m_kelly_fraction;
double stop_ticks = stop_distance / tick_size;
position_size = kelly_size / (stop_ticks * tick_value);
}
break;
case PS_VOLATILITY:
case PS_VOLATILITY_WEIGHTED:
{
double atr = iATR(symbol, PERIOD_CURRENT, 14);
if(atr > 0)
{
double volatility_adjusted_risk = m_config.risk_percent / (stop_distance / atr);
double risk_amount = account_balance * volatility_adjusted_risk / 100.0;
double stop_ticks = stop_distance / tick_size;
position_size = risk_amount / (stop_ticks * tick_value);
}
}
break;
case PS_RISK_PARITY:
// Risk parity requires portfolio context
position_size = account_balance * 0.01 / (stop_distance / tick_size * tick_value);
break;
default:
position_size = 0.01; // Default minimum
}
//--- Apply volatility scaling if enabled
if(m_config.scale_by_volatility)
{
double current_volatility = iATR(symbol, PERIOD_CURRENT, 14);
double avg_volatility = iATR(symbol, PERIOD_D1, 20);
if(current_volatility > 0 && avg_volatility > 0)
{
double volatility_ratio = avg_volatility / current_volatility;
position_size *= MathMin(2.0, MathMax(0.5, volatility_ratio));
}
}
//--- Normalize to lot step
double lot_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
position_size = MathRound(position_size / lot_step) * lot_step;
//--- Apply limits
double min_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double max_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
position_size = MathMax(min_lot, MathMin(max_lot, position_size));
return position_size;
}
//+------------------------------------------------------------------+
//| Calculate optimal size with portfolio optimization |
//+------------------------------------------------------------------+
double CRiskManagerV71::CalculateOptimalSize(string symbol, double stop_distance,
double expected_return, CorrelationMatrix &correlations)
{
//--- Base calculation
double base_size = CalculatePositionSize(symbol, stop_distance);
if(!m_config.use_portfolio_optimization)
return base_size;
//--- Find symbol in correlation matrix
int symbol_index = -1;
for(int i = 0; i < ArraySize(correlations.symbols); i++)
{
if(correlations.symbols[i] == symbol)
{
symbol_index = i;
break;
}
}
if(symbol_index < 0)
return base_size;
//--- Calculate correlation adjustment
double max_corr = 0;
int matrix_size = ArrayRange(correlations.matrix, 0);
for(int i = 0; i < matrix_size; i++)
{
if(i != symbol_index)
{
double corr = MathAbs(correlations.matrix[symbol_index][i]);
if(corr > max_corr)
max_corr = corr;
}
}
//--- Reduce size based on correlation
if(max_corr > m_config.max_correlation)
{
double reduction_factor = 1.0 - (max_corr - m_config.max_correlation);
base_size *= MathMax(0.3, reduction_factor);
}
//--- Apply expected return adjustment
if(expected_return > 0)
{
double return_multiplier = 1.0 + MathMin(0.5, expected_return / 100.0);
base_size *= return_multiplier;
}
return base_size;
}
//+------------------------------------------------------------------+
//| Calculate Kelly size with ML adjustment |
//+------------------------------------------------------------------+
double CRiskManagerV71::CalculateKellySizeML(string symbol, double stop_distance,
double ml_confidence, double predicted_return)
{
//--- Adjust Kelly fraction based on ML confidence
double adjusted_kelly = m_kelly_fraction * ml_confidence;
//--- Further adjust based on predicted return
if(predicted_return > 0)
{
double return_factor = 1.0 + MathMin(0.3, predicted_return / 10.0);
adjusted_kelly *= return_factor;
}
//--- Apply conservative cap
adjusted_kelly = MathMin(adjusted_kelly, 0.25); // Max 25% Kelly
//--- Calculate position size
double account_balance = AccountInfoDouble(ACCOUNT_BALANCE);
double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
if(tick_size == 0) return 0;
double kelly_amount = account_balance * adjusted_kelly;
double stop_ticks = stop_distance / tick_size;
double position_size = kelly_amount / (stop_ticks * tick_value);
//--- Normalize and limit
double lot_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
position_size = MathRound(position_size / lot_step) * lot_step;
double min_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double max_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
return MathMax(min_lot, MathMin(max_lot, position_size));
}
//+------------------------------------------------------------------+
//| Calculate risk parity position size |
//+------------------------------------------------------------------+
double CRiskManagerV71::CalculateRiskParitySize(ManagedTradeV71 &trades[], string symbol)
{
int trade_count = ArraySize(trades);
if(trade_count == 0)
return CalculatePositionSize(symbol, 100 * SymbolInfoDouble(symbol, SYMBOL_POINT));
//--- Calculate total portfolio risk
double total_risk = 0;
for(int i = 0; i < trade_count; i++)
{
total_risk += trades[i].risk_percent;
}
//--- Target equal risk contribution
double target_risk = m_config.max_risk / (trade_count + 1);
//--- Calculate position size for target risk
double account_balance = AccountInfoDouble(ACCOUNT_BALANCE);
double risk_amount = account_balance * target_risk / 100.0;
//--- Use ATR for stop distance if not provided
double atr = iATR(symbol, PERIOD_CURRENT, 14);
double stop_distance = atr * 2.0;
double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
if(tick_size == 0) return 0;
double stop_ticks = stop_distance / tick_size;
double position_size = risk_amount / (stop_ticks * tick_value);
//--- Normalize
double lot_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
position_size = MathRound(position_size / lot_step) * lot_step;
double min_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double max_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
return MathMax(min_lot, MathMin(max_lot, position_size));
}
//+------------------------------------------------------------------+
//| Calculate portfolio VaR |
//+------------------------------------------------------------------+
double CRiskManagerV71::CalculatePortfolioVaR(ManagedTradeV71 &trades[], double confidence, int horizon)
{
int trade_count = ArraySize(trades);
if(trade_count == 0) return 0;
//--- Collect unique symbols
string symbols[];
int symbol_count = 0;
for(int i = 0; i < trade_count; i++)
{
bool found = false;
for(int j = 0; j < symbol_count; j++)
{
if(symbols[j] == trades[i].symbol)
{
found = true;
break;
}
}
if(!found)
{
ArrayResize(symbols, symbol_count + 1);
symbols[symbol_count] = trades[i].symbol;
symbol_count++;
}
}
//--- Calculate returns for each symbol
double portfolio_returns[];
ArrayResize(portfolio_returns, m_config.var_params.lookback_period);
ArrayInitialize(portfolio_returns, 0);
//--- Get historical data and calculate portfolio returns
for(int i = 1; i < m_config.var_params.lookback_period; i++)
{
double portfolio_return = 0;
double total_weight = 0;
for(int j = 0; j < trade_count; j++)
{
double close_prev = iClose(trades[j].symbol, PERIOD_D1, i + 1);
double close_curr = iClose(trades[j].symbol, PERIOD_D1, i);
if(close_prev > 0)
{
double return_pct = (close_curr - close_prev) / close_prev;
//--- Apply position direction
if(trades[j].type == POSITION_TYPE_SELL)
return_pct = -return_pct;
//--- Weight by position size
double position_value = trades[j].volume * close_curr;
portfolio_return += return_pct * position_value;
total_weight += position_value;
}
}
if(total_weight > 0)
portfolio_returns[i] = portfolio_return / total_weight;
}
//--- Calculate VaR based on method
double var_value = 0;
if(m_config.var_params.use_cornish_fisher)
{
var_value = CalculateCornishFisherVaR(portfolio_returns, confidence);
}
else
{
var_value = CalculateHistoricalVaR(portfolio_returns, confidence);
}
//--- Adjust for time horizon
var_value *= MathSqrt(horizon);
//--- Convert to percentage of portfolio
double portfolio_value = 0;
for(int i = 0; i < trade_count; i++)
{
double price = SymbolInfoDouble(trades[i].symbol, SYMBOL_BID);
portfolio_value += trades[i].volume * price;
}
if(portfolio_value > 0)
{
m_portfolio_var = MathAbs(var_value) / portfolio_value * 100;
}
return m_portfolio_var;
}
//+------------------------------------------------------------------+
//| Calculate portfolio CVaR (Conditional VaR) |
//+------------------------------------------------------------------+
double CRiskManagerV71::CalculatePortfolioCVaR(ManagedTradeV71 &trades[], double confidence, int horizon)
{
//--- First calculate VaR
double var_value = CalculatePortfolioVaR(trades, confidence, horizon);
//--- CVaR is the expected value beyond VaR
int trade_count = ArraySize(trades);
if(trade_count == 0) return 0;
//--- Collect portfolio returns
double portfolio_returns[];
ArrayResize(portfolio_returns, m_config.var_params.lookback_period);
//--- [Similar return calculation as in VaR]
//... (abbreviated for space)
//--- Sort returns
ArraySort(portfolio_returns);
//--- Find VaR threshold index
int var_index = (int)((1 - confidence) * ArraySize(portfolio_returns));
//--- Calculate average of returns below VaR
double cvar_sum = 0;
int cvar_count = 0;
for(int i = 0; i <= var_index; i++)
{
cvar_sum += portfolio_returns[i];
cvar_count++;
}
if(cvar_count > 0)
{
double cvar_return = cvar_sum / cvar_count;
//--- Adjust for time horizon
cvar_return *= MathSqrt(horizon);
//--- Convert to percentage
double portfolio_value = 0;
for(int i = 0; i < trade_count; i++)
{
double price = SymbolInfoDouble(trades[i].symbol, SYMBOL_BID);
portfolio_value += trades[i].volume * price;
}
if(portfolio_value > 0)
{
m_portfolio_cvar = MathAbs(cvar_return) / portfolio_value * 100;
}
}
//--- Apply multiplier
m_portfolio_cvar *= m_config.cvar_multiplier;
return m_portfolio_cvar;
}
//+------------------------------------------------------------------+
//| Calculate historical VaR |
//+------------------------------------------------------------------+
double CRiskManagerV71::CalculateHistoricalVaR(double &returns[], double confidence)
{
int size = ArraySize(returns);
if(size == 0) return 0;
//--- Sort returns
double sorted_returns[];
ArrayCopy(sorted_returns, returns);
ArraySort(sorted_returns);
//--- Find percentile
int index = (int)((1 - confidence) * size);
index = MathMax(0, MathMin(size - 1, index));
return sorted_returns[index];
}
//+------------------------------------------------------------------+
//| Calculate Cornish-Fisher VaR (accounts for skew and kurtosis) |
//+------------------------------------------------------------------+
double CRiskManagerV71::CalculateCornishFisherVaR(double &returns[], double confidence)
{
int size = ArraySize(returns);
if(size < 4) return CalculateHistoricalVaR(returns, confidence);
//--- Calculate moments
double mean = MathMean(returns);
double std_dev = MathStandardDeviation(returns);
//--- Calculate skewness
double skew = 0;
for(int i = 0; i < size; i++)
{
skew += MathPow((returns[i] - mean) / std_dev, 3);
}
skew /= size;
//--- Calculate kurtosis
double kurt = 0;
for(int i = 0; i < size; i++)
{
kurt += MathPow((returns[i] - mean) / std_dev, 4);
}
kurt = kurt / size - 3; // Excess kurtosis
//--- Standard normal quantile
double z = MathQuantileNormal(confidence, 0, 1);
//--- Cornish-Fisher expansion
double cf_z = z + (z * z - 1) * skew / 6 +
(z * z * z - 3 * z) * kurt / 24 -
(2 * z * z * z - 5 * z) * skew * skew / 36;
return mean + cf_z * std_dev;
}
//+------------------------------------------------------------------+
//| Calculate portfolio beta |
//+------------------------------------------------------------------+
double CRiskManagerV71::CalculatePortfolioBeta(ManagedTradeV71 &trades[])
{
int trade_count = ArraySize(trades);
if(trade_count == 0) return 0;
//--- Use a market index (simplified - using major currency)
string market_symbol = "EURUSD";
int lookback = 100;
//--- Calculate market returns
double market_returns[];
ArrayResize(market_returns, lookback);
for(int i = 1; i < lookback; i++)
{
double close_prev = iClose(market_symbol, PERIOD_D1, i + 1);
double close_curr = iClose(market_symbol, PERIOD_D1, i);
if(close_prev > 0)
market_returns[i] = (close_curr - close_prev) / close_prev;
}
//--- Calculate portfolio returns
double portfolio_returns[];
ArrayResize(portfolio_returns, lookback);
ArrayInitialize(portfolio_returns, 0);
//--- [Portfolio return calculation similar to VaR method]
//--- Calculate beta (covariance / variance)
double cov = 0;
double market_var = 0;
double market_mean = MathMean(market_returns);
double portfolio_mean = MathMean(portfolio_returns);
for(int i = 0; i < lookback; i++)
{
cov += (portfolio_returns[i] - portfolio_mean) * (market_returns[i] - market_mean);
market_var += MathPow(market_returns[i] - market_mean, 2);
}
if(market_var > 0)
{
m_portfolio_beta = cov / market_var;
}
return m_portfolio_beta;
}
//+------------------------------------------------------------------+
//| Calculate optimal portfolio weights (Markowitz) |
//+------------------------------------------------------------------+
void CRiskManagerV71::CalculateOptimalWeights(double &expected_returns[],
CorrelationMatrix &correlations,
double target_risk,
double &weights[])
{
int n = ArraySize(expected_returns);
ArrayResize(weights, n);
//--- Simple equal weighting if optimization disabled
if(!m_config.use_portfolio_optimization)
{
for(int i = 0; i < n; i++)
weights[i] = 1.0 / n;
return;
}
//--- Build covariance matrix from correlations
CMatrixDouble cov_matrix;
cov_matrix.Resize(n, n);
//--- Estimate standard deviations (simplified)
double std_devs[];
ArrayResize(std_devs, n);
for(int i = 0; i < n; i++)
{
std_devs[i] = 0.15; // Assume 15% annual volatility
}
//--- Convert correlation to covariance
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
if(i < ArrayRange(correlations.matrix, 0) && j < ArrayRange(correlations.matrix, 1))
{
cov_matrix[i].Set(j, correlations.matrix[i][j] * std_devs[i] * std_devs[j]);
}
else if(i == j)
{
cov_matrix[i].Set(j, std_devs[i] * std_devs[i]);
}
else
{
cov_matrix[i].Set(j, 0);
}
}
}
//--- Simplified mean-variance optimization
//--- For now, use risk parity approach
double total_inv_vol = 0;
for(int i = 0; i < n; i++)
{
total_inv_vol += 1.0 / std_devs[i];
}
for(int i = 0; i < n; i++)
{
weights[i] = (1.0 / std_devs[i]) / total_inv_vol;
}
//--- Adjust for expected returns
double total_weight = 0;
for(int i = 0; i < n; i++)
{
if(expected_returns[i] > 0)
{
weights[i] *= (1.0 + expected_returns[i] / 100.0);
}
total_weight += weights[i];
}
//--- Normalize
if(total_weight > 0)
{
for(int i = 0; i < n; i++)
{
weights[i] /= total_weight;
}
}
}
//+------------------------------------------------------------------+
//| Enforce risk rules on position |
//+------------------------------------------------------------------+
void CRiskManagerV71::EnforceRiskRulesOptimized(ManagedTradeV71 &trade)
{
//--- Check if stop loss exists
if(trade.sl == 0)
{
//--- Calculate and set stop loss based on ATR
double atr = iATR(trade.symbol, PERIOD_CURRENT, 14);
double stop_distance = atr * m_config.base_atr_multi;
if(trade.type == POSITION_TYPE_BUY)
trade.sl = trade.open_price - stop_distance;
else
trade.sl = trade.open_price + stop_distance;
//--- Update position
CTrade ctrade;
ctrade.PositionModify(trade.ticket, trade.sl, trade.tp);
}
//--- Check position size against risk limits
if(trade.risk_percent > m_config.risk_percent)
{
//--- Calculate reduction needed
double reduction_factor = m_config.risk_percent / trade.risk_percent;
double new_volume = trade.volume * reduction_factor;
//--- Normalize to lot step
double lot_step = SymbolInfoDouble(trade.symbol, SYMBOL_VOLUME_STEP);
new_volume = MathRound(new_volume / lot_step) * lot_step;
//--- Partial close to reduce risk
if(new_volume < trade.volume && new_volume > 0)
{
CTrade ctrade;
ctrade.PositionClosePartial(trade.ticket, trade.volume - new_volume);
Print("Risk enforcement: Reduced position ", trade.ticket,
" from ", trade.volume, " to ", new_volume);
}
}
}
//+------------------------------------------------------------------+
//| Run portfolio stress test |
//+------------------------------------------------------------------+
double CRiskManagerV71::RunStressTest(ManagedTradeV71 &trades[], double stress_factor)
{
int trade_count = ArraySize(trades);
if(trade_count == 0) return 0;
double stressed_loss = 0;
double account_balance = AccountInfoDouble(ACCOUNT_BALANCE);
//--- Apply stress factor to each position
for(int i = 0; i < trade_count; i++)
{
//--- Calculate stressed move
double atr = iATR(trades[i].symbol, PERIOD_D1, 20);
double stressed_move = atr * stress_factor;
//--- Calculate potential loss
double potential_loss = 0;
if(trades[i].type == POSITION_TYPE_BUY)
{
double stressed_price = trades[i].open_price - stressed_move;
potential_loss = (trades[i].open_price - stressed_price) * trades[i].volume;
}
else
{
double stressed_price = trades[i].open_price + stressed_move;
potential_loss = (stressed_price - trades[i].open_price) * trades[i].volume;
}
//--- Convert to account currency
double tick_value = SymbolInfoDouble(trades[i].symbol, SYMBOL_TRADE_TICK_VALUE);
double tick_size = SymbolInfoDouble(trades[i].symbol, SYMBOL_TRADE_TICK_SIZE);
if(tick_size > 0)
{
potential_loss = potential_loss / tick_size * tick_value;
stressed_loss += potential_loss;
}
}
//--- Return as percentage of balance
return (stressed_loss / account_balance) * 100;
}
//+------------------------------------------------------------------+
//| Update Kelly parameters from trading results |
//+------------------------------------------------------------------+
void CRiskManagerV71::UpdateKellyParameters(double win_rate, double avg_win, double avg_loss)
{
m_win_rate = win_rate;
if(avg_loss != 0)
m_avg_win_loss_ratio = MathAbs(avg_win / avg_loss);
else
m_avg_win_loss_ratio = 1.5;
//--- Calculate optimal Kelly fraction
m_kelly_fraction = OptimizeKellyFraction(win_rate, avg_win, avg_loss);
}
//+------------------------------------------------------------------+
//| Calculate optimal Kelly fraction |
//+------------------------------------------------------------------+
double CRiskManagerV71::OptimizeKellyFraction(double win_rate, double avg_win, double avg_loss)
{
if(avg_loss == 0) return 0.1; // Default conservative
double b = MathAbs(avg_win / avg_loss);
double p = win_rate;
double q = 1 - p;
//--- Kelly formula: f = (p*b - q) / b
double kelly = (p * b - q) / b;
//--- Apply safety factor (1/3 Kelly is common)
kelly *= 0.33;
//--- Cap at maximum
kelly = MathMin(0.25, MathMax(0, kelly));
return kelly;
}
#endif // RISK_MANAGER_V71_MQH