mql5/Include/Experts/PositionManager.mqh

2083 lines
82 KiB
MQL5
Raw Permalink Normal View History

2025-08-16 12:30:04 -04:00
//+------------------------------------------------------------------+
//| PositionManager.mqh |
//| Copyright 2025, EscapeEA |
//| https://www.escapeea.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, EscapeEA"
#property link "https://www.escapeea.com"
#property version "1.00"
#property strict
#include <Trade\PositionInfo.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\Trade.mqh>
#include "Escape/InputValidation.mqh"
//+------------------------------------------------------------------+
//| Position Manager class |
//+------------------------------------------------------------------+
class CPositionManager {
private:
CTrade *m_trade; // Trade object
CSymbolInfo *m_symbol; // Symbol info
CPositionInfo m_position; // Position info
// Configuration
double m_slPoints; // Stop loss in points
double m_tpPoints; // Take profit in points
double m_trailingStop; // Trailing stop in points
double m_breakEven; // Break even level in points
// State
bool m_initialized; // Initialization flag
bool m_autoBreakEven; // Auto break-even flag
bool m_scaleIn; // Scale into positions flag
bool m_scaleOut; // Scale out of positions flag
// Advanced position management
double m_breakEvenAt; // Pips in profit to move to break-even
double m_scaleInAt; // Pips in drawdown to scale in
double m_scaleOutAt; // Pips in profit to scale out
double m_scaleInFactor; // Scale-in volume factor
double m_scaleOutFactor; // Scale-out volume factor
// Time-based exit
bool m_useTimeExit; // Enable time-based exit
datetime m_expiryTime; // Position expiry time
int m_maxHoldBars; // Maximum bars to hold position
// Volatility-based position sizing
bool m_useVolatilitySizing; // Enable volatility-based sizing
int m_volatilityPeriod; // Period for volatility calculation
double m_volatilityMultiplier; // Multiplier for position size
double m_maxPositionRisk; // Maximum risk per position (%)
// Correlation management
string m_correlationSymbols[]; // Array of correlated symbols
double m_maxCorrelation; // Maximum allowed correlation
// Grid trading
bool m_gridTrading; // Enable grid trading
double m_gridStep; // Grid step in pips
int m_maxGridLevels; // Maximum grid levels
double m_gridVolumeMultiplier; // Volume multiplier for grid levels
// News event handling
bool m_newsFilter; // Enable news filtering
int m_newsImpact; // Minimum news impact level (1-3)
int m_minutesBeforeNews; // Minutes before news to avoid trading
int m_minutesAfterNews; // Minutes after news to avoid trading
// Advanced risk management
bool m_useDynamicRisk; // Enable dynamic risk adjustment
double m_maxDailyDrawdown; // Maximum daily drawdown percentage
double m_maxPortfolioRisk; // Maximum portfolio risk (%)
double m_riskDecayFactor; // Risk reduction factor after losses
// Adaptive position sizing
bool m_useAdaptiveSizing; // Enable adaptive position sizing
double m_winRate; // Tracked win rate (0-1)
double m_profitFactor; // Tracked profit factor
int m_tradeCount; // Number of trades in current session
double m_equityPeak; // Peak equity in current session
// Advanced exit strategies
bool m_useTrailingDrawdown; // Enable trailing drawdown exit
double m_trailingDrawdown; // Trailing drawdown percentage
bool m_useVolatilityExit; // Enable volatility-based exit
double m_volatilityExitLevel; // Volatility level for exit
// Position clustering
bool m_usePositionClustering; // Enable position clustering
double m_clusterDistance; // Distance between position clusters
int m_maxClusters; // Maximum number of clusters
// Machine learning integration
bool m_useMLPredictions; // Enable ML-based predictions
double m_mlConfidenceThreshold; // Minimum confidence threshold
// Market regime detection
bool m_useRegimeFilter; // Enable market regime filtering
int m_regimePeriod; // Period for regime detection
double m_trendThreshold; // Threshold for trend detection
double m_rangeThreshold; // Threshold for range detection
// Private methods
bool ValidatePositionParams(const string symbol, double volume, double price,
double sl, double tp, const string comment);
public:
// Constructor/Destructor
CPositionManager();
~CPositionManager();
// Initialization
bool Initialize(CTrade *trade, CSymbolInfo *symbol, double slPoints = 0,
double tpPoints = 0, double trailingStop = 0, double breakEven = 0);
// Position management
bool OpenPosition(const string symbol, ENUM_ORDER_TYPE type, double volume,
double price, double sl, double tp, const string comment = "");
bool ClosePosition(ulong ticket, const string comment = "");
bool CloseAllPositions(const string symbol = "", int magic = -1);
bool ModifyPosition(ulong ticket, double sl, double tp);
// Trailing stop
void CheckTrailingStop();
// Advanced position management
bool SetBreakEven(double pips, double lockInPips = 0);
bool SetScaling(bool scaleIn, bool scaleOut, double scaleInAt = 0,
double scaleOutAt = 0, double scaleInFactor = 0.5,
double scaleOutFactor = 0.5);
bool SetTimeExit(bool enable, datetime expiry = 0, int maxBars = 0);
bool ScaleInPosition(ulong ticket, double volume);
bool ScaleOutPosition(ulong ticket, double volume);
bool PartialClose(ulong ticket, double percent);
bool CloseAtTime(ulong ticket, datetime closeTime);
bool CloseAtProfit(ulong ticket, double targetProfit, bool closePartial = false);
bool CloseAtLoss(ulong ticket, double maxLoss, bool closePartial = false);
bool MoveToBreakEven(ulong ticket);
// Position analysis
double GetPositionRisk(ulong ticket);
double GetPositionRiskPercent(ulong ticket, double accountBalance = 0);
double GetPositionRiskReward(ulong ticket);
datetime GetPositionAge(ulong ticket, ENUM_TIME_UNITS unit = PERIOD_M1);
// Getters
bool IsInitialized() const { return m_initialized; }
int TotalPositions(const string symbol = "", int magic = -1);
double GetPositionProfit(const string symbol = "", int magic = -1);
double GetPositionVolume(const string symbol = "", int magic = -1);
bool HasOpenPosition(const string symbol = "", int magic = -1);
ENUM_POSITION_TYPE GetPositionType(ulong ticket);
double GetPositionOpenPrice(ulong ticket);
double GetPositionCurrentPrice(ulong ticket);
double GetPositionStopLoss(ulong ticket);
double GetPositionTakeProfit(ulong ticket);
// Advanced position management
bool SetVolatilitySizing(bool enable, int period = 14, double multiplier = 1.0, double maxRisk = 2.0);
double CalculateVolatilityAdjustedVolume(double riskPercent, double slPips);
bool SetCorrelationFilter(string &symbols[], double maxCorrelation = 0.7);
double GetCorrelationWith(const string symbol, int period = 100);
bool IsCorrelationSafe();
bool SetGridTrading(bool enable, double gridStep = 20, int maxLevels = 5, double volumeMultiplier = 1.5);
bool ManageGridLevels(const string symbol, double currentPrice, ENUM_POSITION_TYPE type);
bool SetNewsFilter(bool enable, int minImpact = 2, int minutesBefore = 60, int minutesAfter = 30);
bool IsNewsEventImminent();
bool IsHighImpactNewsPending();
// Advanced position scaling
bool SetScalingProfile(ENUM_SCALING_PROFILE profile);
double CalculateOptimalScaleInVolume(double baseVolume, int scaleLevel, double drawdownPct);
bool CheckScalingOpportunity(ulong ticket, double &volume);
bool ScaleInPosition(ulong ticket, double volume);
bool ScaleOutPosition(ulong ticket, double volume);
// Advanced exit strategies
bool SetExitProfile(ENUM_EXIT_PROFILE profile);
bool CheckExitConditions(ulong ticket, double &closePrice, string &reason);
bool UsePyramidingExit(ulong ticket, double &closeVolume);
bool UseTimeExit(ulong ticket, datetime openTime);
bool UseVolatilityExit(ulong ticket, double currentVolatility);
// Correlation-based position sizing
double CalculateCorrelationAdjustedVolume(const string symbol, double baseVolume);
double GetPortfolioCorrelation(const string symbol);
double GetMarketCorrelation(const string symbol1, const string symbol2, int period = 100);
// Machine learning integration
bool LoadMLModel(const string modelFile);
double GetMLPositionScore(const string symbol, ENUM_POSITION_TYPE type);
bool UpdateMLModel(const MqlRates &rates[], const double &features[]);
// Advanced position monitoring
bool MonitorPosition(ulong ticket);
void UpdatePositionStats(ulong ticket);
bool NeedsAdjustment(ulong ticket);
// Advanced order management
bool PlaceBracketOrder(const string symbol, ENUM_ORDER_TYPE type, double volume,
double entryPrice, double sl, double tp,
double breakEvenAt = 0, double trailingStop = 0,
datetime expiration = 0, const string comment = "");
bool PlaceOCOOrders(const string symbol, ENUM_ORDER_TYPE type, double volume,
double price1, double price2, double sl, double tp,
const string comment = "");
// Advanced risk controls
bool CheckVolatilitySpike();
bool CheckLiquidity(const string symbol);
bool CheckMarketHours();
// Position clustering
bool SetPositionClustering(bool enable, double clusterDistance = 20.0, int maxClusters = 3);
bool IsInExistingCluster(const string symbol, double price, ENUM_POSITION_TYPE type);
// Market regime detection
bool SetRegimeFilter(bool enable, int period = 50, double trendThreshold = 0.3, double rangeThreshold = 0.2);
ENUM_MARKET_REGIME DetectMarketRegime();
bool IsFavorableRegime(ENUM_POSITION_TYPE type, ENUM_MARKET_REGIME regime);
// Advanced risk management
bool SetDynamicRisk(bool enable, double maxDailyDrawdown = 5.0, double maxPositionRisk = 2.0,
double maxPortfolioRisk = 20.0, double riskDecay = 0.9);
double CalculateDynamicRisk(double baseRisk);
bool CheckDrawdownLimits();
double GetCurrentDrawdown();
// Adaptive position sizing
bool SetAdaptiveSizing(bool enable, double initialWinRate = 0.5, double initialProfitFactor = 1.5);
void UpdateTradeStats(bool isWin, double profit);
double CalculateAdaptiveVolume(double baseVolume, double confidence = 1.0);
// Advanced exit strategies
bool SetTrailingDrawdown(bool enable, double drawdownPercent = 10.0);
bool SetVolatilityExit(bool enable, double volatilityThreshold = 2.0);
bool CheckAdvancedExitConditions(ulong ticket);
// Position clustering
bool SetPositionClustering(bool enable, double clusterDistance = 20.0, int maxClusters = 3);
bool IsInExistingCluster(const string symbol, double price, ENUM_POSITION_TYPE type);
// Machine learning integration
bool SetMLIntegration(bool enable, double confidenceThreshold = 0.7);
double GetMLPrediction(const string symbol, ENUM_TIMEFRAMES timeframe);
// Market regime detection
bool SetRegimeFilter(bool enable, int period = 50, double trendThreshold = 0.3, double rangeThreshold = 0.2);
ENUM_MARKET_REGIME DetectMarketRegime();
bool IsFavorableRegime(ENUM_POSITION_TYPE type, ENUM_MARKET_REGIME regime);
// Advanced position analysis
double CalculateOptimalF(double winRate, double winLossRatio);
double CalculateKellyCriterion(double winRate, double winLossRatio);
double CalculatePositionScore(const string symbol, ENUM_POSITION_TYPE type);
// Advanced order types
bool PlaceBracketOrder(const string symbol, ENUM_ORDER_TYPE type, double volume, double entryPrice,
double sl, double tp, double breakEvenAt = 0, double trailingStop = 0,
datetime expiration = 0, const string comment = "");
bool PlaceOCOOrders(const string symbol, ENUM_ORDER_TYPE type, double volume, double price1,
double price2, double sl, double tp, const string comment = "");
// Advanced position monitoring
bool MonitorPosition(ulong ticket);
void UpdatePositionStats(ulong ticket);
bool NeedsAdjustment(ulong ticket);
// Advanced risk controls
bool CheckVolatilitySpike();
bool CheckLiquidity(const string symbol);
bool CheckMarketHours();
// State management
void Update();
// Advanced position analysis
double CalculatePositionScore(const string symbol, ENUM_POSITION_TYPE type);
double CalculateOptimalF(double winRate, double winLossRatio);
double CalculateKellyCriterion(double winRate, double winLossRatio);
// Risk management
bool SetDynamicRisk(bool enable, double maxDailyDrawdown = 5.0, double maxPositionRisk = 2.0,
double maxPortfolioRisk = 20.0, double riskDecay = 0.9);
double CalculateDynamicRisk(double baseRisk);
bool CheckDrawdownLimits();
double GetCurrentDrawdown();
// Adaptive position sizing
bool SetAdaptiveSizing(bool enable, double initialWinRate = 0.5, double initialProfitFactor = 1.5);
void UpdateTradeStats(bool isWin, double profit);
double CalculateAdaptiveVolume(double baseVolume, double confidence = 1.0);
// Advanced exit strategies
bool SetTrailingDrawdown(bool enable, double drawdownPercent = 10.0);
bool SetVolatilityExit(bool enable, double volatilityThreshold = 2.0);
bool CheckAdvancedExitConditions(ulong ticket);
// Machine learning integration
bool SetMLIntegration(bool enable, double confidenceThreshold = 0.7);
double GetMLPrediction(const string symbol, ENUM_TIMEFRAMES timeframe);
// Position clustering
bool SetPositionClustering(bool enable, double clusterDistance = 20.0, int maxClusters = 3);
// Market regime detection
bool SetRegimeFilter(bool enable, int period = 50, double trendThreshold = 0.3, double rangeThreshold = 0.2);
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CPositionManager::CPositionManager() :
m_trade(NULL),
m_symbol(NULL),
m_slPoints(0),
m_tpPoints(0),
m_trailingStop(0),
m_breakEven(0),
m_initialized(false),
m_autoBreakEven(false),
m_scaleIn(false),
m_scaleOut(false),
m_breakEvenAt(0),
m_scaleInAt(0),
m_scaleOutAt(0),
m_scaleInFactor(0.5),
m_scaleOutFactor(0.5),
m_useTimeExit(false),
m_expiryTime(0),
m_maxHoldBars(0),
m_useVolatilitySizing(false),
m_volatilityPeriod(14),
m_volatilityMultiplier(1.0),
m_maxPositionRisk(2.0),
m_maxCorrelation(0.7),
m_gridTrading(false),
m_gridStep(20.0),
m_maxGridLevels(5),
m_gridVolumeMultiplier(1.5),
m_newsFilter(false),
m_newsImpact(2),
m_minutesBeforeNews(60),
m_minutesAfterNews(30),
m_useDynamicRisk(false),
m_maxDailyDrawdown(5.0),
m_maxPortfolioRisk(20.0),
m_riskDecayFactor(0.9),
m_useAdaptiveSizing(false),
m_winRate(0.5),
m_profitFactor(1.5),
m_tradeCount(0),
m_equityPeak(0),
m_useTrailingDrawdown(false),
m_trailingDrawdown(10.0),
m_useVolatilityExit(false),
m_volatilityExitLevel(2.0),
m_usePositionClustering(false),
m_clusterDistance(20.0),
m_maxClusters(3),
m_useMLPredictions(false),
m_mlConfidenceThreshold(0.7),
m_useRegimeFilter(false),
m_regimePeriod(50),
m_trendThreshold(0.3),
m_rangeThreshold(0.2)
{
// Initialize position info
m_position = CPositionInfo();
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CPositionManager::~CPositionManager() {
// Clean up
m_trade = NULL;
m_symbol = NULL;
}
//+------------------------------------------------------------------+
//| Initialize position manager |
//+------------------------------------------------------------------+
bool CPositionManager::Initialize(CTrade *trade, CSymbolInfo *symbol, double slPoints,
double tpPoints, double trailingStop, double breakEven) {
// Validate inputs
if(trade == NULL || symbol == NULL) {
Print("Error: Invalid trade or symbol object");
return false;
}
// Store references and settings
m_trade = trade;
m_symbol = symbol;
m_slPoints = slPoints;
m_tpPoints = tpPoints;
m_trailingStop = trailingStop;
m_breakEven = breakEven;
m_initialized = true;
return true;
}
//+------------------------------------------------------------------+
//| Validate position parameters |
//+------------------------------------------------------------------+
bool CPositionManager::ValidatePositionParams(const string symbol, double volume, double price,
double sl, double tp, const string comment) {
// Validate symbol
if(symbol == "" || !SymbolSelect(symbol, true)) {
Print("Error: Invalid symbol ", symbol);
return false;
}
// Validate volume
double minVolume = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double maxVolume = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
double volumeStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
if(volume < minVolume || volume > maxVolume) {
PrintFormat("Error: Volume %.2f is outside allowed range [%.2f, %.2f]",
volume, minVolume, maxVolume);
return false;
}
// Normalize volume to the nearest step
volume = MathFloor(volume / volumeStep) * volumeStep;
// Validate price levels
if(price <= 0) {
Print("Error: Invalid price ", price);
return false;
}
// Validate stop loss and take profit levels
if((sl > 0 && sl >= price) || (sl < 0 && sl <= -price)) {
Print("Error: Invalid stop loss level ", sl);
return false;
}
if((tp > 0 && tp <= price) || (tp < 0 && tp >= -price)) {
Print("Error: Invalid take profit level ", tp);
return false;
}
// Validate comment length
if(StringLen(comment) > 31) {
Print("Error: Comment is too long (max 31 characters)");
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Open a new position |
//+------------------------------------------------------------------+
bool CPositionManager::OpenPosition(const string symbol, ENUM_ORDER_TYPE type, double volume,
double price, double sl, double tp, const string comment) {
if(!m_initialized) {
Print("Error: Position manager not initialized");
return false;
}
// Validate parameters
if(!ValidatePositionParams(symbol, volume, price, sl, tp, comment)) {
return false;
}
// Open position based on order type
if(type == ORDER_TYPE_BUY) {
if(!m_trade.Buy(volume, symbol, price, sl, tp, comment)) {
Print("Error opening buy position: ", GetLastError());
return false;
}
}
else if(type == ORDER_TYPE_SELL) {
if(!m_trade.Sell(volume, symbol, price, sl, tp, comment)) {
Print("Error opening sell position: ", GetLastError());
return false;
}
}
else {
Print("Error: Unsupported order type");
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Close a position by ticket |
//+------------------------------------------------------------------+
bool CPositionManager::ClosePosition(ulong ticket, const string comment) {
if(!m_initialized) {
Print("Error: Position manager not initialized");
return false;
}
// Select position by ticket
if(!m_position.SelectByTicket(ticket)) {
Print("Error selecting position ", ticket, ": ", GetLastError());
return false;
}
// Close the position
if(!m_trade.PositionClose(ticket, m_position.Deviation())) {
Print("Error closing position ", ticket, ": ", GetLastError());
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Close all positions |
//+------------------------------------------------------------------+
bool CPositionManager::CloseAllPositions(const string symbol = "", int magic = -1) {
bool result = true;
// Loop through all open positions
for(int i = PositionsTotal() - 1; i >= 0; i--) {
if(m_position.SelectByIndex(i)) {
// Check symbol filter
if(symbol != "" && m_position.Symbol() != symbol) {
continue;
}
// Check magic number filter
if(magic >= 0 && (int)m_position.Magic() != magic) {
continue;
}
// Close the position
if(!ClosePosition(m_position.Ticket())) {
result = false;
}
}
}
return result;
}
//+------------------------------------------------------------------+
//| Modify position's stop loss and take profit |
//+------------------------------------------------------------------+
bool CPositionManager::ModifyPosition(ulong ticket, double sl, double tp) {
if(!m_initialized) {
Print("Error: Position manager not initialized");
return false;
}
// Select position by ticket
if(!m_position.SelectByTicket(ticket)) {
Print("Error selecting position ", ticket, ": ", GetLastError());
return false;
}
// Get current position details
double openPrice = m_position.PriceOpen();
double currentSl = m_position.StopLoss();
double currentTp = m_position.TakeProfit();
// Only modify if values have changed
if(currentSl != sl || currentTp != tp) {
if(!m_trade.PositionModify(ticket, sl, tp)) {
Print("Error modifying position ", ticket, ": ", GetLastError());
return false;
}
}
return true;
}
//+------------------------------------------------------------------+
//| Check and update trailing stops |
//+------------------------------------------------------------------+
void CPositionManager::CheckTrailingStop() {
if(!m_initialized || m_trailingStop <= 0) {
return;
}
// Update symbol rates
m_symbol.RefreshRates();
// Loop through all open positions
for(int i = PositionsTotal() - 1; i >= 0; i--) {
if(m_position.SelectByIndex(i)) {
// Skip if not our symbol
if(m_position.Symbol() != m_symbol.Name()) {
continue;
}
double positionSl = m_position.StopLoss();
double newSl = 0;
if(m_position.PositionType() == POSITION_TYPE_BUY) {
// For long positions, trail below the current price
newSl = m_symbol.Bid() - m_trailingStop * m_symbol.Point();
// Only move the stop loss up
if(newSl > positionSl || positionSl == 0) {
ModifyPosition(m_position.Ticket(), newSl, m_position.TakeProfit());
}
}
else if(m_position.PositionType() == POSITION_TYPE_SELL) {
// For short positions, trail above the current price
newSl = m_symbol.Ask() + m_trailingStop * m_symbol.Point();
// Only move the stop loss down
if((newSl < positionSl || positionSl == 0) && newSl > 0) {
ModifyPosition(m_position.Ticket(), newSl, m_position.TakeProfit());
}
}
}
}
}
//+------------------------------------------------------------------+
//| Get total number of positions |
//+------------------------------------------------------------------+
int CPositionManager::TotalPositions(const string symbol = "", int magic = -1) {
int count = 0;
for(int i = 0; i < PositionsTotal(); i++) {
if(m_position.SelectByIndex(i)) {
// Check symbol filter
if(symbol != "" && m_position.Symbol() != symbol) {
continue;
}
// Check magic number filter
if(magic >= 0 && (int)m_position.Magic() != magic) {
continue;
}
count++;
}
}
return count;
}
//+------------------------------------------------------------------+
//| Get total profit/loss for positions |
//+------------------------------------------------------------------+
double CPositionManager::GetPositionProfit(const string symbol = "", int magic = -1) {
double profit = 0;
for(int i = 0; i < PositionsTotal(); i++) {
if(m_position.SelectByIndex(i)) {
// Check symbol filter
if(symbol != "" && m_position.Symbol() != symbol) {
continue;
}
// Check magic number filter
if(magic >= 0 && (int)m_position.Magic() != magic) {
continue;
}
profit += m_position.Profit() + m_position.Swap() + m_position.Commission();
}
}
return profit;
}
//+------------------------------------------------------------------+
//| Set break-even parameters |
//+------------------------------------------------------------------+
bool CPositionManager::SetBreakEven(double pips, double lockInPips = 0) {
if(pips <= 0) {
m_autoBreakEven = false;
return true;
}
m_autoBreakEven = true;
m_breakEvenAt = pips * m_symbol.Point() * 10; // Convert pips to points
return true;
}
//+------------------------------------------------------------------+
//| Set position scaling parameters |
//+------------------------------------------------------------------+
bool CPositionManager::SetScaling(bool scaleIn, bool scaleOut, double scaleInAt,
double scaleOutAt, double scaleInFactor, double scaleOutFactor) {
m_scaleIn = scaleIn;
m_scaleOut = scaleOut;
if(scaleIn) {
m_scaleInAt = scaleInAt * m_symbol.Point() * 10;
m_scaleInFactor = MathMin(MathMax(scaleInFactor, 0.1), 1.0);
}
if(scaleOut) {
m_scaleOutAt = scaleOutAt * m_symbol.Point() * 10;
m_scaleOutFactor = MathMin(MathMax(scaleOutFactor, 0.1), 1.0);
}
return true;
}
//+------------------------------------------------------------------+
//| Set time-based exit parameters |
//+------------------------------------------------------------------+
bool CPositionManager::SetTimeExit(bool enable, datetime expiry, int maxBars) {
m_useTimeExit = enable;
if(enable) {
if(expiry > 0) m_expiryTime = expiry;
if(maxBars > 0) m_maxHoldBars = maxBars;
}
return true;
}
//+------------------------------------------------------------------+
//| Scale out of an existing position |
//+------------------------------------------------------------------+
bool CPositionManager::ScaleOutPosition(ulong ticket, double volume) {
if(!m_position.SelectByTicket(ticket)) {
Print("Error selecting position ", ticket);
return false;
}
// Calculate volume to close if not specified
if(volume <= 0) {
volume = m_position.Volume() * m_scaleOutFactor;
}
// Close a portion of the position
return PartialClose(ticket, volume / m_position.Volume() * 100);
}
//+------------------------------------------------------------------+
//| Partially close a position |
//+------------------------------------------------------------------+
bool CPositionManager::PartialClose(ulong ticket, double percent) {
if(percent <= 0 || percent > 100) {
Print("Error: Invalid percentage ", percent);
return false;
}
if(!m_position.SelectByTicket(ticket)) {
Print("Error selecting position ", ticket);
return false;
}
// Calculate volume to close
double volume = NormalizeDouble(m_position.Volume() * percent / 100, 2);
if(volume < m_symbol.LotsMin()) {
Print("Error: Volume too small for partial close");
return false;
}
// Close part of the position
return m_trade.PositionClosePartial(ticket, volume);
}
//+------------------------------------------------------------------+
//| Close position at specific time |
//+------------------------------------------------------------------+
bool CPositionManager::CloseAtTime(ulong ticket, datetime closeTime) {
if(TimeCurrent() >= closeTime) {
return ClosePosition(ticket, "Time-based exit");
}
return false;
}
//+------------------------------------------------------------------+
//| Close position when target profit is reached |
//+------------------------------------------------------------------+
bool CPositionManager::CloseAtProfit(ulong ticket, double targetProfit, bool closePartial = false) {
if(!m_position.SelectByTicket(ticket)) {
return false;
}
double currentProfit = m_position.Profit() + m_position.Swap() + m_position.Commission();
if(currentProfit >= targetProfit) {
if(closePartial) {
// Close a portion based on how much we're in profit
double profitRatio = MathMin(1.0, currentProfit / targetProfit);
return PartialClose(ticket, profitRatio * 100);
} else {
return ClosePosition(ticket, "Target profit reached");
}
}
return false;
}
//+------------------------------------------------------------------+
//| Close position when max loss is reached |
//+------------------------------------------------------------------+
bool CPositionManager::CloseAtLoss(ulong ticket, double maxLoss, bool closePartial = false) {
if(!m_position.SelectByTicket(ticket)) {
return false;
}
double currentLoss = MathAbs(m_position.Profit() + m_position.Swap() + m_position.Commission());
if(currentLoss >= maxLoss) {
if(closePartial) {
// Close a portion based on how much we're in loss
double lossRatio = MathMin(1.0, currentLoss / maxLoss);
return PartialClose(ticket, lossRatio * 100);
} else {
return ClosePosition(ticket, "Max loss reached");
}
}
return false;
}
//+------------------------------------------------------------------+
//| Move position to break-even |
//+------------------------------------------------------------------+
bool CPositionManager::MoveToBreakEven(ulong ticket) {
if(!m_position.SelectByTicket(ticket)) {
return false;
}
double openPrice = m_position.PriceOpen();
double currentSl = m_position.StopLoss();
double newSl = 0;
if(m_position.PositionType() == POSITION_TYPE_BUY) {
newSl = openPrice + m_symbol.Spread() * m_symbol.Point();
if(newSl > currentSl || currentSl == 0) {
return ModifyPosition(ticket, newSl, m_position.TakeProfit());
}
} else if(m_position.PositionType() == POSITION_TYPE_SELL) {
newSl = openPrice - m_symbol.Spread() * m_symbol.Point();
if((newSl < currentSl || currentSl == 0) && newSl > 0) {
return ModifyPosition(ticket, newSl, m_position.TakeProfit());
}
}
return false;
}
//+------------------------------------------------------------------+
//| Get position risk in account currency |
//+------------------------------------------------------------------+
double CPositionManager::GetPositionRisk(ulong ticket) {
if(!m_position.SelectByTicket(ticket)) {
return 0;
}
double openPrice = m_position.PriceOpen();
double sl = m_position.StopLoss();
double volume = m_position.Volume();
if(sl == 0) return 0;
double tickValue = m_symbol.TickValue();
double tickSize = m_symbol.TickSize();
double pointValue = tickValue / tickSize * m_symbol.Point();
if(m_position.PositionType() == POSITION_TYPE_BUY) {
return (openPrice - sl) * volume * pointValue;
} else {
return (sl - openPrice) * volume * pointValue;
}
}
//+------------------------------------------------------------------+
//| Get position risk as percentage of account balance |
//+------------------------------------------------------------------+
double CPositionManager::GetPositionRiskPercent(ulong ticket, double accountBalance = 0) {
if(accountBalance <= 0) {
accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
}
double risk = GetPositionRisk(ticket);
return (risk / accountBalance) * 100;
}
//+------------------------------------------------------------------+
//| Get position risk/reward ratio |
//+------------------------------------------------------------------+
double CPositionManager::GetPositionRiskReward(ulong ticket) {
if(!m_position.SelectByTicket(ticket)) {
return 0;
}
double openPrice = m_position.PriceOpen();
double sl = m_position.StopLoss();
double tp = m_position.TakeProfit();
if(sl == 0 || tp == 0) return 0;
if(m_position.PositionType() == POSITION_TYPE_BUY) {
return (tp - openPrice) / (openPrice - sl);
} else {
return (openPrice - tp) / (sl - openPrice);
}
}
//+------------------------------------------------------------------+
//| Get position age in specified time units |
//+------------------------------------------------------------------+
datetime CPositionManager::GetPositionAge(ulong ticket, ENUM_TIME_UNITS unit = PERIOD_M1) {
if(!m_position.SelectByTicket(ticket)) {
return 0;
}
datetime age = TimeCurrent() - m_position.Time();
switch(unit) {
case PERIOD_M1: return age / 60;
case PERIOD_M5: return age / (60 * 5);
case PERIOD_M15: return age / (60 * 15);
case PERIOD_M30: return age / (60 * 30);
case PERIOD_H1: return age / (60 * 60);
case PERIOD_H4: return age / (60 * 60 * 4);
case PERIOD_D1: return age / (60 * 60 * 24);
default: return age;
}
}
//+------------------------------------------------------------------+
//| Get total position volume |
//+------------------------------------------------------------------+
double CPositionManager::GetPositionVolume(const string symbol = "", int magic = -1) {
double volume = 0;
for(int i = 0; i < PositionsTotal(); i++) {
if(m_position.SelectByIndex(i)) {
if((symbol == "" || m_position.Symbol() == symbol) &&
(magic < 0 || (int)m_position.Magic() == magic)) {
volume += m_position.Volume();
}
}
}
return volume;
}
//+------------------------------------------------------------------+
//| Check if there are any open positions |
//+------------------------------------------------------------------+
bool CPositionManager::HasOpenPosition(const string symbol = "", int magic = -1) {
return (TotalPositions(symbol, magic) > 0);
}
//+------------------------------------------------------------------+
//| Get position type |
//+------------------------------------------------------------------+
ENUM_POSITION_TYPE CPositionManager::GetPositionType(ulong ticket) {
if(m_position.SelectByTicket(ticket)) {
return m_position.PositionType();
}
return WRONG_VALUE;
}
//+------------------------------------------------------------------+
//| Get position open price |
//+------------------------------------------------------------------+
double CPositionManager::GetPositionOpenPrice(ulong ticket) {
if(m_position.SelectByTicket(ticket)) {
return m_position.PriceOpen();
}
return 0;
}
//+------------------------------------------------------------------+
//| Get position current price |
//+------------------------------------------------------------------+
double CPositionManager::GetPositionCurrentPrice(ulong ticket) {
if(!m_position.SelectByTicket(ticket)) {
return 0;
}
if(m_position.PositionType() == POSITION_TYPE_BUY) {
return m_symbol.Bid();
} else {
return m_symbol.Ask();
}
}
//+------------------------------------------------------------------+
//| Get position stop loss level |
//+------------------------------------------------------------------+
double CPositionManager::GetPositionStopLoss(ulong ticket) {
if(m_position.SelectByTicket(ticket)) {
return m_position.StopLoss();
}
return 0;
}
//+------------------------------------------------------------------+
//| Get position take profit level |
//+------------------------------------------------------------------+
double CPositionManager::GetPositionTakeProfit(ulong ticket) {
if(m_position.SelectByTicket(ticket)) {
return m_position.TakeProfit();
}
return 0;
}
//+------------------------------------------------------------------+
//| Set volatility-based position sizing |
//+------------------------------------------------------------------+
bool CPositionManager::SetVolatilitySizing(bool enable, int period, double multiplier, double maxRisk) {
m_useVolatilitySizing = enable;
if(enable) {
m_volatilityPeriod = MathMax(period, 5);
m_volatilityMultiplier = MathMax(multiplier, 0.1);
m_maxPositionRisk = MathMin(MathMax(maxRisk, 0.1), 10.0);
}
return true;
}
//+------------------------------------------------------------------+
//| Calculate position size based on volatility |
//+------------------------------------------------------------------+
double CPositionManager::CalculateVolatilityAdjustedVolume(double riskPercent, double slPips) {
if(!m_initialized || !m_useVolatilitySizing) {
return 0.0;
}
// Calculate ATR for volatility
double atr = iATR(m_symbol.Name(), PERIOD_CURRENT, m_volatilityPeriod, 0);
double atrPips = atr / (m_symbol.Point() * 10);
// Get account balance and calculate risk amount
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskAmount = balance * (riskPercent / 100.0);
// Calculate position size based on ATR
double tickValue = m_symbol.TickValue();
double tickSize = m_symbol.TickSize();
double pointValue = tickValue / tickSize * m_symbol.Point();
// Adjust position size based on volatility
double volume = riskAmount / (slPips * 10 * pointValue);
double volatilityAdjustment = m_volatilityMultiplier * (50.0 / atrPips);
volume *= volatilityAdjustment;
// Apply position limits
double minVolume = m_symbol.LotsMin();
double maxVolume = m_symbol.LotsMax();
double stepVolume = m_symbol.LotsStep();
volume = MathFloor(volume / stepVolume) * stepVolume;
volume = MathMin(MathMax(volume, minVolume), maxVolume);
return volume;
}
//+------------------------------------------------------------------+
//| Set correlation filter for position management |
//+------------------------------------------------------------------+
bool CPositionManager::SetCorrelationFilter(string &symbols[], double maxCorrelation) {
ArrayFree(m_correlationSymbols);
int count = ArraySize(symbols);
if(count == 0) return false;
ArrayResize(m_correlationSymbols, count);
for(int i = 0; i < count; i++) {
m_correlationSymbols[i] = symbols[i];
}
m_maxCorrelation = MathMin(MathMax(maxCorrelation, 0.1), 1.0);
return true;
}
//+------------------------------------------------------------------+
//| Get correlation with another symbol |
//+------------------------------------------------------------------+
double CPositionManager::GetCorrelationWith(const string symbol, int period = 100) {
if(!m_initialized || symbol == m_symbol.Name()) return 1.0;
// Get close prices for both symbols
double basePrices[], targetPrices[];
ArraySetAsSeries(basePrices, true);
ArraySetAsSeries(targetPrices, true);
if(CopyClose(m_symbol.Name(), PERIOD_CURRENT, 0, period, basePrices) != period ||
CopyClose(symbol, PERIOD_CURRENT, 0, period, targetPrices) != period) {
Print("Error getting price data for correlation");
return 0.0;
}
// Calculate correlation coefficient
return MathCorrelation(basePrices, targetPrices, period);
}
//+------------------------------------------------------------------+
//| Check if correlation is within safe limits |
//+------------------------------------------------------------------+
bool CPositionManager::IsCorrelationSafe() {
int count = ArraySize(m_correlationSymbols);
if(count == 0) return true;
for(int i = 0; i < count; i++) {
double correlation = GetCorrelationWith(m_correlationSymbols[i]);
if(MathAbs(correlation) > m_maxCorrelation) {
PrintFormat("High correlation (%.2f) with %s", correlation, m_correlationSymbols[i]);
return false;
}
}
return true;
}
//+------------------------------------------------------------------+
//| Configure grid trading parameters |
//+------------------------------------------------------------------+
bool CPositionManager::SetGridTrading(bool enable, double gridStep, int maxLevels, double volumeMultiplier) {
m_gridTrading = enable;
if(enable) {
m_gridStep = MathMax(gridStep, 1.0) * m_symbol.Point() * 10; // Convert pips to points
m_maxGridLevels = MathMax(maxLevels, 1);
m_gridVolumeMultiplier = MathMax(volumeMultiplier, 1.0);
}
return true;
}
//+------------------------------------------------------------------+
//| Manage grid trading levels |
//+------------------------------------------------------------------+
bool CPositionManager::ManageGridLevels(const string symbol, double currentPrice, ENUM_POSITION_TYPE type) {
if(!m_gridTrading || !m_initialized) return false;
// Count existing grid levels
int gridLevels = 0;
for(int i = 0; i < PositionsTotal(); i++) {
if(m_position.SelectByIndex(i) && m_position.Symbol() == symbol) {
gridLevels++;
}
}
// Check if we can add a new grid level
if(gridLevels >= m_maxGridLevels) return false;
// Get the last position in the grid
double lastPrice = (type == POSITION_TYPE_BUY) ? 0 : DBL_MAX;
double totalVolume = 0;
for(int i = 0; i < PositionsTotal(); i++) {
if(m_position.SelectByIndex(i) && m_position.Symbol() == symbol &&
m_position.PositionType() == type) {
if((type == POSITION_TYPE_BUY && m_position.PriceOpen() > lastPrice) ||
(type == POSITION_TYPE_SELL && m_position.PriceOpen() < lastPrice)) {
lastPrice = m_position.PriceOpen();
}
totalVolume += m_position.Volume();
}
}
// Calculate next grid level
double nextLevel = 0;
double volume = 0;
if(type == POSITION_TYPE_BUY) {
nextLevel = (lastPrice == 0) ? currentPrice : lastPrice - m_gridStep;
if(currentPrice <= nextLevel) {
volume = totalVolume * m_gridVolumeMultiplier;
return OpenPosition(symbol, type, volume, 0, 0, 0, "Grid Level " + IntegerToString(gridLevels + 1));
}
} else {
nextLevel = (lastPrice == DBL_MAX) ? currentPrice : lastPrice + m_gridStep;
if(currentPrice >= nextLevel) {
volume = totalVolume * m_gridVolumeMultiplier;
return OpenPosition(symbol, type, volume, 0, 0, 0, "Grid Level " + IntegerToString(gridLevels + 1));
}
}
return false;
}
//+------------------------------------------------------------------+
//| Configure news filter parameters |
//+------------------------------------------------------------------+
bool CPositionManager::SetNewsFilter(bool enable, int minImpact, int minutesBefore, int minutesAfter) {
m_newsFilter = enable;
if(enable) {
m_newsImpact = MathMin(MathMax(minImpact, 1), 3);
m_minutesBeforeNews = MathMax(minutesBefore, 1);
m_minutesAfterNews = MathMax(minutesAfter, 1);
}
return true;
}
//+------------------------------------------------------------------+
//| Check if a high-impact news event is imminent |
//+------------------------------------------------------------------+
bool CPositionManager::IsNewsEventImminent() {
if(!m_newsFilter) return false;
// This is a placeholder - implement actual news checking logic
// using your preferred news feed or calendar
return false;
}
//+------------------------------------------------------------------+
//| Check if high-impact news is pending |
//+------------------------------------------------------------------+
bool CPositionManager::IsHighImpactNewsPending() {
if(!m_newsFilter) return false;
// This is a placeholder - implement actual news checking logic
// using your preferred news feed or calendar
return false;
}
//+------------------------------------------------------------------+
//| Set dynamic risk parameters |
//+------------------------------------------------------------------+
bool CPositionManager::SetDynamicRisk(bool enable, double maxDailyDrawdown, double maxPositionRisk,
double maxPortfolioRisk, double riskDecay) {
m_useDynamicRisk = enable;
if(enable) {
m_maxDailyDrawdown = MathMin(MathMax(maxDailyDrawdown, 1.0), 50.0);
m_maxPositionRisk = MathMin(MathMax(maxPositionRisk, 0.1), 10.0);
m_maxPortfolioRisk = MathMin(MathMax(maxPortfolioRisk, 1.0), 50.0);
m_riskDecayFactor = MathMin(MathMax(riskDecay, 0.5), 0.99);
// Initialize equity peak
if(m_equityPeak == 0) {
m_equityPeak = AccountInfoDouble(ACCOUNT_EQUITY);
}
}
return true;
}
//+------------------------------------------------------------------+
//| Calculate dynamic risk based on performance |
//+------------------------------------------------------------------+
double CPositionManager::CalculateDynamicRisk(double baseRisk) {
if(!m_useDynamicRisk) return baseRisk;
// Check drawdown limits
if(!CheckDrawdownLimits()) {
return 0.0; // Stop trading if drawdown limit reached
}
// Calculate risk based on performance
double riskMultiplier = 1.0;
// Reduce risk after losses
double currentDrawdown = GetCurrentDrawdown();
if(currentDrawdown > 0) {
riskMultiplier *= MathPow(m_riskDecayFactor, currentDrawdown / (m_maxDailyDrawdown / 2.0));
}
// Adjust based on win rate
if(m_tradeCount > 10) {
if(m_winRate < 0.4) {
riskMultiplier *= 0.5; // Reduce risk if win rate is low
} else if(m_winRate > 0.6) {
riskMultiplier *= 1.2; // Slightly increase risk if win rate is high
}
}
// Apply position and portfolio limits
double maxRisk = MathMin(m_maxPositionRisk, m_maxPortfolioRisk / (PositionsTotal() + 1));
return MathMin(baseRisk * riskMultiplier, maxRisk);
}
//+------------------------------------------------------------------+
//| Check if drawdown limits are exceeded |
//+------------------------------------------------------------------+
bool CPositionManager::CheckDrawdownLimits() {
if(!m_useDynamicRisk) return true;
double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
m_equityPeak = MathMax(m_equityPeak, currentEquity);
double drawdown = 1.0 - (currentEquity / m_equityPeak);
return (drawdown * 100.0) < m_maxDailyDrawdown;
}
//+------------------------------------------------------------------+
//| Get current drawdown percentage |
//+------------------------------------------------------------------+
double CPositionManager::GetCurrentDrawdown() {
double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
m_equityPeak = MathMax(m_equityPeak, currentEquity);
return (1.0 - (currentEquity / m_equityPeak)) * 100.0;
}
//+------------------------------------------------------------------+
//| Set adaptive sizing parameters |
//+------------------------------------------------------------------+
bool CPositionManager::SetAdaptiveSizing(bool enable, double initialWinRate, double initialProfitFactor) {
m_useAdaptiveSizing = enable;
if(enable) {
m_winRate = MathMin(MathMax(initialWinRate, 0.01), 0.99);
m_profitFactor = MathMax(initialProfitFactor, 0.1);
m_tradeCount = 0;
}
return true;
}
//+------------------------------------------------------------------+
//| Update trade statistics |
//+------------------------------------------------------------------+
void CPositionManager::UpdateTradeStats(bool isWin, double profit) {
if(!m_useAdaptiveSizing) return;
m_tradeCount++;
// Update win rate
double winIncrement = (isWin ? 1.0 : 0.0 - m_winRate) / MathMin(m_tradeCount, 100);
m_winRate = MathMin(MathMax(m_winRate + winIncrement, 0.01), 0.99);
// Update profit factor (simplified)
static double totalProfit = 0, totalLoss = 0.01; // Avoid division by zero
if(profit > 0) totalProfit += profit;
else totalLoss += MathAbs(profit);
m_profitFactor = totalProfit / totalLoss;
}
//+------------------------------------------------------------------+
//| Calculate adaptive position size |
//+------------------------------------------------------------------+
double CPositionManager::CalculateAdaptiveVolume(double baseVolume, double confidence = 1.0) {
if(!m_useAdaptiveSizing || m_tradeCount < 5) return baseVolume;
// Kelly criterion for position sizing
double kelly = CalculateKellyCriterion(m_winRate, m_profitFactor);
// Adjust based on confidence
double adjustedVolume = baseVolume * kelly * confidence;
// Apply limits
double minVolume = m_symbol.LotsMin();
double maxVolume = m_symbol.LotsMax();
return MathMin(MathMax(adjustedVolume, minVolume), maxVolume);
}
//+------------------------------------------------------------------+
//| Calculate optimal fraction using Kelly Criterion |
//+------------------------------------------------------------------+
double CPositionManager::CalculateKellyCriterion(double winRate, double winLossRatio) {
// Kelly % = W - [(1 - W) / R]
// Where: W = Win probability, R = Win/loss ratio
return winRate - ((1.0 - winRate) / winLossRatio);
}
//+------------------------------------------------------------------+
//| Set trailing drawdown exit |
//+------------------------------------------------------------------+
bool CPositionManager::SetTrailingDrawdown(bool enable, double drawdownPercent) {
m_useTrailingDrawdown = enable;
if(enable) {
m_trailingDrawdown = MathMin(MathMax(drawdownPercent, 1.0), 50.0);
}
return true;
}
//+------------------------------------------------------------------+
//| Set volatility-based exit |
//+------------------------------------------------------------------+
bool CPositionManager::SetVolatilityExit(bool enable, double volatilityThreshold) {
m_useVolatilityExit = enable;
if(enable) {
m_volatilityExitLevel = MathMax(volatilityThreshold, 0.5);
}
return true;
}
//+------------------------------------------------------------------+
//| Check advanced exit conditions |
//+------------------------------------------------------------------+
bool CPositionManager::CheckAdvancedExitConditions(ulong ticket) {
if(!m_position.SelectByTicket(ticket)) return false;
// Check trailing drawdown
if(m_useTrailingDrawdown) {
double openPrice = m_position.PriceOpen();
double currentPrice = (m_position.PositionType() == POSITION_TYPE_BUY) ? m_symbol.Bid() : m_symbol.Ask();
double profit = m_position.Profit() + m_position.Swap() + m_position.Commission();
if(profit > 0) {
double maxProfit = MathMax(m_position.Profit() + m_position.Swap() + m_position.Commission(), 0.01);
double currentDrawdown = (maxProfit - profit) / maxProfit * 100.0;
if(currentDrawdown >= m_trailingDrawdown) {
return ClosePosition(ticket, "Trailing drawdown hit");
}
}
}
// Check volatility exit
if(m_useVolatilityExit) {
double atr = iATR(m_symbol.Name(), PERIOD_CURRENT, m_volatilityPeriod, 0);
double atrPct = (atr / m_symbol.Ask()) * 100.0;
if(atrPct >= m_volatilityExitLevel) {
return ClosePosition(ticket, "High volatility exit");
}
}
return false;
}
//+------------------------------------------------------------------+
//| Set position clustering |
//+------------------------------------------------------------------+
bool CPositionManager::SetPositionClustering(bool enable, double clusterDistance, int maxClusters) {
m_usePositionClustering = enable;
if(enable) {
m_clusterDistance = MathMax(clusterDistance, 1.0) * m_symbol.Point() * 10; // Convert pips to points
m_maxClusters = MathMax(maxClusters, 1);
}
return true;
}
//+------------------------------------------------------------------+
//| Check if price is in existing cluster |
//+------------------------------------------------------------------+
bool CPositionManager::IsInExistingCluster(const string symbol, double price, ENUM_POSITION_TYPE type) {
if(!m_usePositionClustering) return false;
// This is a simplified implementation
// In a real system, you'd track clusters more precisely
for(int i = 0; i < PositionsTotal(); i++) {
if(m_position.SelectByIndex(i) && m_position.Symbol() == symbol &&
m_position.PositionType() == type) {
double distance = MathAbs(m_position.PriceOpen() - price) / (m_symbol.Point() * 10);
if(distance <= m_clusterDistance) {
return true;
}
}
}
return false;
}
//+------------------------------------------------------------------+
//| Set machine learning integration |
//+------------------------------------------------------------------+
bool CPositionManager::SetMLIntegration(bool enable, double confidenceThreshold) {
m_useMLPredictions = enable;
if(enable) {
m_mlConfidenceThreshold = MathMin(MathMax(confidenceThreshold, 0.5), 0.95);
}
return true;
}
//+------------------------------------------------------------------+
//| Get ML prediction for symbol/timeframe |
//+------------------------------------------------------------------+
double CPositionManager::GetMLPrediction(const string symbol, ENUM_TIMEFRAMES timeframe) {
if(!m_useMLPredictions) return 0.0;
// This is a placeholder - implement actual ML prediction
// Return values: >0 for buy, <0 for sell, magnitude = confidence
return 0.0; // No prediction available
}
//+------------------------------------------------------------------+
//| Set market regime filter |
//+------------------------------------------------------------------+
bool CPositionManager::SetRegimeFilter(bool enable, int period, double trendThreshold, double rangeThreshold) {
m_useRegimeFilter = enable;
if(enable) {
m_regimePeriod = MathMax(period, 10);
m_trendThreshold = MathMin(MathMax(trendThreshold, 0.1), 0.5);
m_rangeThreshold = MathMin(MathMax(rangeThreshold, 0.05), 0.3);
}
return true;
}
//+------------------------------------------------------------------+
//| Detect current market regime |
//+------------------------------------------------------------------+
ENUM_MARKET_REGIME CPositionManager::DetectMarketRegime() {
if(!m_useRegimeFilter) return REGIME_TRENDING;
// Calculate ADX for trend strength
double adx = iADX(m_symbol.Name(), PERIOD_CURRENT, m_regimePeriod, PRICE_CLOSE, MODE_MAIN, 0);
// Calculate ATR for volatility
double atr = iATR(m_symbol.Name(), PERIOD_CURRENT, m_regimePeriod, 0);
double atrPct = (atr / m_symbol.Ask()) * 100.0;
// Determine regime
if(adx > 25) {
return REGIME_TRENDING;
} else if(atrPct > m_rangeThreshold * 2) {
return REGIME_VOLATILE;
} else {
return REGIME_RANGING;
}
}
//+------------------------------------------------------------------+
//| Check if position type is favorable for current regime |
//+------------------------------------------------------------------+
bool CPositionManager::IsFavorableRegime(ENUM_POSITION_TYPE type, ENUM_MARKET_REGIME regime) {
if(!m_useRegimeFilter) return true;
// This is a simplified implementation
switch(regime) {
case REGIME_TRENDING:
return true; // All positions okay in trends
case REGIME_RANGING:
return false; // Avoid new positions in ranging markets
case REGIME_VOLATILE:
return (type == POSITION_TYPE_BUY); // Prefer long in high volatility
default:
return true;
}
}
//+------------------------------------------------------------------+
//| Calculate position score based on multiple factors |
//+------------------------------------------------------------------+
double CPositionManager::CalculatePositionScore(const string symbol, ENUM_POSITION_TYPE type) {
double score = 0.0;
// 1. Trend strength (higher is better)
double adx = iADX(symbol, PERIOD_CURRENT, 14, PRICE_CLOSE, MODE_MAIN, 0);
score += NormalizeDouble(adx / 50.0, 2); // Normalize to 0-1 range
// 2. Volatility (moderate is best)
double atr = iATR(symbol, PERIOD_CURRENT, 14, 0);
double atrPct = (atr / SymbolInfoDouble(symbol, SYMBOL_ASK)) * 100.0;
score += 1.0 - MathAbs(0.5 - MathMin(atrPct, 1.0)); // Best at 0.5% ATR
// 3. Volume (higher is better)
double volume[];
if(CopyTickVolume(symbol, PERIOD_CURRENT, 0, 5, volume) == 5) {
double avgVolume = (volume[0] + volume[1] + volume[2] + volume[3] + volume[4]) / 5.0;
score += MathMin(avgVolume / 1000.0, 1.0); // Normalize
}
// 4. ML prediction (if available)
if(m_useMLPredictions) {
double mlSignal = GetMLPrediction(symbol, PERIOD_CURRENT);
if((type == POSITION_TYPE_BUY && mlSignal > 0) ||
(type == POSITION_TYPE_SELL && mlSignal < 0)) {
score += MathAbs(mlSignal);
}
}
// 5. Market regime
ENUM_MARKET_REGIME regime = DetectMarketRegime();
if(IsFavorableRegime(type, regime)) {
score += 0.5;
}
return NormalizeDouble(score, 2);
}
//+------------------------------------------------------------------+
//| Place bracket order with advanced features |
//+------------------------------------------------------------------+
bool CPositionManager::PlaceBracketOrder(const string symbol, ENUM_ORDER_TYPE type, double volume,
double entryPrice, double sl, double tp, double breakEvenAt,
double trailingStop, datetime expiration, const string comment) {
// Implement bracket order logic
// This would include entry order, stop loss, take profit, and optional break-even/trailing
// Placeholder implementation
return OpenPosition(symbol, (type == ORDER_TYPE_BUY) ? POSITION_TYPE_BUY : POSITION_TYPE_SELL,
volume, entryPrice, sl, tp, comment);
}
//+------------------------------------------------------------------+
//| Place OCO (One-Cancels-Other) orders |
//+------------------------------------------------------------------+
bool CPositionManager::PlaceOCOOrders(const string symbol, ENUM_ORDER_TYPE type, double volume,
double price1, double price2, double sl, double tp, const string comment) {
// Implement OCO order logic
// This would place two orders where if one is filled, the other is cancelled
// Placeholder implementation
return OpenPosition(symbol, (type == ORDER_TYPE_BUY) ? POSITION_TYPE_BUY : POSITION_TYPE_SELL,
volume, (type == ORDER_TYPE_BUY) ? price1 : price2, sl, tp, comment + " OCO");
}
//+------------------------------------------------------------------+
//| Monitor position and apply advanced management |
//+------------------------------------------------------------------+
bool CPositionManager::MonitorPosition(ulong ticket) {
if(!m_position.SelectByTicket(ticket)) return false;
// Update position statistics
UpdatePositionStats(ticket);
// Check if position needs adjustment
if(NeedsAdjustment(ticket)) {
// Apply adjustments (trailing stop, take profit, etc.)
return true;
}
// Check advanced exit conditions
return CheckAdvancedExitConditions(ticket);
}
//+------------------------------------------------------------------+
//| Update position statistics |
//+------------------------------------------------------------------+
void CPositionManager::UpdatePositionStats(ulong ticket) {
// Update statistics like time in trade, drawdown, etc.
// This would be called periodically for each open position
}
//+------------------------------------------------------------------+
//| Check if position needs adjustment |
//+------------------------------------------------------------------+
bool CPositionManager::NeedsAdjustment(ulong ticket) {
// Check if position needs any adjustments
// (e.g., moving stop to break-even, trailing stop, etc.)
return false;
}
//+------------------------------------------------------------------+
//| Check for volatility spikes |
//+------------------------------------------------------------------+
bool CPositionManager::CheckVolatilitySpike() {
// Check if current volatility is significantly higher than average
double currentAtr = iATR(m_symbol.Name(), PERIOD_CURRENT, 14, 0);
double avgAtr = iATR(m_symbol.Name(), PERIOD_CURRENT, 100, 0);
return (currentAtr > avgAtr * 2.0); // 2x average volatility
}
//+------------------------------------------------------------------+
//| Check market liquidity |
//+------------------------------------------------------------------+
bool CPositionManager::CheckLiquidity(const string symbol) {
// Check if there's sufficient liquidity for the trade
// This is a simplified check
double spread = SymbolInfoInteger(symbol, SYMBOL_SPREAD) * m_symbol.Point();
double avgSpread = iATR(symbol, PERIOD_CURRENT, 100, 0) / 10.0; // Simplified
return (spread <= avgSpread * 2.0); // Allow up to 2x average spread
}
//+------------------------------------------------------------------+
//| Check if current time is within trading hours |
//+------------------------------------------------------------------+
bool CPositionManager::CheckMarketHours() {
// Check if current time is within allowed trading hours
// This is a placeholder - implement based on your trading strategy
MqlDateTime time;
TimeToStruct(TimeCurrent(), time);
// Example: Only trade during main session hours (8 AM to 4 PM server time)
return (time.hour >= 8 && time.hour < 16);
}
//+------------------------------------------------------------------+
//| Set scaling profile |
//+------------------------------------------------------------------+
bool CPositionManager::SetScalingProfile(ENUM_SCALING_PROFILE profile) {
switch(profile) {
case SCALING_AGGRESSIVE:
m_scaleInFactor = 1.5;
m_scaleOutFactor = 0.5;
m_scaleInAt = 0.5; // Scale in at 0.5R drawdown
m_scaleOutAt = 1.5; // Scale out at 1.5R profit
break;
case SCALING_MODERATE:
m_scaleInFactor = 1.2;
m_scaleOutFactor = 0.7;
m_scaleInAt = 0.7;
m_scaleOutAt = 2.0;
break;
case SCALING_CONSERVATIVE:
m_scaleInFactor = 1.0;
m_scaleOutFactor = 1.0;
m_scaleInAt = 1.0;
m_scaleOutAt = 3.0;
break;
default:
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Calculate optimal scale-in volume |
//+------------------------------------------------------------------+
double CPositionManager::CalculateOptimalScaleInVolume(double baseVolume, int scaleLevel, double drawdownPct) {
if(scaleLevel <= 0) return 0.0;
// Reduce volume as drawdown increases
double drawdownFactor = 1.0 - (drawdownPct / 100.0);
drawdownFactor = MathMax(drawdownFactor, 0.1); // Minimum 10% of base volume
// Scale volume based on level and drawdown
double scaleFactor = MathPow(m_scaleInFactor, scaleLevel) * drawdownFactor;
// Apply position limits
double minVolume = m_symbol.LotsMin();
double maxVolume = m_symbol.LotsMax();
double stepVolume = m_symbol.LotsStep();
double volume = baseVolume * scaleFactor;
volume = MathFloor(volume / stepVolume) * stepVolume;
return MathMin(MathMax(volume, minVolume), maxVolume);
}
//+------------------------------------------------------------------+
//| Check for scaling opportunities |
//+------------------------------------------------------------------+
bool CPositionManager::CheckScalingOpportunity(ulong ticket, double &volume) {
if(!m_position.SelectByTicket(ticket)) return false;
double currentPrice = (m_position.PositionType() == POSITION_TYPE_BUY) ? m_symbol.Bid() : m_symbol.Ask();
double openPrice = m_position.PriceOpen();
double sl = m_position.StopLoss();
double tp = m_position.TakeProfit();
if(sl == 0.0 || tp == 0.0) return false; // No scaling without proper stops
// Calculate price movement in pips
double priceDiff = (currentPrice - openPrice) / m_symbol.Point();
if(m_position.PositionType() == POSITION_TYPE_SELL) priceDiff = -priceDiff;
// Check if we should scale in
if(priceDiff <= -m_scaleInAt * 10) { // Convert to pips
int scaleLevel = (int)(-priceDiff / (m_scaleInAt * 10));
double drawdownPct = MathAbs(priceDiff / ((openPrice - sl) / m_symbol.Point())) * 100.0;
volume = CalculateOptimalScaleInVolume(m_position.Volume(), scaleLevel, drawdownPct);
return (volume > 0);
}
return false;
}
//+------------------------------------------------------------------+
//| Scale into a position |
//+------------------------------------------------------------------+
bool CPositionManager::ScaleInPosition(ulong ticket, double volume) {
if(!m_position.SelectByTicket(ticket)) return false;
string symbol = m_position.Symbol();
ENUM_POSITION_TYPE type = m_position.PositionType();
double openPrice = (type == POSITION_TYPE_BUY) ? m_symbol.Ask() : m_symbol.Bid();
// Calculate new average price and position size
double currentVolume = m_position.Volume();
double currentPrice = m_position.PriceOpen();
double newVolume = currentVolume + volume;
double newPrice = (currentPrice * currentVolume + openPrice * volume) / newVolume;
// Close existing position
if(!ClosePosition(ticket, "Scaling in")) return false;
// Open new position with updated size and price
return OpenPosition(symbol, type, newVolume, newPrice, m_position.StopLoss(),
m_position.TakeProfit(), m_position.Comment() + " Scaled In");
}
//+------------------------------------------------------------------+
//| Scale out of a position |
//+------------------------------------------------------------------+
bool CPositionManager::ScaleOutPosition(ulong ticket, double volume) {
if(!m_position.SelectByTicket(ticket)) return false;
double currentVolume = m_position.Volume();
if(volume >= currentVolume) return ClosePosition(ticket, "Full close");
// Close partial position
if(!ClosePartial(ticket, volume, "Scale out")) return false;
return true;
}
//+------------------------------------------------------------------+
//| Set exit profile |
//+------------------------------------------------------------------+
bool CPositionManager::SetExitProfile(ENUM_EXIT_PROFILE profile) {
switch(profile) {
case EXIT_AGGRESSIVE:
m_trailingStop = 20; // 20 pips trailing stop
m_breakEvenAt = 15; // Move to breakeven at 15 pips
m_useTimeExit = true;
m_maxHoldBars = 50; // Close after 50 bars
break;
case EXIT_MODERATE:
m_trailingStop = 30;
m_breakEvenAt = 20;
m_useTimeExit = true;
m_maxHoldBars = 100;
break;
case EXIT_CONSERVATIVE:
m_trailingStop = 50;
m_breakEvenAt = 30;
m_useTimeExit = false;
m_maxHoldBars = 0; // No time-based exit
break;
default:
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Check exit conditions |
//+------------------------------------------------------------------+
bool CPositionManager::CheckExitConditions(ulong ticket, double &closePrice, string &reason) {
if(!m_position.SelectByTicket(ticket)) return false;
// Check standard exit conditions
if(CheckAdvancedExitConditions(ticket)) {
reason = "Advanced exit condition";
return true;
}
// Check time-based exit
if(m_useTimeExit && UseTimeExit(ticket, m_position.Time())) {
reason = "Time-based exit";
return true;
}
// Check volatility-based exit
if(m_useVolatilityExit) {
double atr = iATR(m_symbol.Name(), PERIOD_CURRENT, m_volatilityPeriod, 0);
double atrPct = (atr / m_symbol.Ask()) * 100.0;
if(atrPct >= m_volatilityExitLevel) {
reason = "High volatility exit";
return true;
}
}
// Check pyramiding exit
double closeVolume = 0.0;
if(UsePyramidingExit(ticket, closeVolume)) {
reason = "Pyramiding exit";
closePrice = (m_position.PositionType() == POSITION_TYPE_BUY) ? m_symbol.Bid() : m_symbol.Ask();
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Use pyramiding exit strategy |
//+------------------------------------------------------------------+
bool CPositionManager::UsePyramidingExit(ulong ticket, double &closeVolume) {
if(!m_position.SelectByTicket(ticket)) return false;
// Get all positions for this symbol
int totalPositions = 0;
double totalVolume = 0.0;
double totalProfit = 0.0;
for(int i = PositionsTotal() - 1; i >= 0; i--) {
if(m_position.SelectByIndex(i) && m_position.Symbol() == m_symbol.Name() &&
m_position.PositionType() == m_position.PositionType()) {
totalPositions++;
totalVolume += m_position.Volume();
totalProfit += m_position.Profit() + m_position.Swap() + m_position.Commission();
}
}
// If we have multiple positions and the total profit is positive, consider closing some
if(totalPositions > 1 && totalProfit > 0) {
// Close 50% of the smallest positions
closeVolume = totalVolume * 0.5;
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Check if time-based exit is needed |
//+------------------------------------------------------------------+
bool CPositionManager::UseTimeExit(ulong ticket, datetime openTime) {
if(!m_useTimeExit || m_maxHoldBars <= 0) return false;
datetime currentTime = TimeCurrent();
int barsHeld = iBarShift(m_symbol.Name(), PERIOD_CURRENT, openTime, true);
return (barsHeld >= m_maxHoldBars);
}
//+------------------------------------------------------------------+
//| Check if volatility-based exit is needed |
//+------------------------------------------------------------------+
bool CPositionManager::UseVolatilityExit(ulong ticket, double currentVolatility) {
if(!m_useVolatilityExit) return false;
double atr = iATR(m_symbol.Name(), PERIOD_CURRENT, m_volatilityPeriod, 0);
double atrPct = (atr / m_symbol.Ask()) * 100.0;
return (atrPct >= m_volatilityExitLevel);
}
//+------------------------------------------------------------------+
//| Calculate correlation-adjusted volume |
//+------------------------------------------------------------------+
double CPositionManager::CalculateCorrelationAdjustedVolume(const string symbol, double baseVolume) {
if(!m_initialized) return baseVolume;
// Get correlation with existing portfolio
double correlation = GetPortfolioCorrelation(symbol);
// Reduce position size for highly correlated positions
if(correlation > 0.7) {
return baseVolume * (1.0 - (correlation - 0.7) * 0.5);
}
return baseVolume;
}
//+------------------------------------------------------------------+
//| Get portfolio correlation for a symbol |
//+------------------------------------------------------------------+
double CPositionManager::GetPortfolioCorrelation(const string symbol) {
if(PositionsTotal() == 0) return 0.0;
double totalCorrelation = 0.0;
int count = 0;
// Calculate average correlation with existing positions
for(int i = PositionsTotal() - 1; i >= 0; i--) {
if(m_position.SelectByIndex(i)) {
string posSymbol = m_position.Symbol();
if(posSymbol != symbol) {
double corr = GetMarketCorrelation(symbol, posSymbol);
totalCorrelation += MathAbs(corr);
count++;
}
}
}
return (count > 0) ? (totalCorrelation / count) : 0.0;
}
//+------------------------------------------------------------------+
//| Get market correlation between two symbols |
//+------------------------------------------------------------------+
double CPositionManager::GetMarketCorrelation(const string symbol1, const string symbol2, int period = 100) {
if(symbol1 == symbol2) return 1.0;
// Get close prices for both symbols
double prices1[], prices2[];
ArraySetAsSeries(prices1, true);
ArraySetAsSeries(prices2, true);
if(CopyClose(symbol1, PERIOD_CURRENT, 0, period, prices1) != period ||
CopyClose(symbol2, PERIOD_CURRENT, 0, period, prices2) != period) {
Print("Error getting price data for correlation");
return 0.0;
}
// Calculate correlation coefficient
return MathCorrelation(prices1, prices2, period);
}
//+------------------------------------------------------------------+
//| Load machine learning model |
//+------------------------------------------------------------------+
bool CPositionManager::LoadMLModel(const string modelFile) {
// Placeholder for ML model loading
// In a real implementation, this would load a pre-trained model
Print("Loading ML model: ", modelFile);
return true;
}
//+------------------------------------------------------------------+
//| Get ML-based position score |
//+------------------------------------------------------------------+
double CPositionManager::GetMLPositionScore(const string symbol, ENUM_POSITION_TYPE type) {
if(!m_useMLPredictions) return 0.0;
// Placeholder for ML prediction
// In a real implementation, this would use the loaded model to predict
// Return a random score for demonstration
return MathRand() / 32767.0;
}
//+------------------------------------------------------------------+
//| Update ML model with new data |
//+------------------------------------------------------------------+
bool CPositionManager::UpdateMLModel(const MqlRates &rates[], const double &features[]) {
if(!m_useMLPredictions) return false;
// Placeholder for model updating
// In a real implementation, this would update the model with new data
return true;
}
//+------------------------------------------------------------------+
//| Update position manager state |
//+------------------------------------------------------------------+
void CPositionManager::Update() {
// Update trailing stops if enabled
if(m_trailingStop > 0) {
CheckTrailingStop();
}
// Update break-even if enabled
if(m_autoBreakEven) {
for(int i = 0; i < PositionsTotal(); i++) {
if(m_position.SelectByIndex(i)) {
double profit = m_position.Profit() + m_position.Swap() + m_position.Commission();
double openPrice = m_position.PriceOpen();
double currentPrice = (m_position.PositionType() == POSITION_TYPE_BUY) ? m_symbol.Bid() : m_symbol.Ask();
// Check if we should move to break-even
if((m_position.PositionType() == POSITION_TYPE_BUY &&
currentPrice - openPrice >= m_breakEvenAt) ||
(m_position.PositionType() == POSITION_TYPE_SELL &&
openPrice - currentPrice >= m_breakEvenAt)) {
MoveToBreakEven(m_position.Ticket());
}
}
}
}
// Check time-based exits
if(m_useTimeExit) {
datetime currentTime = TimeCurrent();
for(int i = PositionsTotal() - 1; i >= 0; i--) {
if(m_position.SelectByIndex(i)) {
// Check position age in bars if enabled
if(m_maxHoldBars > 0) {
int barsHeld = (int)((currentTime - m_position.Time()) / PeriodSeconds(PERIOD_CURRENT));
if(barsHeld >= m_maxHoldBars) {
ClosePosition(m_position.Ticket(), "Max hold time reached");
continue;
}
}
// Check expiry time if set
if(m_expiryTime > 0 && currentTime >= m_expiryTime) {
ClosePosition(m_position.Ticket(), "Position expired");
}
}
}
}
// Check scaling opportunities
if(m_scaleIn || m_scaleOut) {
for(int i = 0; i < PositionsTotal(); i++) {
if(m_position.SelectByIndex(i)) {
double openPrice = m_position.PriceOpen();
double currentPrice = (m_position.PositionType() == POSITION_TYPE_BUY) ? m_symbol.Bid() : m_symbol.Ask();
double priceDiff = MathAbs(currentPrice - openPrice);
// Check for scale-in opportunities
if(m_scaleIn && priceDiff >= m_scaleInAt) {
ScaleInPosition(m_position.Ticket());
}
// Check for scale-out opportunities
if(m_scaleOut && priceDiff >= m_scaleOutAt) {
ScaleOutPosition(m_position.Ticket());
}
}
}
}
}
//+------------------------------------------------------------------+