306 lines
11 KiB
MQL5
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);
| |||
}
|