mql5/Shared Projects/ERMT-ML/Modules-ML8x/ExecutionOptimizer.mqh

963 lines
32 KiB
MQL5
Raw Permalink Normal View History

Module Integration Summary for External Trade Management Overview To fully integrate the enhanced external trade management system, updates are required to 5 out of 7 existing modules. The updates maintain backward compatibility while adding new functionality for external trade handling. Module Update Requirements 🟢 No Updates Required (2 modules) TechnicalAnalysis.mqh - Already provides necessary calculations EntrySystem.mqh - Only handles EA's own entry signals 🟡 Minor Updates (2 modules) DataTypes.mqh - Add external trade structures and fields Utilities.mqh - Enhanced logging for external trades 🟠 Moderate Updates (3 modules) RiskManager.mqh - Enhanced risk enforcement methods TradeManager.mqh - Improved stop management for externals Dashboard.mqh - Display external trade information Integration Steps Phase 1: Data Structures (DataTypes.mqh) Add ENUM_EXTERNAL_STATUS enumeration Extend ManagedTrade structure with external-specific fields Add ExternalTradeStats structure for metrics Update DashboardConfig with show_external flag Key additions: external_status - Track state of external trade source_name - Identify where trade came from stops_modified - Track if we modified the trade original_sl/tp - Store original values for comparison Phase 2: Risk Management (RiskManager.mqh) Add EnforceRiskRulesEnhanced() method Implement GetExternalExposure() for risk aggregation Add UpdateExternalStats() for tracking Enhance ValidateAndAdjustRiskExternal() method Key features: Separate risk calculation for external trades Cache mechanism for performance Statistical tracking of external positions Smart risk adjustment without closing trades Phase 3: Trade Management (TradeManager.mqh) Add ApplyDefaultStopsEnhanced() with better logic Implement OverrideExternalStops() with smart override Create ManageExternalTrade() with different rules Add ApplyBreakevenExternal() with wider triggers Key features: Smart stop override (only improve, never worsen) Different management rules for external trades Respect minimum broker distances Track modification success/failure rates Phase 4: User Interface (Dashboard.mqh) Add CreateExternalSection() for display area Implement UpdateExternalSection() for real-time updates Add SetCustomText() for flexible display Create ShowExternalTrades() toggle method Key features: Real-time external trade count and risk Color-coded risk warnings List of active external positions Modification statistics display Phase 5: Logging (Utilities.mqh) Add LogExternalTrade() for detailed event logging Create separate CSV log for external trades Enhance GenerateReportEnhanced() with external section Add IdentifyTradeSource() for magic number interpretation Key features: Separate CSV log for external trade events Detailed tracking of all modifications Source identification from magic numbers Enhanced reporting with external statistics
2025-08-27 14:21:02 +01:00
//+------------------------------------------------------------------+
//| ExecutionOptimizer.mqh |
//| Smart Order Routing and Execution |
//| Optimal Execution Algorithms & Strategies |
//+------------------------------------------------------------------+
#ifndef EXECUTION_OPTIMIZER_MQH
#define EXECUTION_OPTIMIZER_MQH
#include "DataTypes_v71.mqh"
#include <Trade/Trade.mqh>
//+------------------------------------------------------------------+
//| 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