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
766 lines
No EOL
27 KiB
MQL5
766 lines
No EOL
27 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| TradeManager_v71.mqh |
|
|
//| Smart Trade Execution Module v7.1 |
|
|
//| Iceberg Orders, TWAP/VWAP, Execution Optimization |
|
|
//+------------------------------------------------------------------+
|
|
#ifndef TRADE_MANAGER_V71_MQH
|
|
#define TRADE_MANAGER_V71_MQH
|
|
|
|
#include "DataTypes_v71.mqh"
|
|
#include <Trade/Trade.mqh>
|
|
#include <Trade/SymbolInfo.mqh>
|
|
#include <Trade/PositionInfo.mqh>
|
|
#include <Trade/OrderInfo.mqh>
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Trade Manager Class - Institutional Execution |
|
|
//+------------------------------------------------------------------+
|
|
class CTradeManagerV71
|
|
{
|
|
private:
|
|
//--- Configuration
|
|
TradeManagerConfigV71 m_config;
|
|
|
|
//--- Trade operations
|
|
CTrade m_trade;
|
|
CPositionInfo m_position;
|
|
COrderInfo m_order;
|
|
|
|
//--- Execution tracking
|
|
ExecutionStats m_exec_stats;
|
|
datetime m_last_execution;
|
|
|
|
//--- Iceberg order management
|
|
struct IcebergOrder
|
|
{
|
|
ulong parent_ticket;
|
|
string symbol;
|
|
ENUM_ORDER_TYPE type;
|
|
double total_volume;
|
|
double displayed_volume;
|
|
double filled_volume;
|
|
double price_limit;
|
|
datetime expiry;
|
|
bool is_active;
|
|
};
|
|
IcebergOrder m_iceberg_orders[];
|
|
|
|
//--- TWAP/VWAP tracking
|
|
struct TWAPOrder
|
|
{
|
|
string symbol;
|
|
ENUM_ORDER_TYPE type;
|
|
double total_volume;
|
|
double filled_volume;
|
|
datetime start_time;
|
|
datetime end_time;
|
|
int slices;
|
|
double last_slice_time;
|
|
};
|
|
TWAPOrder m_twap_orders[];
|
|
|
|
//--- Smart routing cache
|
|
struct LiquidityProvider
|
|
{
|
|
string name;
|
|
double spread;
|
|
double depth;
|
|
double last_latency;
|
|
int priority;
|
|
};
|
|
LiquidityProvider m_liquidity_providers[];
|
|
|
|
//--- Helper methods
|
|
bool ExecuteMarketOrder(string symbol, ENUM_ORDER_TYPE type, double volume);
|
|
bool ExecuteLimitOrder(string symbol, ENUM_ORDER_TYPE type, double volume, double price);
|
|
bool ExecuteIcebergSlice(IcebergOrder &order);
|
|
bool ExecuteTWAPSlice(TWAPOrder &order);
|
|
double CalculateOptimalSliceSize(string symbol, double total_volume);
|
|
double GetBestExecutionPrice(string symbol, ENUM_ORDER_TYPE type, double volume);
|
|
void UpdateExecutionMetrics(double slippage, ulong latency);
|
|
|
|
public:
|
|
CTradeManagerV71();
|
|
~CTradeManagerV71();
|
|
|
|
//--- Initialization
|
|
bool Initialize(const TradeManagerConfigV71 &config);
|
|
|
|
//--- Standard trade operations
|
|
bool OpenPosition(string symbol, ENUM_ORDER_TYPE type, double volume,
|
|
double sl = 0, double tp = 0, string comment = "");
|
|
bool ClosePosition(ulong ticket);
|
|
bool ClosePositionOptimized(ulong ticket);
|
|
bool ModifyPosition(ulong ticket, double sl, double tp);
|
|
|
|
//--- Smart execution methods
|
|
bool ExecuteSmartOrder(ExecutionPlan &plan);
|
|
bool ExecuteAdaptiveOrder(string symbol, ENUM_ORDER_TYPE type, double volume);
|
|
bool ExecuteTWAPOrder(string symbol, ENUM_ORDER_TYPE type, double volume, int minutes);
|
|
bool ExecuteVWAPOrder(string symbol, ENUM_ORDER_TYPE type, double volume);
|
|
bool ExecuteIcebergOrder(string symbol, ENUM_ORDER_TYPE type,
|
|
double total_volume, double display_percent);
|
|
|
|
//--- Position management
|
|
void ManageTrade(ManagedTradeV71 &trade);
|
|
void ManageTradeOptimized(ManagedTradeV71 &trade, SymbolDataV71 &symbol_data);
|
|
bool ApplyBreakeven(ManagedTradeV71 &trade);
|
|
bool ApplyTrailingStop(ManagedTradeV71 &trade);
|
|
bool ApplyPartialClose(ManagedTradeV71 &trade);
|
|
|
|
//--- Advanced operations
|
|
bool PartialCloseOptimized(ulong ticket, double volume);
|
|
bool AddToPositionOptimized(ManagedTradeV71 &trade, double add_volume);
|
|
void ProcessPendingOrders();
|
|
void ProcessIcebergOrders();
|
|
void ProcessTWAPOrders();
|
|
|
|
//--- Emergency operations
|
|
bool ExecuteEmergencyClose(ulong ticket);
|
|
void CloseAllPositions();
|
|
void CancelAllPendingOrders();
|
|
|
|
//--- Execution quality
|
|
double GetLastExecutionSlippage() { return m_exec_stats.last_slippage; }
|
|
ExecutionStats GetExecutionStats() { return m_exec_stats; }
|
|
double CalculateSlippage(string symbol, ENUM_ORDER_TYPE type,
|
|
double expected_price, double fill_price);
|
|
|
|
//--- Setters
|
|
void SetSlippageLimit(double pips) { m_config.max_slippage = pips; }
|
|
void SetExecutionAlgorithm(ENUM_EXECUTION_ALGO algo) { m_config.execution_algo = algo; }
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor |
|
|
//+------------------------------------------------------------------+
|
|
CTradeManagerV71::CTradeManagerV71()
|
|
{
|
|
m_last_execution = 0;
|
|
|
|
//--- Initialize execution stats
|
|
m_exec_stats.last_slippage = 0;
|
|
m_exec_stats.last_fill_time = 0;
|
|
m_exec_stats.avg_slippage = 0;
|
|
m_exec_stats.positive_slippage_rate = 0;
|
|
m_exec_stats.total_fills = 0;
|
|
m_exec_stats.total_rejected = 0;
|
|
m_exec_stats.improvement_amount = 0;
|
|
m_exec_stats.vwap_tracking_error = 0;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTradeManagerV71::~CTradeManagerV71()
|
|
{
|
|
ArrayFree(m_iceberg_orders);
|
|
ArrayFree(m_twap_orders);
|
|
ArrayFree(m_liquidity_providers);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize trade manager |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeManagerV71::Initialize(const TradeManagerConfigV71 &config)
|
|
{
|
|
m_config = config;
|
|
|
|
//--- Setup trade object
|
|
m_trade.SetExpertMagicNumber(m_config.magic_number);
|
|
m_trade.SetDeviationInPoints((int)(m_config.max_slippage * 10));
|
|
m_trade.SetTypeFilling(ORDER_FILLING_IOC);
|
|
|
|
//--- Initialize liquidity providers (simplified)
|
|
ArrayResize(m_liquidity_providers, 3);
|
|
m_liquidity_providers[0].name = "Primary";
|
|
m_liquidity_providers[0].priority = 1;
|
|
m_liquidity_providers[1].name = "ECN";
|
|
m_liquidity_providers[1].priority = 2;
|
|
m_liquidity_providers[2].name = "Dark";
|
|
m_liquidity_providers[2].priority = 3;
|
|
|
|
Print("TradeManagerV71 initialized with smart execution");
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Execute smart order based on plan |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeManagerV71::ExecuteSmartOrder(ExecutionPlan &plan)
|
|
{
|
|
bool result = false;
|
|
ulong start_time = GetMicrosecondCount();
|
|
|
|
switch(plan.algorithm)
|
|
{
|
|
case EXEC_MARKET:
|
|
result = ExecuteMarketOrder(plan.symbol, plan.direction, plan.total_volume);
|
|
break;
|
|
|
|
case EXEC_LIMIT:
|
|
result = ExecuteLimitOrder(plan.symbol, plan.direction, plan.total_volume, plan.limit_price);
|
|
break;
|
|
|
|
case EXEC_ADAPTIVE:
|
|
result = ExecuteAdaptiveOrder(plan.symbol, plan.direction, plan.total_volume);
|
|
break;
|
|
|
|
case EXEC_TWAP:
|
|
result = ExecuteTWAPOrder(plan.symbol, plan.direction, plan.total_volume, plan.time_horizon / 60);
|
|
break;
|
|
|
|
case EXEC_VWAP:
|
|
result = ExecuteVWAPOrder(plan.symbol, plan.direction, plan.total_volume);
|
|
break;
|
|
|
|
case EXEC_ICEBERG:
|
|
{
|
|
double display_percent = plan.display_volume / plan.total_volume * 100;
|
|
result = ExecuteIcebergOrder(plan.symbol, plan.direction, plan.total_volume, display_percent);
|
|
}
|
|
break;
|
|
|
|
case EXEC_SNIPER:
|
|
//--- Aggressive fill at best available price
|
|
m_trade.SetDeviationInPoints(100); // Wider slippage for aggressive fill
|
|
result = ExecuteMarketOrder(plan.symbol, plan.direction, plan.total_volume);
|
|
m_trade.SetDeviationInPoints((int)(m_config.max_slippage * 10)); // Reset
|
|
break;
|
|
}
|
|
|
|
//--- Update execution metrics
|
|
if(result)
|
|
{
|
|
ulong execution_time = GetMicrosecondCount() - start_time;
|
|
m_exec_stats.last_fill_time = execution_time / 1000.0; // Convert to ms
|
|
m_exec_stats.total_fills++;
|
|
}
|
|
else
|
|
{
|
|
m_exec_stats.total_rejected++;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Execute adaptive order based on market conditions |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeManagerV71::ExecuteAdaptiveOrder(string symbol, ENUM_ORDER_TYPE type, double volume)
|
|
{
|
|
//--- Analyze current market conditions
|
|
double spread = SymbolInfoInteger(symbol, SYMBOL_SPREAD);
|
|
double avg_spread = spread; // Simplified - would track average
|
|
|
|
MqlTick tick;
|
|
SymbolInfoTick(symbol, tick);
|
|
|
|
//--- Check liquidity
|
|
double bid_volume = tick.volume_real;
|
|
|
|
//--- Adaptive logic
|
|
if(spread > avg_spread * 1.5)
|
|
{
|
|
//--- Wide spread - use limit order
|
|
double price = (type == ORDER_TYPE_BUY) ? tick.ask - SymbolInfoDouble(symbol, SYMBOL_POINT) :
|
|
tick.bid + SymbolInfoDouble(symbol, SYMBOL_POINT);
|
|
return ExecuteLimitOrder(symbol, type, volume, price);
|
|
}
|
|
else if(volume > bid_volume * 0.1 && m_config.use_iceberg)
|
|
{
|
|
//--- Large order relative to liquidity - use iceberg
|
|
return ExecuteIcebergOrder(symbol, type, volume, 20); // Show 20%
|
|
}
|
|
else
|
|
{
|
|
//--- Normal conditions - market order
|
|
return ExecuteMarketOrder(symbol, type, volume);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Execute TWAP order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeManagerV71::ExecuteTWAPOrder(string symbol, ENUM_ORDER_TYPE type,
|
|
double volume, int minutes)
|
|
{
|
|
//--- Create TWAP order
|
|
TWAPOrder twap;
|
|
twap.symbol = symbol;
|
|
twap.type = type;
|
|
twap.total_volume = volume;
|
|
twap.filled_volume = 0;
|
|
twap.start_time = TimeCurrent();
|
|
twap.end_time = twap.start_time + minutes * 60;
|
|
twap.slices = MathMax(5, minutes); // One slice per minute, minimum 5
|
|
twap.last_slice_time = 0;
|
|
|
|
//--- Add to tracking array
|
|
int size = ArraySize(m_twap_orders);
|
|
ArrayResize(m_twap_orders, size + 1);
|
|
m_twap_orders[size] = twap;
|
|
|
|
//--- Execute first slice immediately
|
|
ProcessTWAPOrders();
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Execute VWAP order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeManagerV71::ExecuteVWAPOrder(string symbol, ENUM_ORDER_TYPE type, double volume)
|
|
{
|
|
//--- Analyze volume profile
|
|
double total_volume = 0;
|
|
double vwap = 0;
|
|
|
|
//--- Get recent ticks for volume analysis
|
|
MqlTick ticks[];
|
|
int copied = CopyTicks(symbol, ticks, COPY_TICKS_TRADE, TimeCurrent() - 3600, 1000);
|
|
|
|
if(copied > 0)
|
|
{
|
|
//--- Calculate VWAP
|
|
for(int i = 0; i < copied; i++)
|
|
{
|
|
vwap += ticks[i].last * ticks[i].volume_real;
|
|
total_volume += ticks[i].volume_real;
|
|
}
|
|
|
|
if(total_volume > 0)
|
|
vwap /= total_volume;
|
|
}
|
|
|
|
//--- Execute based on VWAP
|
|
if(vwap > 0)
|
|
{
|
|
MqlTick current_tick;
|
|
SymbolInfoTick(symbol, current_tick);
|
|
|
|
//--- If price is better than VWAP, execute immediately
|
|
if((type == ORDER_TYPE_BUY && current_tick.ask <= vwap) ||
|
|
(type == ORDER_TYPE_SELL && current_tick.bid >= vwap))
|
|
{
|
|
return ExecuteMarketOrder(symbol, type, volume);
|
|
}
|
|
else
|
|
{
|
|
//--- Use limit order at VWAP
|
|
return ExecuteLimitOrder(symbol, type, volume, vwap);
|
|
}
|
|
}
|
|
|
|
//--- Fallback to market order
|
|
return ExecuteMarketOrder(symbol, type, volume);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Execute iceberg order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeManagerV71::ExecuteIcebergOrder(string symbol, ENUM_ORDER_TYPE type,
|
|
double total_volume, double display_percent)
|
|
{
|
|
//--- Calculate displayed volume
|
|
double displayed = total_volume * display_percent / 100.0;
|
|
|
|
//--- Normalize to lot step
|
|
double lot_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
|
|
displayed = MathRound(displayed / lot_step) * lot_step;
|
|
|
|
//--- Ensure minimum lot
|
|
double min_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
|
|
displayed = MathMax(displayed, min_lot);
|
|
|
|
//--- Create iceberg order
|
|
IcebergOrder iceberg;
|
|
iceberg.parent_ticket = 0;
|
|
iceberg.symbol = symbol;
|
|
iceberg.type = type;
|
|
iceberg.total_volume = total_volume;
|
|
iceberg.displayed_volume = displayed;
|
|
iceberg.filled_volume = 0;
|
|
iceberg.price_limit = 0;
|
|
iceberg.expiry = TimeCurrent() + 86400; // 24 hours
|
|
iceberg.is_active = true;
|
|
|
|
//--- Add to tracking
|
|
int size = ArraySize(m_iceberg_orders);
|
|
ArrayResize(m_iceberg_orders, size + 1);
|
|
m_iceberg_orders[size] = iceberg;
|
|
|
|
//--- Execute first slice
|
|
return ExecuteIcebergSlice(m_iceberg_orders[size]);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Execute market order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeManagerV71::ExecuteMarketOrder(string symbol, ENUM_ORDER_TYPE type, double volume)
|
|
{
|
|
//--- Get current price
|
|
MqlTick tick;
|
|
if(!SymbolInfoTick(symbol, tick))
|
|
return false;
|
|
|
|
double price = (type == ORDER_TYPE_BUY) ? tick.ask : tick.bid;
|
|
|
|
//--- Execute order
|
|
bool result = false;
|
|
|
|
if(type == ORDER_TYPE_BUY)
|
|
result = m_trade.Buy(volume, symbol);
|
|
else
|
|
result = m_trade.Sell(volume, symbol);
|
|
|
|
//--- Track execution quality
|
|
if(result)
|
|
{
|
|
//--- Get fill price
|
|
if(m_trade.ResultDeal() > 0)
|
|
{
|
|
double fill_price = m_trade.ResultPrice();
|
|
double slippage = CalculateSlippage(symbol, type, price, fill_price);
|
|
UpdateExecutionMetrics(slippage, m_trade.ResultRetcodeDescription());
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Execute limit order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeManagerV71::ExecuteLimitOrder(string symbol, ENUM_ORDER_TYPE type,
|
|
double volume, double price)
|
|
{
|
|
bool result = false;
|
|
|
|
if(type == ORDER_TYPE_BUY)
|
|
result = m_trade.BuyLimit(volume, price, symbol);
|
|
else
|
|
result = m_trade.SellLimit(volume, price, symbol);
|
|
|
|
return result;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Execute iceberg slice |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeManagerV71::ExecuteIcebergSlice(IcebergOrder &order)
|
|
{
|
|
//--- Calculate remaining volume
|
|
double remaining = order.total_volume - order.filled_volume;
|
|
if(remaining <= 0)
|
|
{
|
|
order.is_active = false;
|
|
return true;
|
|
}
|
|
|
|
//--- Determine slice size
|
|
double slice_volume = MathMin(order.displayed_volume, remaining);
|
|
|
|
//--- Execute slice
|
|
bool result = ExecuteMarketOrder(order.symbol, order.type, slice_volume);
|
|
|
|
if(result)
|
|
{
|
|
order.filled_volume += slice_volume;
|
|
|
|
//--- Store parent ticket if first slice
|
|
if(order.parent_ticket == 0 && m_trade.ResultDeal() > 0)
|
|
{
|
|
order.parent_ticket = m_trade.ResultDeal();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Process pending iceberg orders |
|
|
//+------------------------------------------------------------------+
|
|
void CTradeManagerV71::ProcessIcebergOrders()
|
|
{
|
|
for(int i = 0; i < ArraySize(m_iceberg_orders); i++)
|
|
{
|
|
if(!m_iceberg_orders[i].is_active)
|
|
continue;
|
|
|
|
//--- Check if current slice is filled
|
|
if(m_iceberg_orders[i].parent_ticket > 0)
|
|
{
|
|
//--- Check position
|
|
if(!PositionSelectByTicket(m_iceberg_orders[i].parent_ticket))
|
|
{
|
|
//--- Position closed, execute next slice
|
|
ExecuteIcebergSlice(m_iceberg_orders[i]);
|
|
}
|
|
}
|
|
|
|
//--- Check expiry
|
|
if(TimeCurrent() > m_iceberg_orders[i].expiry)
|
|
{
|
|
m_iceberg_orders[i].is_active = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Process TWAP orders |
|
|
//+------------------------------------------------------------------+
|
|
void CTradeManagerV71::ProcessTWAPOrders()
|
|
{
|
|
datetime current_time = TimeCurrent();
|
|
|
|
for(int i = 0; i < ArraySize(m_twap_orders); i++)
|
|
{
|
|
TWAPOrder &twap = m_twap_orders[i];
|
|
|
|
//--- Check if order is complete
|
|
if(twap.filled_volume >= twap.total_volume || current_time > twap.end_time)
|
|
continue;
|
|
|
|
//--- Calculate time for next slice
|
|
double time_elapsed = current_time - twap.start_time;
|
|
double total_time = twap.end_time - twap.start_time;
|
|
double progress = time_elapsed / total_time;
|
|
|
|
//--- Calculate expected filled volume
|
|
double expected_filled = twap.total_volume * progress;
|
|
|
|
//--- Execute slice if behind schedule
|
|
if(twap.filled_volume < expected_filled)
|
|
{
|
|
double slice_size = (expected_filled - twap.filled_volume);
|
|
|
|
//--- Normalize
|
|
double lot_step = SymbolInfoDouble(twap.symbol, SYMBOL_VOLUME_STEP);
|
|
slice_size = MathRound(slice_size / lot_step) * lot_step;
|
|
|
|
//--- Ensure minimum
|
|
double min_lot = SymbolInfoDouble(twap.symbol, SYMBOL_VOLUME_MIN);
|
|
slice_size = MathMax(slice_size, min_lot);
|
|
|
|
//--- Execute
|
|
if(ExecuteMarketOrder(twap.symbol, twap.type, slice_size))
|
|
{
|
|
twap.filled_volume += slice_size;
|
|
twap.last_slice_time = current_time;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Manage trade with optimized logic |
|
|
//+------------------------------------------------------------------+
|
|
void CTradeManagerV71::ManageTradeOptimized(ManagedTradeV71 &trade, SymbolDataV71 &symbol_data)
|
|
{
|
|
//--- Quick checks using cached data
|
|
if(!trade.is_managed || trade.ticket == 0)
|
|
return;
|
|
|
|
//--- Get current price from last tick
|
|
double current_price = (trade.type == POSITION_TYPE_BUY) ?
|
|
SymbolInfoDouble(trade.symbol, SYMBOL_BID) :
|
|
SymbolInfoDouble(trade.symbol, SYMBOL_ASK);
|
|
|
|
//--- Calculate current profit in points
|
|
double profit_points = (trade.type == POSITION_TYPE_BUY) ?
|
|
(current_price - trade.open_price) :
|
|
(trade.open_price - current_price);
|
|
|
|
profit_points /= symbol_data.point;
|
|
|
|
//--- Breakeven management
|
|
if(!trade.be_activated && profit_points >= m_config.be_trigger_atr * 100)
|
|
{
|
|
double new_sl = (trade.type == POSITION_TYPE_BUY) ?
|
|
trade.open_price + m_config.be_offset_atr * symbol_data.volatility_5m :
|
|
trade.open_price - m_config.be_offset_atr * symbol_data.volatility_5m;
|
|
|
|
if((trade.type == POSITION_TYPE_BUY && new_sl > trade.sl) ||
|
|
(trade.type == POSITION_TYPE_SELL && new_sl < trade.sl))
|
|
{
|
|
if(m_trade.PositionModify(trade.ticket, new_sl, trade.tp))
|
|
{
|
|
trade.sl = new_sl;
|
|
trade.be_activated = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//--- Trailing stop
|
|
if(trade.be_activated && profit_points >= m_config.trail_trigger_atr * 100)
|
|
{
|
|
double trail_distance = m_config.trail_distance_atr * symbol_data.volatility_5m;
|
|
double new_sl = (trade.type == POSITION_TYPE_BUY) ?
|
|
current_price - trail_distance :
|
|
current_price + trail_distance;
|
|
|
|
if((trade.type == POSITION_TYPE_BUY && new_sl > trade.sl) ||
|
|
(trade.type == POSITION_TYPE_SELL && new_sl < trade.sl))
|
|
{
|
|
if(m_trade.PositionModify(trade.ticket, new_sl, trade.tp))
|
|
{
|
|
trade.sl = new_sl;
|
|
trade.trailing_activated = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//--- Partial close with smart execution
|
|
if(m_config.use_partial_close && trade.partial_count == 0 &&
|
|
profit_points >= m_config.partial_close_trigger * 100)
|
|
{
|
|
double close_volume = trade.volume * m_config.partial_close_percent / 100.0;
|
|
|
|
//--- Use iceberg for partial close if large
|
|
if(close_volume > symbol_data.min_lot * 10 && m_config.use_iceberg)
|
|
{
|
|
ExecuteIcebergOrder(trade.symbol,
|
|
(trade.type == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY,
|
|
close_volume, 30); // Show 30%
|
|
}
|
|
else
|
|
{
|
|
PartialCloseOptimized(trade.ticket, close_volume);
|
|
}
|
|
|
|
trade.partial_count++;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Close position with optimization |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeManagerV71::ClosePositionOptimized(ulong ticket)
|
|
{
|
|
if(!PositionSelectByTicket(ticket))
|
|
return false;
|
|
|
|
string symbol = PositionGetString(POSITION_SYMBOL);
|
|
double volume = PositionGetDouble(POSITION_VOLUME);
|
|
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
|
|
//--- Use smart execution for large positions
|
|
if(volume > SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN) * 20 && m_config.use_smart_routing)
|
|
{
|
|
ExecutionPlan plan;
|
|
plan.symbol = symbol;
|
|
plan.direction = (type == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
|
|
plan.total_volume = volume;
|
|
plan.algorithm = m_config.execution_algo;
|
|
plan.urgency = 0.8; // High urgency for closes
|
|
|
|
return ExecuteSmartOrder(plan);
|
|
}
|
|
|
|
//--- Standard close
|
|
return m_trade.PositionClose(ticket);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Partial close with optimization |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeManagerV71::PartialCloseOptimized(ulong ticket, double volume)
|
|
{
|
|
if(!PositionSelectByTicket(ticket))
|
|
return false;
|
|
|
|
//--- Normalize volume
|
|
string symbol = PositionGetString(POSITION_SYMBOL);
|
|
double lot_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
|
|
volume = MathRound(volume / lot_step) * lot_step;
|
|
|
|
//--- Ensure minimum
|
|
double min_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
|
|
volume = MathMax(volume, min_lot);
|
|
|
|
//--- Execute partial close
|
|
return m_trade.PositionClosePartial(ticket, volume);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Add to position with smart execution |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeManagerV71::AddToPositionOptimized(ManagedTradeV71 &trade, double add_volume)
|
|
{
|
|
//--- Create execution plan
|
|
ExecutionPlan plan;
|
|
plan.symbol = trade.symbol;
|
|
plan.direction = (trade.type == POSITION_TYPE_BUY) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
|
|
plan.total_volume = add_volume;
|
|
plan.algorithm = m_config.execution_algo;
|
|
plan.urgency = 0.5; // Medium urgency for additions
|
|
|
|
return ExecuteSmartOrder(plan);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate slippage |
|
|
//+------------------------------------------------------------------+
|
|
double CTradeManagerV71::CalculateSlippage(string symbol, ENUM_ORDER_TYPE type,
|
|
double expected_price, double fill_price)
|
|
{
|
|
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
|
if(point == 0) return 0;
|
|
|
|
double slippage = 0;
|
|
|
|
if(type == ORDER_TYPE_BUY || type == ORDER_TYPE_BUY_LIMIT)
|
|
slippage = (fill_price - expected_price) / point;
|
|
else
|
|
slippage = (expected_price - fill_price) / point;
|
|
|
|
return slippage / 10.0; // Convert to pips
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Update execution metrics |
|
|
//+------------------------------------------------------------------+
|
|
void CTradeManagerV71::UpdateExecutionMetrics(double slippage, ulong latency)
|
|
{
|
|
m_exec_stats.last_slippage = slippage;
|
|
|
|
//--- Update average slippage
|
|
m_exec_stats.avg_slippage = (m_exec_stats.avg_slippage * m_exec_stats.total_fills + slippage) /
|
|
(m_exec_stats.total_fills + 1);
|
|
|
|
//--- Track positive slippage
|
|
if(slippage < 0) // Negative slippage is positive for trader
|
|
{
|
|
m_exec_stats.positive_slippage_rate =
|
|
(m_exec_stats.positive_slippage_rate * m_exec_stats.total_fills + 1) /
|
|
(m_exec_stats.total_fills + 1);
|
|
|
|
m_exec_stats.improvement_amount += MathAbs(slippage);
|
|
}
|
|
else
|
|
{
|
|
m_exec_stats.positive_slippage_rate =
|
|
(m_exec_stats.positive_slippage_rate * m_exec_stats.total_fills) /
|
|
(m_exec_stats.total_fills + 1);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Execute emergency close |
|
|
//+------------------------------------------------------------------+
|
|
bool CTradeManagerV71::ExecuteEmergencyClose(ulong ticket)
|
|
{
|
|
//--- Set maximum slippage for emergency
|
|
int old_deviation = m_trade.GetDeviationInPoints();
|
|
m_trade.SetDeviationInPoints(1000); // 100 pips emergency slippage
|
|
|
|
bool result = m_trade.PositionClose(ticket);
|
|
|
|
//--- Restore normal slippage
|
|
m_trade.SetDeviationInPoints(old_deviation);
|
|
|
|
return result;
|
|
}
|
|
|
|
#endif // TRADE_MANAGER_V71_MQH |