mql5/Shared Projects/ERMT-ML/Modules-ML8x/ExecutionOptimizer.mqh
darashikoh 0fb1bd1b0a 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

963 lines
No EOL
32 KiB
MQL5

//+------------------------------------------------------------------+
//| 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