mql5/Include/Experts/Risk/PositionSizer.mqh
2025-08-16 12:30:04 -04:00

306 lines
11 KiB
MQL5

//+------------------------------------------------------------------+
//| PositionSizer.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 "RiskParameters.mqh"
#include "RiskMetrics.mqh"
//+------------------------------------------------------------------+
//| Position Sizer Class |
//+------------------------------------------------------------------+
class CPositionSizer
{
private:
CRiskParameters* m_params; // Risk parameters
CRiskMetrics* m_metrics; // Risk metrics
// Position sizing parameters
double m_volatilityFactor; // Current volatility adjustment factor
double m_correlationFactor; // Correlation adjustment factor
// State
bool m_isInitialized; // Initialization flag
// Private methods
double CalculateBasePositionSize(const string symbol, const double stopLossPips,
const double riskPercent, const double accountEquity);
double ApplyVolatilityAdjustment(const string symbol, const double size);
double ApplyCorrelationAdjustment(const string symbol, const double size);
double ApplyLeverageAdjustment(const double size);
double ApplyLotStepRounding(const string symbol, const double size);
public:
// Constructor/Destructor
CPositionSizer();
~CPositionSizer();
// Initialization
bool Initialize(CRiskParameters* params = NULL, CRiskMetrics* metrics = NULL);
void Deinitialize();
// Position sizing
bool CalculateSize(const string symbol, const double stopLossPips,
const double riskPercent, const double accountEquity,
double &size);
// Update methods
void UpdateVolatility(const double volatility);
void UpdateCorrelation(const double correlation);
// Getters
double GetVolatilityFactor() const { return m_volatilityFactor; }
double GetCorrelationFactor() const { return m_correlationFactor; }
bool IsInitialized() const { return m_isInitialized; }
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CPositionSizer::CPositionSizer() :
m_params(NULL),
m_metrics(NULL),
m_volatilityFactor(1.0),
m_correlationFactor(1.0),
m_isInitialized(false)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CPositionSizer::~CPositionSizer()
{
Deinitialize();
}
//+------------------------------------------------------------------+
//| Initialize position sizer |
//+------------------------------------------------------------------+
bool CPositionSizer::Initialize(CRiskParameters* params = NULL, CRiskMetrics* metrics = NULL)
{
m_params = params;
m_metrics = metrics;
m_volatilityFactor = 1.0;
m_correlationFactor = 1.0;
m_isInitialized = true;
return true;
}
//+------------------------------------------------------------------+
//| Deinitialize position sizer |
//+------------------------------------------------------------------+
void CPositionSizer::Deinitialize()
{
m_params = NULL;
m_metrics = NULL;
m_isInitialized = false;
}
//+------------------------------------------------------------------+
//| Calculate position size |
//+------------------------------------------------------------------+
bool CPositionSizer::CalculateSize(const string symbol, const double stopLossPips,
const double riskPercent, const double accountEquity,
double &size)
{
if (!m_isInitialized || stopLossPips <= 0.0 || riskPercent <= 0.0 || accountEquity <= 0.0)
return false;
// Calculate base position size based on risk percentage
double baseSize = CalculateBasePositionSize(symbol, stopLossPips, riskPercent, accountEquity);
if (baseSize <= 0.0)
return false;
// Apply adjustments
double adjustedSize = baseSize;
// Apply volatility adjustment if metrics are available
if (m_metrics != NULL)
{
adjustedSize = ApplyVolatilityAdjustment(symbol, adjustedSize);
}
// Apply correlation adjustment if metrics are available
if (m_metrics != NULL)
{
adjustedSize = ApplyCorrelationAdjustment(symbol, adjustedSize);
}
// Apply leverage adjustment
adjustedSize = ApplyLeverageAdjustment(adjustedSize);
// Apply lot step rounding
adjustedSize = ApplyLotStepRounding(symbol, adjustedSize);
// Ensure size is within allowed limits
if (m_params != NULL)
{
adjustedSize = MathMin(adjustedSize, m_params.GetMaxLotsPerTrade());
adjustedSize = MathMax(adjustedSize, m_params.GetMinPositionSize());
}
size = adjustedSize;
return (size > 0.0);
}
//+------------------------------------------------------------------+
//| Calculate base position size based on risk percentage |
//+------------------------------------------------------------------+
double CPositionSizer::CalculateBasePositionSize(const string symbol, const double stopLossPips,
const double riskPercent, const double accountEquity)
{
// Get symbol information
double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double lotSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_CONTRACT_SIZE);
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
if (tickSize <= 0.0 || tickValue <= 0.0 || lotSize <= 0.0 || point <= 0.0)
return 0.0;
// Calculate pip value (accounting for 5-digit brokers)
double pipValue = point * 10.0;
// Calculate value per pip
double valuePerPip = (tickValue / tickSize) * pipValue;
// Calculate risk amount in account currency
double riskAmount = accountEquity * (riskPercent / 100.0);
// Calculate position size in lots
double stopLossValue = stopLossPips * valuePerPip;
if (stopLossValue <= 0.0)
return 0.0;
double positionSize = riskAmount / stopLossValue;
// Convert to lots
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
if (lotStep > 0.0)
{
positionSize = MathFloor(positionSize / lotStep) * lotStep;
}
return positionSize;
}
//+------------------------------------------------------------------+
//| Apply volatility adjustment to position size |
//+------------------------------------------------------------------
double CPositionSizer::ApplyVolatilityAdjustment(const string symbol, const double size)
{
if (m_metrics == NULL || !m_params.UseVolatility())
return size;
double currentVol = m_metrics.GetCurrentVolatility();
double historicalVol = m_metrics.GetHistoricalVolatility();
// If we don't have valid volatility data, return original size
if (currentVol <= 0.0 || historicalVol <= 0.0)
return size;
// Calculate volatility ratio (current / historical)
double volRatio = currentVol / historicalVol;
// Apply inverse relationship: higher volatility = smaller position
// Cap the adjustment to avoid extreme position sizing
volRatio = MathMax(0.5, MathMin(2.0, 1.0 / volRatio));
// Apply volatility factor from parameters
if (m_params != NULL)
{
volRatio *= m_params.GetVolatilityFactor();
}
m_volatilityFactor = volRatio;
return size * volRatio;
}
//+------------------------------------------------------------------+
//| Apply correlation adjustment to position size |
//+------------------------------------------------------------------
double CPositionSizer::ApplyCorrelationAdjustment(const string symbol, const double size)
{
if (m_metrics == NULL || !m_params.UseCorrelation())
return size;
// This is a simplified implementation
// In practice, you would check correlation with existing positions
// For now, we'll just return the original size
// A full implementation would use the correlation matrix
return size;
}
//+------------------------------------------------------------------+
//| Apply leverage adjustment to position size |
//+------------------------------------------------------------------
double CPositionSizer::ApplyLeverageAdjustment(const double size)
{
if (m_params == NULL)
return size;
double maxLeverage = m_params.GetMaxLeverage();
double accountLeverage = (double)AccountInfoInteger(ACCOUNT_LEVERAGE);
if (maxLeverage <= 0.0 || accountLeverage <= 0.0)
return size;
// Adjust position size based on leverage
double leverageRatio = MathMin(1.0, maxLeverage / accountLeverage);
return size * leverageRatio;
}
//+------------------------------------------------------------------+
//| Apply lot step rounding |
//+------------------------------------------------------------------
double CPositionSizer::ApplyLotStepRounding(const string symbol, const double size)
{
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
if (lotStep <= 0.0)
return size;
// Round to nearest lot step
double roundedSize = MathFloor(size / lotStep + 0.5) * lotStep;
// Ensure within min/max limits
if (minLot > 0.0)
roundedSize = MathMax(roundedSize, minLot);
if (maxLot > 0.0)
roundedSize = MathMin(roundedSize, maxLot);
return roundedSize;
}
//+------------------------------------------------------------------+
//| Update volatility factor |
//+------------------------------------------------------------------
void CPositionSizer::UpdateVolatility(const double volatility)
{
if (volatility > 0.0)
{
// Store the inverse of volatility (higher volatility = smaller position)
m_volatilityFactor = 1.0 / (1.0 + volatility);
}
}
//+------------------------------------------------------------------+
//| Update correlation factor |
//+------------------------------------------------------------------
void CPositionSizer::UpdateCorrelation(const double correlation)
{
// Store the inverse of correlation (higher correlation = smaller position)
m_correlationFactor = 1.0 - (0.5 * correlation);
}