mql5/Shared Projects/ERMT-ML/Modules-ML8x/RiskManager_v71.mqh
darashikoh 0fb1bd1b0a 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

873 lines
No EOL
29 KiB
MQL5

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