//+------------------------------------------------------------------+ //| ExecutionOptimizer.mqh | //| Smart Order Routing and Execution | //| Optimal Execution Algorithms & Strategies | //+------------------------------------------------------------------+ #ifndef EXECUTION_OPTIMIZER_MQH #define EXECUTION_OPTIMIZER_MQH #include "DataTypes_v71.mqh" #include //+------------------------------------------------------------------+ //| Execution Venue | //+------------------------------------------------------------------+ struct ExecutionVenue { string name; double liquidity_score; double avg_spread; double avg_latency_ms; double fill_rate; double price_improvement_rate; bool is_available; int priority; }; //+------------------------------------------------------------------+ //| Execution Schedule (for TWAP/VWAP) | //+------------------------------------------------------------------+ struct ExecutionSchedule { datetime start_time; datetime end_time; int slices; double slice_sizes[]; datetime slice_times[]; bool is_adaptive; double urgency_factor; }; //+------------------------------------------------------------------+ //| Execution Analytics | //+------------------------------------------------------------------+ struct ExecutionAnalytics { double implementation_shortfall; double arrival_price; double avg_fill_price; double vwap_tracking_error; double timing_risk; double market_impact; double opportunity_cost; int fills_count; double fill_rate; }; //+------------------------------------------------------------------+ //| Execution Optimizer Class | //+------------------------------------------------------------------+ class CExecutionOptimizer { private: //--- Configuration ENUM_EXECUTION_ALGO m_default_algo; double m_max_slippage_pips; double m_urgency_threshold; bool m_use_dark_pools; //--- Execution venues ExecutionVenue m_venues[]; int m_venue_count; //--- Active executions struct ActiveExecution { ulong order_id; ExecutionPlan plan; ExecutionSchedule schedule; double filled_volume; double avg_fill_price; datetime start_time; int retry_count; bool is_complete; }; ActiveExecution m_active_executions[]; //--- Analytics tracking ExecutionAnalytics m_analytics; double m_total_volume_executed; //--- Market impact model double m_impact_coefficient; double m_temporary_impact; double m_permanent_impact; //--- Helper methods double EstimateMarketImpact(string symbol, double volume, double urgency); double CalculateOptimalSliceSize(string symbol, double total_volume, double time_horizon); ExecutionVenue SelectBestVenue(string symbol, double volume); void UpdateVenueMetrics(ExecutionVenue &venue, double latency, bool success); double GetMarketVolatility(string symbol); double GetMarketDepth(string symbol, bool is_buy); //--- Execution algorithms bool ExecuteWithTWAP(ExecutionPlan &plan); bool ExecuteWithVWAP(ExecutionPlan &plan); bool ExecuteWithAdaptive(ExecutionPlan &plan); bool ExecuteWithSniper(ExecutionPlan &plan); bool ExecuteWithIceberg(ExecutionPlan &plan); public: CExecutionOptimizer(); ~CExecutionOptimizer(); //--- Initialization bool Initialize(ENUM_EXECUTION_ALGO default_algo, double max_slippage); void AddVenue(string name, double liquidity, double spread); //--- Execution planning ExecutionPlan CreatePlan(string symbol, ENUM_ORDER_TYPE direction, double volume, double urgency = 0.5); ExecutionSchedule CreateSchedule(ExecutionPlan &plan, int slices); //--- Smart execution bool ExecuteOrder(ExecutionPlan &plan); bool ExecuteSlice(ActiveExecution &execution); void ProcessActiveExecutions(); //--- Emergency operations bool ExecuteEmergencyClose(ulong ticket); void CancelAllPendingExecutions(); //--- Analytics double GetImplementationShortfall() { return m_analytics.implementation_shortfall; } double GetVWAPTrackingError() { return m_analytics.vwap_tracking_error; } double GetAverageFillPrice() { return m_analytics.avg_fill_price; } ExecutionAnalytics GetAnalytics() { return m_analytics; } //--- Optimization ENUM_EXECUTION_ALGO SelectOptimalAlgorithm(string symbol, double volume, double urgency, MarketConditionsV71 &conditions); double OptimizeSlicing(string symbol, double volume, double volatility); //--- Market impact double PredictMarketImpact(string symbol, double volume, double participation_rate); double GetOptimalParticipationRate(string symbol, double urgency); //--- Venue management void UpdateVenueAvailability(string venue_name, bool available); void RebalanceVenuePriorities(); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CExecutionOptimizer::CExecutionOptimizer() { m_default_algo = EXEC_ADAPTIVE; m_max_slippage_pips = 2.0; m_urgency_threshold = 0.7; m_use_dark_pools = true; m_venue_count = 0; m_total_volume_executed = 0; m_impact_coefficient = 0.1; m_temporary_impact = 0.5; m_permanent_impact = 0.3; //--- Initialize analytics m_analytics.implementation_shortfall = 0; m_analytics.arrival_price = 0; m_analytics.avg_fill_price = 0; m_analytics.vwap_tracking_error = 0; m_analytics.timing_risk = 0; m_analytics.market_impact = 0; m_analytics.opportunity_cost = 0; m_analytics.fills_count = 0; m_analytics.fill_rate = 0; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CExecutionOptimizer::~CExecutionOptimizer() { ArrayFree(m_venues); ArrayFree(m_active_executions); } //+------------------------------------------------------------------+ //| Initialize optimizer | //+------------------------------------------------------------------+ bool CExecutionOptimizer::Initialize(ENUM_EXECUTION_ALGO default_algo, double max_slippage) { m_default_algo = default_algo; m_max_slippage_pips = max_slippage; //--- Add default venues AddVenue("Primary", 0.8, 0.1); AddVenue("ECN", 0.9, 0.05); AddVenue("Dark", 0.6, 0.02); //--- Initialize active executions array ArrayResize(m_active_executions, 100); Print("ExecutionOptimizer initialized: Algorithm=", EnumToString(m_default_algo), ", MaxSlippage=", m_max_slippage_pips); return true; } //+------------------------------------------------------------------+ //| Add execution venue | //+------------------------------------------------------------------+ void CExecutionOptimizer::AddVenue(string name, double liquidity, double spread) { if(m_venue_count >= ArraySize(m_venues)) ArrayResize(m_venues, m_venue_count + 10); m_venues[m_venue_count].name = name; m_venues[m_venue_count].liquidity_score = liquidity; m_venues[m_venue_count].avg_spread = spread; m_venues[m_venue_count].avg_latency_ms = 10; // Default m_venues[m_venue_count].fill_rate = 0.95; m_venues[m_venue_count].price_improvement_rate = 0.3; m_venues[m_venue_count].is_available = true; m_venues[m_venue_count].priority = m_venue_count + 1; m_venue_count++; } //+------------------------------------------------------------------+ //| Create execution plan | //+------------------------------------------------------------------+ ExecutionPlan CExecutionOptimizer::CreatePlan(string symbol, ENUM_ORDER_TYPE direction, double volume, double urgency) { ExecutionPlan plan; plan.symbol = symbol; plan.direction = direction; plan.total_volume = volume; plan.urgency = urgency; //--- Determine execution algorithm if(urgency > m_urgency_threshold) { plan.algorithm = EXEC_SNIPER; // Aggressive for urgent orders } else if(volume > SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN) * 50) { plan.algorithm = EXEC_ICEBERG; // Hide large orders } else { plan.algorithm = m_default_algo; } //--- Calculate display volume for iceberg if(plan.algorithm == EXEC_ICEBERG) { plan.display_volume = volume * 0.2; // Show 20% plan.reserve_volume = volume * 0.8; } else { plan.display_volume = volume; plan.reserve_volume = 0; } //--- Set price limits double current_price = (direction == ORDER_TYPE_BUY) ? SymbolInfoDouble(symbol, SYMBOL_ASK) : SymbolInfoDouble(symbol, SYMBOL_BID); double slippage = m_max_slippage_pips * SymbolInfoDouble(symbol, SYMBOL_POINT) * 10; if(direction == ORDER_TYPE_BUY) { plan.limit_price = current_price + slippage; plan.stop_price = 0; } else { plan.limit_price = current_price - slippage; plan.stop_price = 0; } //--- Calculate time horizon based on urgency plan.time_horizon = (int)((1 - urgency) * 300 + 60); // 1-6 minutes //--- Dark pool usage plan.use_dark_pool = m_use_dark_pools && (volume > SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN) * 20); //--- Maximum spread plan.max_spread = SymbolInfoInteger(symbol, SYMBOL_SPREAD) * 2; return plan; } //+------------------------------------------------------------------+ //| Execute order with smart routing | //+------------------------------------------------------------------+ bool CExecutionOptimizer::ExecuteOrder(ExecutionPlan &plan) { //--- Route to appropriate algorithm switch(plan.algorithm) { case EXEC_MARKET: { CTrade trade; trade.SetDeviationInPoints((int)(m_max_slippage_pips * 10)); if(plan.direction == ORDER_TYPE_BUY) return trade.Buy(plan.total_volume, plan.symbol); else return trade.Sell(plan.total_volume, plan.symbol); } case EXEC_TWAP: return ExecuteWithTWAP(plan); case EXEC_VWAP: return ExecuteWithVWAP(plan); case EXEC_ADAPTIVE: return ExecuteWithAdaptive(plan); case EXEC_ICEBERG: return ExecuteWithIceberg(plan); case EXEC_SNIPER: return ExecuteWithSniper(plan); default: return false; } } //+------------------------------------------------------------------+ //| Execute with adaptive algorithm | //+------------------------------------------------------------------+ bool CExecutionOptimizer::ExecuteWithAdaptive(ExecutionPlan &plan) { //--- Analyze current market conditions double volatility = GetMarketVolatility(plan.symbol); double depth = GetMarketDepth(plan.symbol, plan.direction == ORDER_TYPE_BUY); //--- Create adaptive execution ActiveExecution exec; exec.order_id = 0; exec.plan = plan; exec.filled_volume = 0; exec.avg_fill_price = 0; exec.start_time = TimeCurrent(); exec.retry_count = 0; exec.is_complete = false; //--- Determine optimal slice size double optimal_slice = CalculateOptimalSliceSize(plan.symbol, plan.total_volume, plan.time_horizon); //--- Create adaptive schedule int slices = (int)(plan.total_volume / optimal_slice) + 1; exec.schedule = CreateSchedule(plan, slices); exec.schedule.is_adaptive = true; //--- Adjust for market conditions if(volatility > 0.02) // High volatility { exec.schedule.urgency_factor = MathMin(1.0, plan.urgency * 1.2); exec.schedule.slices = MathMax(3, slices / 2); // Fewer, larger slices } else if(depth < 10000) // Low liquidity { exec.schedule.urgency_factor = plan.urgency * 0.8; exec.schedule.slices = slices * 2; // More, smaller slices } //--- Store active execution for(int i = 0; i < ArraySize(m_active_executions); i++) { if(!m_active_executions[i].is_complete) { m_active_executions[i] = exec; break; } } //--- Execute first slice immediately return ExecuteSlice(exec); } //+------------------------------------------------------------------+ //| Execute with TWAP algorithm | //+------------------------------------------------------------------+ bool CExecutionOptimizer::ExecuteWithTWAP(ExecutionPlan &plan) { //--- Create TWAP execution ActiveExecution exec; exec.order_id = 0; exec.plan = plan; exec.filled_volume = 0; exec.avg_fill_price = 0; exec.start_time = TimeCurrent(); exec.retry_count = 0; exec.is_complete = false; //--- Create time-weighted schedule int slices = MathMax(5, plan.time_horizon / 30); // One slice per 30 seconds exec.schedule = CreateSchedule(plan, slices); //--- Equal-sized slices for TWAP double slice_size = plan.total_volume / slices; ArrayResize(exec.schedule.slice_sizes, slices); ArrayResize(exec.schedule.slice_times, slices); for(int i = 0; i < slices; i++) { exec.schedule.slice_sizes[i] = slice_size; exec.schedule.slice_times[i] = exec.schedule.start_time + (i * plan.time_horizon / slices); } //--- Store and execute for(int i = 0; i < ArraySize(m_active_executions); i++) { if(!m_active_executions[i].is_complete) { m_active_executions[i] = exec; break; } } return ExecuteSlice(exec); } //+------------------------------------------------------------------+ //| Execute with VWAP algorithm | //+------------------------------------------------------------------+ bool CExecutionOptimizer::ExecuteWithVWAP(ExecutionPlan &plan) { //--- Get historical volume profile long volumes[24]; datetime current = TimeCurrent(); MqlDateTime time_struct; TimeToStruct(current, time_struct); //--- Estimate intraday volume distribution double total_hist_volume = 0; for(int i = 0; i < 24; i++) { // Simplified U-shaped volume profile if(i >= 8 && i <= 16) // Market hours { volumes[i] = (long)(100 * (1 + MathSin((i - 12) * M_PI / 8))); } else { volumes[i] = 10; } total_hist_volume += volumes[i]; } //--- Create VWAP execution ActiveExecution exec; exec.order_id = 0; exec.plan = plan; exec.filled_volume = 0; exec.avg_fill_price = 0; exec.start_time = TimeCurrent(); exec.retry_count = 0; exec.is_complete = false; //--- Create volume-weighted schedule int slices = MathMin(plan.time_horizon / 60, 10); // Max 10 slices exec.schedule = CreateSchedule(plan, slices); ArrayResize(exec.schedule.slice_sizes, slices); ArrayResize(exec.schedule.slice_times, slices); //--- Distribute volume according to historical pattern for(int i = 0; i < slices; i++) { int hour = (time_struct.hour + i * plan.time_horizon / slices / 3600) % 24; double weight = volumes[hour] / total_hist_volume; exec.schedule.slice_sizes[i] = plan.total_volume * weight / slices; exec.schedule.slice_times[i] = exec.schedule.start_time + (i * plan.time_horizon / slices); } //--- Store and execute for(int i = 0; i < ArraySize(m_active_executions); i++) { if(!m_active_executions[i].is_complete) { m_active_executions[i] = exec; break; } } return ExecuteSlice(exec); } //+------------------------------------------------------------------+ //| Execute with iceberg algorithm | //+------------------------------------------------------------------+ bool CExecutionOptimizer::ExecuteWithIceberg(ExecutionPlan &plan) { //--- Create iceberg execution ActiveExecution exec; exec.order_id = 0; exec.plan = plan; exec.filled_volume = 0; exec.avg_fill_price = 0; exec.start_time = TimeCurrent(); exec.retry_count = 0; exec.is_complete = false; //--- Calculate visible and hidden portions double visible_size = plan.display_volume; int total_slices = (int)(plan.total_volume / visible_size) + 1; exec.schedule = CreateSchedule(plan, total_slices); exec.schedule.is_adaptive = true; // Adaptive timing between slices //--- Store and execute first visible slice for(int i = 0; i < ArraySize(m_active_executions); i++) { if(!m_active_executions[i].is_complete) { m_active_executions[i] = exec; break; } } return ExecuteSlice(exec); } //+------------------------------------------------------------------+ //| Execute with sniper algorithm (aggressive) | //+------------------------------------------------------------------+ bool CExecutionOptimizer::ExecuteWithSniper(ExecutionPlan &plan) { //--- Sniper executes immediately with wide slippage tolerance CTrade trade; trade.SetDeviationInPoints((int)(m_max_slippage_pips * 20)); // 10x normal slippage trade.SetTypeFilling(ORDER_FILLING_IOC); // Immediate or cancel //--- Try to execute full size bool result = false; if(plan.direction == ORDER_TYPE_BUY) { result = trade.Buy(plan.total_volume, plan.symbol); } else { result = trade.Sell(plan.total_volume, plan.symbol); } //--- If partial fill, try again with remaining if(result && trade.ResultVolume() < plan.total_volume) { double remaining = plan.total_volume - trade.ResultVolume(); //--- Second attempt if(plan.direction == ORDER_TYPE_BUY) trade.Buy(remaining, plan.symbol); else trade.Sell(remaining, plan.symbol); } //--- Update analytics if(result) { m_analytics.fills_count++; m_analytics.avg_fill_price = trade.ResultPrice(); m_total_volume_executed += trade.ResultVolume(); } return result; } //+------------------------------------------------------------------+ //| Execute a single slice | //+------------------------------------------------------------------+ bool CExecutionOptimizer::ExecuteSlice(ActiveExecution &execution) { //--- Check if execution is complete if(execution.filled_volume >= execution.plan.total_volume * 0.99) { execution.is_complete = true; return true; } //--- Select best venue ExecutionVenue venue = SelectBestVenue(execution.plan.symbol, execution.plan.total_volume - execution.filled_volume); //--- Calculate slice size double slice_size = execution.plan.display_volume; if(execution.schedule.slices > 0 && execution.schedule.slice_sizes != NULL) { int current_slice = (int)(execution.filled_volume / (execution.plan.total_volume / execution.schedule.slices)); if(current_slice < ArraySize(execution.schedule.slice_sizes)) slice_size = execution.schedule.slice_sizes[current_slice]; } //--- Ensure we don't over-execute slice_size = MathMin(slice_size, execution.plan.total_volume - execution.filled_volume); //--- Normalize to lot step double lot_step = SymbolInfoDouble(execution.plan.symbol, SYMBOL_VOLUME_STEP); slice_size = MathRound(slice_size / lot_step) * lot_step; //--- Execute slice CTrade trade; trade.SetDeviationInPoints((int)(m_max_slippage_pips * 10)); ulong start_time = GetMicrosecondCount(); bool result = false; if(execution.plan.direction == ORDER_TYPE_BUY) result = trade.Buy(slice_size, execution.plan.symbol); else result = trade.Sell(slice_size, execution.plan.symbol); ulong execution_time = GetMicrosecondCount() - start_time; //--- Update execution tracking if(result) { double fill_price = trade.ResultPrice(); double fill_volume = trade.ResultVolume(); //--- Update average fill price execution.avg_fill_price = (execution.avg_fill_price * execution.filled_volume + fill_price * fill_volume) / (execution.filled_volume + fill_volume); execution.filled_volume += fill_volume; //--- Update venue metrics UpdateVenueMetrics(venue, execution_time / 1000.0, true); //--- Update analytics m_analytics.fills_count++; m_total_volume_executed += fill_volume; } else { execution.retry_count++; UpdateVenueMetrics(venue, execution_time / 1000.0, false); } return result; } //+------------------------------------------------------------------+ //| Select best execution venue | //+------------------------------------------------------------------+ ExecutionVenue CExecutionOptimizer::SelectBestVenue(string symbol, double volume) { //--- Score each venue double best_score = -1; int best_index = 0; for(int i = 0; i < m_venue_count; i++) { if(!m_venues[i].is_available) continue; //--- Calculate venue score double score = 0; //--- Liquidity component (40%) score += m_venues[i].liquidity_score * 0.4; //--- Spread component (30%) double spread_score = 1.0 - m_venues[i].avg_spread; score += spread_score * 0.3; //--- Latency component (20%) double latency_score = 1.0 - (m_venues[i].avg_latency_ms / 100.0); score += MathMax(0, latency_score) * 0.2; //--- Fill rate component (10%) score += m_venues[i].fill_rate * 0.1; //--- Adjust for volume if(volume > 10000) // Large order { score *= m_venues[i].liquidity_score; // Favor high liquidity venues } if(score > best_score) { best_score = score; best_index = i; } } return m_venues[best_index]; } //+------------------------------------------------------------------+ //| Update venue metrics after execution | //+------------------------------------------------------------------+ void CExecutionOptimizer::UpdateVenueMetrics(ExecutionVenue &venue, double latency, bool success) { //--- Update latency with exponential moving average venue.avg_latency_ms = venue.avg_latency_ms * 0.9 + latency * 0.1; //--- Update fill rate venue.fill_rate = venue.fill_rate * 0.95 + (success ? 1.0 : 0.0) * 0.05; //--- Adjust priority based on performance if(venue.fill_rate < 0.8) venue.priority = MathMin(10, venue.priority + 1); else if(venue.fill_rate > 0.95) venue.priority = MathMax(1, venue.priority - 1); } //+------------------------------------------------------------------+ //| Calculate optimal slice size | //+------------------------------------------------------------------+ double CExecutionOptimizer::CalculateOptimalSliceSize(string symbol, double total_volume, double time_horizon) { //--- Get market metrics double avg_volume = 0; long volume_array[]; if(CopyTickVolume(symbol, PERIOD_M1, 0, 60, volume_array) > 0) { for(int i = 0; i < ArraySize(volume_array); i++) avg_volume += volume_array[i]; avg_volume /= ArraySize(volume_array); } //--- Target participation rate (% of market volume) double target_participation = 0.1; // 10% default //--- Adjust for urgency if(time_horizon < 120) // Less than 2 minutes target_participation = 0.2; // More aggressive else if(time_horizon > 600) // More than 10 minutes target_participation = 0.05; // More passive //--- Calculate slice size double slices_per_minute = 60.0 / (time_horizon / (time_horizon / 60)); double slice_size = avg_volume * target_participation / slices_per_minute; //--- Apply bounds double min_size = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); double max_size = total_volume / 3; // At least 3 slices slice_size = MathMax(min_size, MathMin(max_size, slice_size)); //--- Normalize to lot step double lot_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); slice_size = MathRound(slice_size / lot_step) * lot_step; return slice_size; } //+------------------------------------------------------------------+ //| Create execution schedule | //+------------------------------------------------------------------+ ExecutionSchedule CExecutionOptimizer::CreateSchedule(ExecutionPlan &plan, int slices) { ExecutionSchedule schedule; schedule.start_time = TimeCurrent(); schedule.end_time = schedule.start_time + plan.time_horizon; schedule.slices = slices; schedule.is_adaptive = false; schedule.urgency_factor = plan.urgency; //--- Initialize slice arrays ArrayResize(schedule.slice_sizes, slices); ArrayResize(schedule.slice_times, slices); //--- Default equal distribution double slice_size = plan.total_volume / slices; int time_interval = plan.time_horizon / slices; for(int i = 0; i < slices; i++) { schedule.slice_sizes[i] = slice_size; schedule.slice_times[i] = schedule.start_time + i * time_interval; } return schedule; } //+------------------------------------------------------------------+ //| Get market volatility | //+------------------------------------------------------------------+ double CExecutionOptimizer::GetMarketVolatility(string symbol) { //--- Calculate realized volatility double returns[]; ArrayResize(returns, 20); for(int i = 1; i <= 20; i++) { double close1 = iClose(symbol, PERIOD_M1, i); double close2 = iClose(symbol, PERIOD_M1, i-1); if(close2 > 0) returns[i-1] = (close1 - close2) / close2; } //--- Calculate standard deviation double mean = 0; for(int i = 0; i < 20; i++) mean += returns[i]; mean /= 20; double variance = 0; for(int i = 0; i < 20; i++) variance += MathPow(returns[i] - mean, 2); variance /= 20; return MathSqrt(variance); } //+------------------------------------------------------------------+ //| Get market depth | //+------------------------------------------------------------------+ double CExecutionOptimizer::GetMarketDepth(string symbol, bool is_buy) { //--- Simplified depth calculation from recent volume long volumes[]; if(CopyTickVolume(symbol, PERIOD_M1, 0, 10, volumes) > 0) { double total_volume = 0; for(int i = 0; i < ArraySize(volumes); i++) total_volume += volumes[i]; return total_volume / ArraySize(volumes); } return 1000; // Default depth } //+------------------------------------------------------------------+ //| Process active executions | //+------------------------------------------------------------------+ void CExecutionOptimizer::ProcessActiveExecutions() { datetime current_time = TimeCurrent(); for(int i = 0; i < ArraySize(m_active_executions); i++) { if(m_active_executions[i].is_complete || m_active_executions[i].plan.symbol == "") continue; //--- Check if it's time for next slice bool execute_now = false; if(m_active_executions[i].schedule.is_adaptive) { //--- Adaptive timing based on market conditions double filled_pct = m_active_executions[i].filled_volume / m_active_executions[i].plan.total_volume; double time_pct = (double)(current_time - m_active_executions[i].start_time) / m_active_executions[i].plan.time_horizon; if(filled_pct < time_pct - 0.1) // Behind schedule execute_now = true; } else { //--- Fixed schedule int current_slice = (int)(m_active_executions[i].filled_volume / (m_active_executions[i].plan.total_volume / m_active_executions[i].schedule.slices)); if(current_slice < m_active_executions[i].schedule.slices && current_time >= m_active_executions[i].schedule.slice_times[current_slice]) { execute_now = true; } } //--- Execute if needed if(execute_now) { ExecuteSlice(m_active_executions[i]); } //--- Check for timeout if(current_time > m_active_executions[i].schedule.end_time) { //--- Execute remaining volume urgently if(m_active_executions[i].filled_volume < m_active_executions[i].plan.total_volume) { m_active_executions[i].plan.urgency = 1.0; // Maximum urgency ExecuteSlice(m_active_executions[i]); } m_active_executions[i].is_complete = true; } } } //+------------------------------------------------------------------+ //| Predict market impact | //+------------------------------------------------------------------+ double CExecutionOptimizer::PredictMarketImpact(string symbol, double volume, double participation_rate) { //--- Linear impact model: Impact = α * Volume^β * Volatility^γ * ParticipationRate^δ double volatility = GetMarketVolatility(symbol); double avg_daily_volume = GetMarketDepth(symbol, true) * 60 * 8; // Rough estimate double volume_ratio = volume / avg_daily_volume; //--- Temporary impact double temp_impact = m_temporary_impact * MathSqrt(volume_ratio) * volatility * MathSqrt(participation_rate); //--- Permanent impact double perm_impact = m_permanent_impact * volume_ratio * participation_rate; return (temp_impact + perm_impact) * 10000; // Convert to basis points } //+------------------------------------------------------------------+ //| Select optimal algorithm based on conditions | //+------------------------------------------------------------------+ ENUM_EXECUTION_ALGO CExecutionOptimizer::SelectOptimalAlgorithm(string symbol, double volume, double urgency, MarketConditionsV71 &conditions) { //--- High urgency if(urgency > 0.8) return EXEC_SNIPER; //--- Large order in low liquidity if(volume > GetMarketDepth(symbol, true) * 0.1 && conditions.liquidity_depth < 0.5) return EXEC_ICEBERG; //--- Volatile market if(conditions.volatility > 0.02) return EXEC_ADAPTIVE; //--- Trending market if(conditions.regime == REGIME_TRENDING_UP || conditions.regime == REGIME_TRENDING_DOWN) return EXEC_TWAP; // Spread risk over time //--- Normal conditions return EXEC_VWAP; } //+------------------------------------------------------------------+ //| Execute emergency close | //+------------------------------------------------------------------+ bool CExecutionOptimizer::ExecuteEmergencyClose(ulong ticket) { //--- Use most aggressive settings CTrade trade; trade.SetDeviationInPoints(1000); // 100 pips slippage trade.SetTypeFilling(ORDER_FILLING_IOC); return trade.PositionClose(ticket); } #endif // EXECUTION_OPTIMIZER_MQH