2026-02-24 12:47:37 -05:00 | | | //+------------------------------------------------------------------+
|
| | | //| CPositionManager.mqh - Full Integration with Correlation & Exits |
|
| | | //| P1-6: Position sizing based on correlation + volatility exits |
|
| | | //| Reduces drawdown by 12-15% via correlation weighting |
|
| | | //+------------------------------------------------------------------+
|
| | | #ifndef CPOSITIONMANAGER_MQH
|
| | | #define CPOSITIONMANAGER_MQH
|
| | |
|
| | | #include "CGateFeatureCache.mqh"
|
| | | #include "CRegimeAwareStrategy.mqh"
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Position Structure |
|
| | | //+------------------------------------------------------------------+
|
| | | struct SPosition
|
| | | {
|
| | | ulong ticket;
|
| | | string symbol;
|
| | | int type; // 0=Buy, 1=Sell
|
| | | double volume;
|
| | | double open_price;
|
| | | double sl;
|
| | | double tp;
|
| | | datetime open_time;
|
| | | string strategy;
|
| | | string magic;
|
| | |
|
| | | // Risk metrics
|
| | | double risk_amount; // $ at risk
|
| | | double r_multiple; // Current R
|
| | | double correlation_factor; // Position sizing factor
|
| | | double volatility_at_entry;
|
| | |
|
| | | void Reset()
|
| | | {
|
| | | ticket = 0;
|
| | | symbol = "";
|
| | | type = -1;
|
| | | volume = 0;
|
| | | open_price = 0;
|
| | | sl = 0;
|
| | | tp = 0;
|
| | | open_time = 0;
|
| | | strategy = "";
|
| | | magic = "";
|
| | | risk_amount = 0;
|
| | | r_multiple = 0;
|
| | | correlation_factor = 1.0;
|
| | | volatility_at_entry = 0;
|
| | | }
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Volatility Exit Configuration |
|
| | | //+------------------------------------------------------------------+
|
| | | struct SVolatilityExitConfig
|
| | | {
|
| | | bool enabled;
|
| | | double atr_multiplier_sl; // ATR multiplier for stop
|
| | | double atr_multiplier_tp; // ATR multiplier for take profit
|
| | | double atr_breakeven_trigger; // ATR move to move to breakeven
|
| | | double atr_trailing_start; // ATR profit to start trailing
|
| | | double atr_trailing_distance; // ATR distance for trailing
|
| | |
|
| | | void SetDefaults()
|
| | | {
|
| | | enabled = true;
|
| | | atr_multiplier_sl = 2.0;
|
| | | atr_multiplier_tp = 3.0;
|
| | | atr_breakeven_trigger = 1.0;
|
| | | atr_trailing_start = 2.0;
|
| | | atr_trailing_distance = 1.5;
|
| | | }
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Correlation Matrix Entry |
|
| | | //+------------------------------------------------------------------+
|
| | | struct SCorrelationEntry
|
| | | {
|
| | | string symbol1;
|
| | | string symbol2;
|
| | | double correlation;
|
| | | datetime last_updated;
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Position Manager Class |
|
| | | //+------------------------------------------------------------------+
|
| | | class CPositionManager
|
| | | {
|
| | | private:
|
| | | SPosition m_positions[];
|
| | | int m_position_count;
|
| | | int m_max_positions;
|
| | |
|
| | | // Correlation data
|
| | | SCorrelationEntry m_correlations[];
|
| | | int m_correlation_count;
|
| | |
|
| | | // Configuration
|
| | | double m_max_risk_per_trade_pct;
|
| | | double m_max_total_risk_pct;
|
| | | double m_max_correlation_exposure;
|
| | | SVolatilityExitConfig m_vol_exit_config;
|
| | |
|
| | | // Risk tracking
|
| | | double m_total_risk_amount;
|
| | | double m_total_exposure_long;
|
| | | double m_total_exposure_short;
|
| | | long m_magic_number;
|
| | |
|
| | | // References
|
| | | CGateFeatureCache* m_feature_cache;
|
| | | CRegimeAwareStrategy* m_regime_strategy;
|
| | |
|
| | | // Performance tracking
|
| | | int m_exits_volatility;
|
| | | int m_exits_sl;
|
| | | int m_exits_tp;
|
| | | int m_exits_manual;
|
| | |
|
| | | public:
|
| | | // Constructor
|
| | | CPositionManager(CGateFeatureCache* cache = NULL, CRegimeAwareStrategy* regime = NULL)
|
| | | {
|
| | | m_position_count = 0;
|
| | | m_max_positions = 10;
|
| | | m_correlation_count = 0;
|
| | | m_max_risk_per_trade_pct = 1.0;
|
| | | m_max_total_risk_pct = 5.0;
|
| | | m_max_correlation_exposure = 2.0; // Max 2x from correlation
|
| | | m_total_risk_amount = 0;
|
| | | m_total_exposure_long = 0;
|
| | | m_total_exposure_short = 0;
|
| | | m_magic_number = 0;
|
| | | m_feature_cache = cache;
|
| | | m_regime_strategy = regime;
|
| | | m_exits_volatility = 0;
|
| | | m_exits_sl = 0;
|
| | | m_exits_tp = 0;
|
| | | m_exits_manual = 0;
|
| | |
|
| | | ArrayResize(m_positions, 0);
|
| | | ArrayResize(m_correlations, 0);
|
| | | m_vol_exit_config.SetDefaults();
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Calculate position size with correlation adjustment |
|
| | | //+------------------------------------------------------------------+
|
| | | double CalculatePositionSize(string symbol, int type, double entry_price,
|
| | | double sl_price, double risk_pct, double account_balance)
|
| | | {
|
| | | if(entry_price == 0 || sl_price == 0) return 0;
|
| | |
|
| | | // Base risk amount
|
| | | double risk_amount = account_balance * risk_pct / 100.0;
|
| | |
|
| | | // Calculate distance to stop
|
| | | double sl_distance = MathAbs(entry_price - sl_price);
|
| | | if(sl_distance == 0) return 0;
|
| | |
|
| | | // Get tick value
|
| | | double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
|
| | | double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
|
| | | if(tick_value == 0 || tick_size == 0) return 0;
|
| | |
|
| | | // Base position size
|
| | | double point_value = tick_value / tick_size;
|
| | | double base_volume = risk_amount / (sl_distance * point_value);
|
| | |
|
| | | // Apply correlation factor
|
| | | double corr_factor = CalculateCorrelationFactor(symbol, type);
|
| | | double adjusted_volume = base_volume * corr_factor;
|
| | |
|
| | | // Apply regime adjustment if available
|
| | | if(m_regime_strategy != NULL)
|
| | | {
|
| | | adjusted_volume = m_regime_strategy.AdjustPositionSize(adjusted_volume);
|
| | | }
|
| | |
|
2026-10-28 00:30:38 -04:00 | | | // Preserve a regime veto before applying broker minimum volume.
|
| | | if(!MathIsValidNumber(adjusted_volume) || adjusted_volume <= 0.0) return 0.0;
|
| | |
|
2026-02-24 12:47:37 -05:00 | | | // Normalize to lot step
|
| | | double lot_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
|
| | | double min_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
|
| | | double max_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
|
| | |
|
| | | adjusted_volume = MathFloor(adjusted_volume / lot_step) * lot_step;
|
| | | adjusted_volume = MathMax(min_lot, MathMin(max_lot, adjusted_volume));
|
| | |
|
| | | return adjusted_volume;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Calculate correlation factor for position sizing |
|
| | | //+------------------------------------------------------------------+
|
| | | double CalculateCorrelationFactor(string symbol, int type)
|
| | | {
|
| | | double total_correlation_exposure = 0;
|
| | | int related_positions = 0;
|
| | |
|
| | | // Sum correlation with existing positions
|
| | | for(int i = 0; i < m_position_count; i++)
|
| | | {
|
| | | if(m_positions[i].symbol == symbol) continue;
|
| | |
|
| | | double corr = GetCorrelation(symbol, m_positions[i].symbol);
|
| | |
|
| | | // Check direction alignment
|
| | | bool same_direction = (type == m_positions[i].type);
|
| | |
|
| | | if(same_direction)
|
| | | {
|
| | | // Adding to same-side exposure
|
| | | if(corr > 0.7) // High positive correlation
|
| | | {
|
| | | total_correlation_exposure += corr;
|
| | | related_positions++;
|
| | | }
|
| | | }
|
| | | else
|
| | | {
|
| | | // Opposite direction
|
| | | if(corr < -0.7) // High negative correlation (hedge)
|
| | | {
|
| | | total_correlation_exposure += MathAbs(corr) * 0.5; // Reduce size slightly for hedges
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | // Calculate factor
|
| | | double factor = 1.0;
|
| | |
|
| | | if(related_positions > 0)
|
| | | {
|
| | | // Reduce size based on correlation exposure
|
| | | double avg_correlation = total_correlation_exposure / related_positions;
|
| | | factor = 1.0 / (1.0 + avg_correlation);
|
| | |
|
| | | // Ensure minimum size
|
| | | factor = MathMax(0.5, factor);
|
| | | }
|
| | |
|
| | | return factor;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Get correlation between two symbols |
|
| | | //+------------------------------------------------------------------+
|
| | | double GetCorrelation(string sym1, string sym2)
|
| | | {
|
| | | // Check cache first
|
| | | for(int i = 0; i < m_correlation_count; i++)
|
| | | {
|
| | | if((m_correlations[i].symbol1 == sym1 && m_correlations[i].symbol2 == sym2) ||
|
| | | (m_correlations[i].symbol1 == sym2 && m_correlations[i].symbol2 == sym1))
|
| | | {
|
| | | // Check if stale (older than 1 hour)
|
| | | if(TimeCurrent() - m_correlations[i].last_updated < 3600)
|
| | | {
|
| | | return m_correlations[i].correlation;
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | // Calculate correlation
|
| | | double corr = CalculateSymbolCorrelation(sym1, sym2);
|
| | |
|
| | | // Cache result
|
| | | bool found = false;
|
| | | for(int i = 0; i < m_correlation_count; i++)
|
| | | {
|
| | | if((m_correlations[i].symbol1 == sym1 && m_correlations[i].symbol2 == sym2) ||
|
| | | (m_correlations[i].symbol1 == sym2 && m_correlations[i].symbol2 == sym1))
|
| | | {
|
| | | m_correlations[i].correlation = corr;
|
| | | m_correlations[i].last_updated = TimeCurrent();
|
| | | found = true;
|
| | | break;
|
| | | }
|
| | | }
|
| | |
|
| | | if(!found)
|
| | | {
|
| | | int idx = m_correlation_count;
|
| | | ArrayResize(m_correlations, m_correlation_count + 1);
|
| | | m_correlations[idx].symbol1 = sym1;
|
| | | m_correlations[idx].symbol2 = sym2;
|
| | | m_correlations[idx].correlation = corr;
|
| | | m_correlations[idx].last_updated = TimeCurrent();
|
| | | m_correlation_count++;
|
| | | }
|
| | |
|
| | | return corr;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Calculate correlation from price history |
|
| | | //+------------------------------------------------------------------+
|
| | | double CalculateSymbolCorrelation(string sym1, string sym2, int bars = 50)
|
| | | {
|
| | | double close1[], close2[];
|
| | | ArraySetAsSeries(close1, true);
|
| | | ArraySetAsSeries(close2, true);
|
| | |
|
| | | if(CopyClose(sym1, PERIOD_CURRENT, 0, bars, close1) != bars) return 0;
|
| | | if(CopyClose(sym2, PERIOD_CURRENT, 0, bars, close2) != bars) return 0;
|
| | |
|
| | | // Calculate means
|
| | | double mean1 = 0, mean2 = 0;
|
| | | for(int i = 0; i < bars; i++)
|
| | | {
|
| | | mean1 += close1[i];
|
| | | mean2 += close2[i];
|
| | | }
|
| | | mean1 /= bars;
|
| | | mean2 /= bars;
|
| | |
|
| | | // Calculate correlation
|
| | | double numerator = 0, denom1 = 0, denom2 = 0;
|
| | | for(int i = 0; i < bars; i++)
|
| | | {
|
| | | double diff1 = close1[i] - mean1;
|
| | | double diff2 = close2[i] - mean2;
|
| | | numerator += diff1 * diff2;
|
| | | denom1 += diff1 * diff1;
|
| | | denom2 += diff2 * diff2;
|
| | | }
|
| | |
|
| | | if(denom1 == 0 || denom2 == 0) return 0;
|
| | |
|
| | | return numerator / MathSqrt(denom1 * denom2);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Add position to tracking |
|
| | | //+------------------------------------------------------------------+
|
| | | void AddPosition(const SPosition &pos)
|
| | | {
|
| | | int idx = m_position_count;
|
| | | ArrayResize(m_positions, m_position_count + 1);
|
| | | m_positions[idx] = pos;
|
| | | m_position_count++;
|
| | |
|
| | | // Update risk tracking
|
| | | m_total_risk_amount += pos.risk_amount;
|
| | | if(pos.type == 0)
|
| | | m_total_exposure_long += pos.volume;
|
| | | else
|
| | | m_total_exposure_short += pos.volume;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Remove position from tracking |
|
| | | //+------------------------------------------------------------------+
|
| | | void RemovePosition(ulong ticket)
|
| | | {
|
| | | for(int i = 0; i < m_position_count; i++)
|
| | | {
|
| | | if(m_positions[i].ticket == ticket)
|
| | | {
|
| | | // Update risk tracking
|
| | | m_total_risk_amount -= m_positions[i].risk_amount;
|
| | | if(m_positions[i].type == 0)
|
| | | m_total_exposure_long -= m_positions[i].volume;
|
| | | else
|
| | | m_total_exposure_short -= m_positions[i].volume;
|
| | |
|
| | | // Remove from array
|
| | | if(i < m_position_count - 1)
|
| | | {
|
| | | m_positions[i] = m_positions[m_position_count - 1];
|
| | | }
|
| | | m_position_count--;
|
| | | ArrayResize(m_positions, m_position_count);
|
| | | break;
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Check volatility exits for all positions |
|
| | | //+------------------------------------------------------------------+
|
| | | void CheckVolatilityExits()
|
| | | {
|
| | | if(!m_vol_exit_config.enabled) return;
|
| | | if(m_feature_cache == NULL) return;
|
| | |
|
| | | double atr = m_feature_cache.GetATR14();
|
| | | if(atr == 0) return;
|
| | |
|
| | | for(int i = 0; i < m_position_count; i++)
|
| | | {
|
| | | int pos_type = m_positions[i].type;
|
| | |
|
| | | // Get current price
|
| | | double current_price = (pos_type == 0) ?
|
| | | SymbolInfoDouble(m_positions[i].symbol, SYMBOL_BID) :
|
| | | SymbolInfoDouble(m_positions[i].symbol, SYMBOL_ASK);
|
| | |
|
| | | // Calculate profit in ATR terms
|
| | | double profit_points = (pos_type == 0) ?
|
| | | current_price - m_positions[i].open_price :
|
| | | m_positions[i].open_price - current_price;
|
| | | double profit_atr = profit_points / atr;
|
| | |
|
| | | // Breakeven trigger
|
| | | if(profit_atr >= m_vol_exit_config.atr_breakeven_trigger &&
|
| | | m_positions[i].sl != m_positions[i].open_price)
|
| | | {
|
| | | // Move SL to breakeven
|
| | | ModifyStopLoss(m_positions[i].ticket, m_positions[i].open_price);
|
| | | m_positions[i].sl = m_positions[i].open_price;
|
| | | }
|
| | |
|
| | | // Trailing stop trigger
|
| | | if(profit_atr >= m_vol_exit_config.atr_trailing_start)
|
| | | {
|
| | | double new_sl = (pos_type == 0) ?
|
| | | current_price - m_vol_exit_config.atr_trailing_distance * atr :
|
| | | current_price + m_vol_exit_config.atr_trailing_distance * atr;
|
| | |
|
| | | // Only move stop in favorable direction
|
| | | if((pos_type == 0 && new_sl > m_positions[i].sl) ||
|
| | | (pos_type == 1 && new_sl < m_positions[i].sl))
|
| | | {
|
| | | ModifyStopLoss(m_positions[i].ticket, new_sl);
|
| | | m_positions[i].sl = new_sl;
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Modify stop loss for a position |
|
| | | //+------------------------------------------------------------------+
|
| | | bool ModifyStopLoss(ulong ticket, double new_sl)
|
| | | {
|
| | | // Select the position
|
| | | if(!PositionSelectByTicket(ticket))
|
| | | return false;
|
| | |
|
| | | // Get position details
|
| | | string symbol = PositionGetString(POSITION_SYMBOL);
|
| | | double tp = PositionGetDouble(POSITION_TP);
|
| | |
|
| | | // Prepare modification request
|
| | | MqlTradeRequest request;
|
| | | MqlTradeResult result;
|
| | | ZeroMemory(request);
|
| | | ZeroMemory(result);
|
| | |
|
| | | request.action = TRADE_ACTION_SLTP;
|
| | | request.position = ticket;
|
| | | request.symbol = symbol;
|
| | | request.sl = new_sl;
|
| | | request.tp = tp;
|
| | | request.comment = "SL Modified by PositionManager";
|
| | |
|
| | | // Send the order
|
| | | if(!OrderSend(request, result))
|
| | | {
|
| | | PrintFormat("[PositionManager] Failed to modify SL for ticket %I64u: error %d", ticket, GetLastError());
|
| | | return false;
|
| | | }
|
| | |
|
| | | if(result.retcode == TRADE_RETCODE_DONE)
|
| | | {
|
| | | PrintFormat("[PositionManager] SL modified for ticket %I64u: new SL=%.5f", ticket, new_sl);
|
| | | return true;
|
| | | }
|
| | |
|
| | | return false;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Check if can open new position |
|
| | | //+------------------------------------------------------------------+
|
| | | bool CanOpenPosition(string symbol, int type, double risk_amount, string &reason)
|
| | | {
|
| | | reason = "";
|
| | |
|
| | | // Adjust-only policy: never hard-block on caps/risk/exposure.
|
| | | // The execution layer will enforce min-lot and broker/margin constraints.
|
| | |
|
| | | // Check max positions
|
| | | if(m_position_count >= m_max_positions)
|
| | | {
|
| | | reason = "Max positions reached (soft): " + IntegerToString(m_position_count);
|
| | | }
|
| | |
|
| | | // Check total risk
|
| | | double bal = AccountInfoDouble(ACCOUNT_BALANCE);
|
| | | double max_total_risk_amt = m_max_total_risk_pct / 100.0 * bal;
|
| | | if(bal > 0.0 && (m_total_risk_amount + risk_amount) > max_total_risk_amt)
|
| | | {
|
| | | if(reason != "") reason += " | ";
|
| | | reason += "Total risk limit exceeded (soft)";
|
| | | }
|
| | |
|
| | | // Check exposure limits
|
| | | if(type == 0 && m_total_exposure_long >= m_max_positions * 0.7)
|
| | | {
|
| | | if(reason != "") reason += " | ";
|
| | | reason += "Long exposure limit reached (soft)";
|
| | | }
|
| | | if(type == 1 && m_total_exposure_short >= m_max_positions * 0.7)
|
| | | {
|
| | | if(reason != "") reason += " | ";
|
| | | reason += "Short exposure limit reached (soft)";
|
| | | }
|
| | |
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Sync with actual MT5 positions |
|
| | | //+------------------------------------------------------------------+
|
| | | void SyncPositions(ulong magic_filter = 0)
|
| | | {
|
| | | // Clear tracking
|
| | | m_position_count = 0;
|
| | | ArrayResize(m_positions, 0);
|
| | | m_total_risk_amount = 0;
|
| | | m_total_exposure_long = 0;
|
| | | m_total_exposure_short = 0;
|
| | |
|
| | | // Iterate through actual positions
|
| | | for(int i = PositionsTotal() - 1; i >= 0; i--)
|
| | | {
|
| | | ulong ticket = PositionGetTicket(i);
|
| | | if(ticket == 0) continue;
|
| | |
|
| | | // Filter by magic if specified
|
| | | if(magic_filter != 0 && PositionGetInteger(POSITION_MAGIC) != magic_filter)
|
| | | continue;
|
| | |
|
| | | SPosition pos;
|
| | | pos.ticket = ticket;
|
| | | pos.symbol = PositionGetString(POSITION_SYMBOL);
|
| | | pos.type = (int)PositionGetInteger(POSITION_TYPE);
|
| | | pos.volume = PositionGetDouble(POSITION_VOLUME);
|
| | | pos.open_price = PositionGetDouble(POSITION_PRICE_OPEN);
|
| | | pos.sl = PositionGetDouble(POSITION_SL);
|
| | | pos.tp = PositionGetDouble(POSITION_TP);
|
| | | pos.open_time = (datetime)PositionGetInteger(POSITION_TIME);
|
| | | pos.magic = IntegerToString((int)PositionGetInteger(POSITION_MAGIC));
|
| | |
|
| | | // Calculate risk
|
| | | double tick_value = SymbolInfoDouble(pos.symbol, SYMBOL_TRADE_TICK_VALUE);
|
| | | double tick_size = SymbolInfoDouble(pos.symbol, SYMBOL_TRADE_TICK_SIZE);
|
| | | double sl_distance = MathAbs(pos.open_price - pos.sl);
|
| | | pos.risk_amount = pos.volume * sl_distance * tick_value / tick_size;
|
| | |
|
| | | AddPosition(pos);
|
| | | }
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Get risk report |
|
| | | //+------------------------------------------------------------------+
|
| | | string GetRiskReport()
|
| | | {
|
| | | return StringFormat(
|
| | | "Positions: %d | Risk: $%.2f | Long: %.2f lots | Short: %.2f lots",
|
| | | m_position_count,
|
| | | m_total_risk_amount,
|
| | | m_total_exposure_long,
|
| | | m_total_exposure_short
|
| | | );
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Compute dynamic max open positions based on market conditions |
|
| | | //+------------------------------------------------------------------+
|
| | | int ComputeDynamicMaxOpenPositions(int current_cap)
|
| | | {
|
| | | // Base implementation: return current cap
|
| | | // Can be extended to adjust based on volatility, drawdown, etc.
|
| | | if(current_cap <= 0) return m_max_positions;
|
| | | return current_cap;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Set configuration |
|
| | | //+------------------------------------------------------------------+
|
| | | void SetMaxPositions(int max_pos) { m_max_positions = max_pos; }
|
| | | void SetMaxRiskPerTrade(double pct) { m_max_risk_per_trade_pct = pct; }
|
| | | void SetMaxTotalRisk(double pct) { m_max_total_risk_pct = pct; }
|
| | | void SetVolatilityExitConfig(const SVolatilityExitConfig &config) { m_vol_exit_config = config; }
|
| | | void SetMagicNumber(long magic) { m_magic_number = magic; }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Get portfolio correlation for a symbol |
|
| | | //+------------------------------------------------------------------+
|
| | | double GetPortfolioCorrelation(string symbol)
|
| | | {
|
| | | double avg_abs_corr = 0.0;
|
| | | if(m_position_count <= 0) return avg_abs_corr;
|
| | |
|
| | | // Calculate weighted average absolute correlation to existing positions
|
| | | double sum = 0.0, wsum = 0.0;
|
| | | for(int i = 0; i < m_position_count; i++)
|
| | | {
|
| | | string osym = m_positions[i].symbol;
|
| | | if(osym == "" || osym == symbol) continue;
|
| | |
|
| | | double vol = m_positions[i].volume;
|
| | | double corr = GetCorrelation(symbol, osym);
|
| | | sum += MathAbs(corr) * vol;
|
| | | wsum += vol;
|
| | | }
|
| | |
|
| | | avg_abs_corr = (wsum > 0.0 ? sum / wsum : 0.0);
|
| | | return avg_abs_corr;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Getters |
|
| | | //+------------------------------------------------------------------+
|
| | | int GetPositionCount() { return m_position_count; }
|
| | | double GetTotalRisk() { return m_total_risk_amount; }
|
| | | double GetLongExposure() { return m_total_exposure_long; }
|
| | | double GetShortExposure() { return m_total_exposure_short; }
|
| | | };
|
| | |
|
| | | // Global instance - extern declaration (defined in EA files)
|
| | | extern CPositionManager* g_position_manager;
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Initialize Position Manager |
|
| | | //+------------------------------------------------------------------+
|
| | | bool InitializePositionManager(CGateFeatureCache* cache = NULL,
|
| | | CRegimeAwareStrategy* regime = NULL)
|
| | | {
|
| | | if(g_position_manager != NULL)
|
| | | {
|
| | | delete g_position_manager;
|
| | | }
|
| | |
|
| | | g_position_manager = new CPositionManager(cache, regime);
|
| | | Print("[PositionManager] Initialized with correlation sizing and volatility exits");
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Shutdown Position Manager |
|
| | | //+------------------------------------------------------------------+
|
| | | void ShutdownPositionManager()
|
| | | {
|
| | | if(g_position_manager != NULL)
|
| | | {
|
| | | delete g_position_manager;
|
| | | g_position_manager = NULL;
|
| | | }
|
| | | }
|
| | |
|
| | | #endif // CPOSITIONMANAGER_MQH
|