658 lines
23 KiB
MQL5
658 lines
23 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| 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);
| |||
}
| |||
| |||
// 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
|