2082 lines
82 KiB
MQL5
2082 lines
82 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| 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());
| |||
}
| |||
}
| |||
}
| |||
}
| |||
}
| |||
//+------------------------------------------------------------------+
|