701 lines
26 KiB
MQL5
701 lines
26 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| TradeExecutor.mqh |
| |||
//| Copyright 2025, EscapeEA |
| |||
//| https://www.escapeea.com |
| |||
//+------------------------------------------------------------------+
| |||
#property copyright "Copyright 2025, EscapeEA"
| |||
#property link "https://www.escapeea.com"
| |||
#property version "2.00" // Major version bump for security enhancements
| |||
#property strict
| |||
| |||
/**
| |||
* @file TradeExecutor.mqh
| |||
* @brief Advanced trade execution module with security and risk management features
| |||
*
| |||
* This module provides a robust trade execution system with the following features:
| |||
* - Secure order execution with validation and retry logic
| |||
* - Circuit breaker pattern to prevent excessive losses
| |||
* - Rate limiting to prevent order flooding
| |||
* - Comprehensive trade logging and performance tracking
| |||
* - Integration with RiskManager for position sizing and risk control
| |||
* - Paper trading simulation capabilities
| |||
*
| |||
* Security Features:
| |||
* - Input validation for all trade parameters
| |||
* - Market condition verification
| |||
* - Anomaly detection for suspicious trading patterns
| |||
* - Secure order filling policies
| |||
* - Comprehensive error handling and logging
| |||
*
| |||
* @see CRiskManager.h For risk management integration
| |||
* @see SecurityEnhancements.h For additional security features
| |||
*/
| |||
| |||
#include <Trade\Trade.mqh>
| |||
#include <Trade\PositionInfo.mqh>
| |||
#include <Trade\OrderInfo.mqh>
| |||
#include <Arrays\ArrayObj.mqh>
| |||
#include "RiskManager.mqh"
| |||
#include "SecurityEnhancements.mqh"
| |||
#include "SecurityManager.mqh"
| |||
| |||
// Execution limits and constraints
| |||
#define MAX_ORDER_AGE_SEC 60 // Maximum age of an order in seconds before considering it stale
| |||
#define PRICE_VALIDATION_RANGE 0.0005 // Price validation range (0.05%) for market order validation
| |||
| |||
// Custom error codes for trade execution (using high numbers to avoid conflicts with MQL5 built-in codes)
| |||
#define ESCAPE_ERR_TRADE_DISABLED 10027 // Trade is disabled
| |||
#define ESCAPE_ERR_TRADE_POSITION_NOT_FOUND 4753 // Position not found
| |||
#define ESCAPE_ERR_TRADE_ORDER_NOT_FOUND 4752 // Order not found
| |||
#define ESCAPE_ERR_TRADE_WRONG_SYMBOL 4751 // Wrong symbol
| |||
#define ESCAPE_ERR_TRADE_INVALID_VOLUME 131 // Invalid volume
| |||
#define ESCAPE_ERR_TRADE_INVALID_PRICE 4106 // Invalid price
| |||
#define ESCAPE_ERR_TRADE_SPREAD_TOO_WIDE 135 // Market is too far away
| |||
#define ESCAPE_ERR_TRADE_TOO_MANY_ORDERS 148 // Too many orders
| |||
#define ESCAPE_ERR_TRADE_LIMIT_REACHED 162 // Trading is prohibited
| |||
#define ESCAPE_ERR_INVALID_STOPS 130 // Invalid stops
| |||
| |||
// Forward declaration of CRiskManager to resolve circular dependency
| |||
class CRiskManager;
| |||
| |||
// Order execution constants - using standard ENUM_ORDER_TYPE_FILLING values
| |||
#define FILL_DEFAULT ORDER_FILLING_FOK // Default fill policy (Fill or Kill)
| |||
#define FILL_IOC ORDER_FILLING_IOC // Immediate or Cancel
| |||
#define FILL_RETURN ORDER_FILLING_RETURN // Return partial fills
| |||
| |||
// Trade execution result codes - using standard TRADE_RETCODE values with custom extensions
| |||
#define TRADE_EXECUTION_SUCCESS TRADE_RETCODE_DONE
| |||
#define TRADE_EXECUTION_FAILED TRADE_RETCODE_ERROR
| |||
#define TRADE_EXECUTION_REJECTED TRADE_RETCODE_REJECT
| |||
#define TRADE_EXECUTION_TIMEOUT TRADE_RETCODE_TIMEOUT
| |||
#define TRADE_EXECUTION_VALIDATION_ERROR TRADE_RETCODE_INVALID
| |||
#define TRADE_EXECUTION_CIRCUIT_BROKEN TRADE_RETCODE_OFF_QUOTES
| |||
#define TRADE_EXECUTION_RATE_LIMITED TRADE_RETCODE_TOO_MANY_REQUESTS
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Trade Execution Configuration |
| |||
//+------------------------------------------------------------------+
| |||
/**
| |||
* Execution Settings
| |||
* These parameters control the behavior of trade execution
| |||
*/
| |||
// Removed: input group "=== Execution Settings ==="
| |||
// Removed: input int InpMaxRetries = 3; // Declare only in main EA (.mq5)
| |||
// Removed: input int InpRetryDelay = 100; // Declare only in main EA (.mq5)
| |||
// Removed: input int InpMaxSlippage = 10; // Declare only in main EA (.mq5)
| |||
// Removed: input ENUM_ORDER_FILL_POLICY InpFillPolicy = FILL_DEFAULT; // Declare only in main EA (.mq5)
| |||
| |||
/**
| |||
* Circuit Breaker Settings
| |||
* Circuit breaker halts trading after consecutive errors to prevent excessive losses
| |||
*/
| |||
// Removed: input group "=== Circuit Breaker Settings ==="
| |||
// Removed: input bool InpUseCircuitBreaker = true; // Declare only in main EA (.mq5)
| |||
// Removed: input int InpMaxErrors = 5; // Declare only in main EA (.mq5)
| |||
// Removed: input int InpCircuitBreakTime = 300; // Declare only in main EA (.mq5)
| |||
| |||
/**
| |||
* Security and Logging Settings
| |||
* These parameters control security features and logging verbosity
| |||
*/
| |||
// Removed: input group "=== Security Settings ==="
| |||
// Removed: input bool InpEnableAnomalyDetection = true; // Declare only in main EA (.mq5)
| |||
// Removed: input bool InpVerifyOrderFills = true; // Declare only in main EA (.mq5)
| |||
// Removed: input bool InpCheckMarketConditions = true; // Declare only in main EA (.mq5)
| |||
// Removed: input bool InpLogAllTrades = true; // Declare only in main EA (.mq5)
| |||
// Removed: input int InpMaxOrdersPerMinute = 10; // Declare only in main EA (.mq5)
| |||
// Removed: input bool InpEnableTradeSignals = true; // Declare only in main EA (.mq5)
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Trade Execution Record |
| |||
//+------------------------------------------------------------------+
| |||
class CTradeRecord : public CObject
| |||
{
| |||
public:
| |||
ulong ticket; // Trade ticket
| |||
string symbol; // Symbol
| |||
ENUM_ORDER_TYPE type; // Order type
| |||
double volume; // Trade volume
| |||
double price; // Execution price
| |||
double sl; // Stop loss
| |||
double tp; // Take profit
| |||
datetime time; // Execution time
| |||
double slippage; // Slippage in points
| |||
bool isClosed; // Is position closed
| |||
string comment; // Trade comment
| |||
| |||
CTradeRecord() {}
| |||
~CTradeRecord() {}
| |||
| |||
void Set(ulong t, string s, ENUM_ORDER_TYPE ty, double v, double p,
| |||
double sll, double tpp, double slp, string cmt = "")
| |||
{
| |||
ticket = t;
| |||
symbol = s;
| |||
type = ty;
| |||
volume = v;
| |||
price = p;
| |||
sl = sll;
| |||
tp = tpp;
| |||
slippage = slp;
| |||
time = TimeCurrent();
| |||
isClosed = false;
| |||
comment = cmt;
| |||
}
| |||
| |||
void Close() { isClosed = true; }
| |||
| |||
virtual int Compare(const CObject *node, const int mode=0) const
| |||
{
| |||
const CTradeRecord *other = node;
| |||
if(this.ticket < other.ticket) return -1;
| |||
if(this.ticket > other.ticket) return 1;
| |||
return 0;
| |||
}
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| CTradeExecutor Class with Enhanced Security |
| |||
//+------------------------------------------------------------------+
| |||
class CTradeExecutor : public CObject
| |||
{
| |||
private:
| |||
// Trade statistics
| |||
int m_totalTrades;
| |||
int m_successfulTrades;
| |||
int m_failedTrades;
| |||
double m_totalSlippage;
| |||
double m_avgExecutionTime;
| |||
| |||
// State flags
| |||
bool m_isInitialized;
| |||
bool m_isTradingAllowed;
| |||
string m_lastError;
| |||
| |||
// Trade objects
| |||
CTrade m_trade;
| |||
CRiskManager *m_riskManager;
| |||
CPositionInfo m_position;
| |||
COrderInfo m_order;
| |||
| |||
// Configuration
| |||
int m_maxRetries;
| |||
bool m_verifyOrderFills;
| |||
bool m_logAllTrades;
| |||
| |||
// Trade history and state tracking
| |||
CArrayObj m_tradeHistory; // Trade execution history
| |||
CArrayObj m_activeTrades; // Currently active trades
| |||
| |||
// Circuit breaker state
| |||
int m_consecutiveErrors; // Number of consecutive errors for circuit breaker
| |||
datetime m_circuitBreakTime; // When the circuit breaker was activated
| |||
bool m_circuitBroken; // Whether the circuit breaker is active
| |||
| |||
// Rate limiting
| |||
static int s_ordersThisMinute; // Orders in current minute
| |||
static datetime s_currentMinute; // Current minute for rate limiting
| |||
| |||
// Performance metrics
| |||
int m_ordersThisMinute; // Number of orders in the current minute
| |||
| |||
// Order tracking
| |||
struct SOrderRequest
| |||
{
| |||
ulong ticket; // Order ticket
| |||
string symbol; // Trading symbol
| |||
ENUM_ORDER_TYPE type; // Order type
| |||
double volume; // Order volume
| |||
double price; // Order price
| |||
double sl; // Stop loss
| |||
double tp; // Take profit
| |||
double deviation; // Maximum price deviation
| |||
ulong magic; // Magic number
| |||
string comment; // Order comment
| |||
datetime expiration; // Order expiration time
| |||
datetime timestamp; // Order creation timestamp
| |||
int retryCount; // Number of retry attempts (0 = first attempt)
| |||
bool isProcessed; // Whether the order has been processed
| |||
MqlTradeRequest request; // Trade request
| |||
MqlTradeResult result; // Trade result
| |||
int errorCode; // Last error code
| |||
string errorMessage; // Last error message
| |||
};
| |||
| |||
SOrderRequest m_pendingOrder; // Pending order request
| |||
bool m_hasPendingOrder; // Has pending order flag
| |||
| |||
// Security state
| |||
bool m_isInitialized; // Initialization flag
| |||
| |||
// Security manager
| |||
CSecurityManager* m_securityManager; // Security manager instance
| |||
| |||
// Private methods for security and validation
| |||
bool ValidateOrder(const string symbol, ENUM_ORDER_TYPE type, double volume, double price, double sl, double tp, int magic, double slippage, string comment);
| |||
bool CheckMarketConditions(const string symbol);
| |||
bool CheckRateLimit();
| |||
bool VerifyOrderFill(const MqlTradeRequest &request, const MqlTradeResult &result);
| |||
void UpdateCircuitBreaker(const bool errorOccurred);
| |||
void LogTradeExecution(const MqlTradeRequest &request, const MqlTradeResult &result, const ulong durationMs);
| |||
void CleanupOldRecords();
| |||
void AddToHistory(CTradeRecord *record);
| |||
void UpdateTradeStatus(const ulong ticket, const bool isClosed);
| |||
| |||
public:
| |||
// Constructor/Destructor
| |||
CTradeExecutor(void);
| |||
~CTradeExecutor(void);
| |||
| |||
// Initialization
| |||
bool Initialize(CRiskManager *riskManager, int maxRetries = 3,
| |||
bool verifyOrderFills = true, bool logAllTrades = true);
| |||
void Deinitialize(void);
| |||
| |||
// Order execution
| |||
bool OpenPosition(string symbol, ENUM_ORDER_TYPE type, double volume, double price, double sl, double tp, string comment = "", int magic = 0, datetime expiration = 0, double maxSlippage = 0.0);
| |||
bool ClosePosition(ulong ticket, double volume = 0);
| |||
bool ModifyPosition(ulong ticket, double sl, double tp);
| |||
| |||
// Pending order management
| |||
bool PlacePendingOrder(string symbol, ENUM_ORDER_TYPE type, double volume, double price, double sl, double tp, string comment = "", int magic = 0, datetime expiration = 0);
| |||
bool ProcessPendingOrders(void);
| |||
| |||
// Circuit breaker control
| |||
bool IsCircuitBroken(void) const { return m_circuitBroken; }
| |||
void ResetCircuitBreaker(void);
| |||
| |||
// Performance metrics
| |||
double GetSuccessRate(void) const;
| |||
int GetTotalTrades(void) const { return m_totalTrades; }
| |||
int GetSuccessfulTrades(void) const { return m_successfulTrades; }
| |||
int GetFailedTrades(void) const { return m_failedTrades; }
| |||
| |||
// Error handling
| |||
string GetLastError(void) const { return m_lastError; }
| |||
| |||
// Trading status
| |||
bool IsTradingAllowed(void) const { return m_isTradingAllowed; }
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Initialize the trade executor |
| |||
//+------------------------------------------------------------------+
| |||
bool CTradeExecutor::Initialize(CRiskManager *riskManager, int maxRetries,
| |||
bool verifyOrderFills, bool logAllTrades)
| |||
{
| |||
if(riskManager == NULL)
| |||
{
| |||
m_lastError = "Risk manager cannot be NULL";
| |||
return false;
| |||
}
| |||
| |||
m_riskManager = riskManager;
| |||
m_maxRetries = maxRetries;
| |||
m_verifyOrderFills = verifyOrderFills;
| |||
m_logAllTrades = logAllTrades;
| |||
m_isInitialized = true;
| |||
m_isTradingAllowed = true;
| |||
| |||
// Initialize trade statistics
| |||
m_totalTrades = 0;
| |||
m_successfulTrades = 0;
| |||
m_failedTrades = 0;
| |||
m_totalSlippage = 0.0;
| |||
m_avgExecutionTime = 0.0;
| |||
| |||
return true;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Deinitialize trade executor and clean up resources |
| |||
//+------------------------------------------------------------------+
| |||
| |||
void CTradeExecutor::Deinitialize(void)
| |||
{
| |||
if(!m_isInitialized)
| |||
return;
| |||
| |||
// Save any necessary state before deinitializing
| |||
// ...
| |||
| |||
// Close any open positions if needed (configurable)
| |||
// ...
| |||
| |||
// Clean up trade history
| |||
m_tradeHistory.Clear();
| |||
m_activeTrades.Clear();
| |||
| |||
// Reset state
| |||
m_hasPendingOrder = false;
| |||
ZeroMemory(m_pendingOrder);
| |||
| |||
// Reset circuit breaker
| |||
m_circuitBroken = false;
| |||
m_consecutiveErrors = 0;
| |||
m_circuitBreakTime = 0;
| |||
| |||
// Reset rate limiting
| |||
s_ordersThisMinute = 0;
| |||
s_currentMinute = 0;
| |||
| |||
// Reset metrics
| |||
m_totalTrades = 0;
| |||
m_successfulTrades = 0;
| |||
m_failedTrades = 0;
| |||
m_totalSlippage = 0.0;
| |||
m_avgExecutionTime = 0.0;
| |||
| |||
// Clear last error
| |||
m_lastError = "";
| |||
| |||
// Mark as uninitialized
| |||
m_isInitialized = false;
| |||
m_isTradingAllowed = false;
| |||
| |||
Print("TradeExecutor: Deinitialized");
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Open a new market position with comprehensive security checks |
| |||
//+------------------------------------------------------------------+
| |||
| |||
bool CTradeExecutor::OpenPosition(string symbol, ENUM_ORDER_TYPE type, double volume,
| |||
double price, double sl, double tp, string comment,
| |||
int magic, datetime expiration, double maxSlippage)
| |||
{
| |||
// Check if trading is allowed
| |||
if(!m_isInitialized || !m_isTradingAllowed)
| |||
{
| |||
m_lastError = "Trading is not allowed. Check initialization.";
| |||
Print("TradeExecutor Error: ", m_lastError);
| |||
return false;
| |||
}
| |||
| |||
// Check circuit breaker
| |||
if(m_circuitBroken)
| |||
{
| |||
if(TimeCurrent() >= m_circuitBreakTime + m_circuitBreakTime)
| |||
{
| |||
Print("Circuit breaker cooldown period ended. Resuming normal operations.");
| |||
ResetCircuitBreaker();
| |||
}
| |||
else
| |||
{
| |||
int remaining = (int)(m_circuitBreakTime + m_circuitBreakTime - TimeCurrent());
| |||
m_lastError = "Circuit breaker active. Trading suspended for " + IntegerToString(remaining) + " seconds";
| |||
Print("TradeExecutor Error: ", m_lastError);
| |||
return false;
| |||
}
| |||
}
| |||
| |||
// Check rate limiting
| |||
if(!CheckRateLimit())
| |||
{
| |||
m_lastError = "Rate limit exceeded";
| |||
return false;
| |||
}
| |||
| |||
// Check market conditions if enabled
| |||
if(m_checkMarketConditions && !CheckMarketConditions(symbol))
| |||
{
| |||
m_lastError = "Market conditions check failed for " + symbol;
| |||
Print("TradeExecutor Error: ", m_lastError);
| |||
return false;
| |||
}
| |||
| |||
// Prepare order request with additional security parameters
| |||
SOrderRequest request = {};
| |||
request.symbol = symbol;
| |||
request.type = type;
| |||
request.volume = volume;
| |||
request.price = price;
| |||
request.sl = sl;
| |||
request.tp = tp;
| |||
request.comment = comment;
| |||
request.magic = (magic > 0) ? magic : m_trade.RequestMagic();
| |||
request.expiration = expiration;
| |||
request.attempts = 0;
| |||
request.reqId = IntegerToString(GetTickCount()) + "_" + IntegerToString(OrdersTotal());
| |||
request.maxSlippage = (maxSlippage > 0) ? maxSlippage : InpMaxSlippage;
| |||
request.requireVerification = InpVerifyOrderFills;
| |||
| |||
// Log the trade attempt
| |||
if(InpLogAllTrades)
| |||
{
| |||
string orderType = (type == ORDER_TYPE_BUY) ? "BUY" : "SELL";
| |||
PrintFormat("TradeExecutor: Attempting %s %s %.2f lots @ %s (SL: %s, TP: %s)",
| |||
orderType, symbol, volume, DoubleToString(price, _Digits),
| |||
DoubleToString(sl, _Digits), DoubleToString(tp, _Digits));
| |||
}
| |||
| |||
// Execute the order with retry logic
| |||
bool result = false;
| |||
MqlTradeRequest tradeRequest = {};
| |||
MqlTradeResult tradeResult = {};
| |||
| |||
// Prepare trade request
| |||
tradeRequest.action = TRADE_ACTION_DEAL;
| |||
tradeRequest.symbol = symbol;
| |||
tradeRequest.volume = volume;
| |||
tradeRequest.type = type;
| |||
tradeRequest.price = price;
| |||
tradeRequest.sl = sl;
| |||
tradeRequest.tp = tp;
| |||
tradeRequest.deviation = (ushort)request.maxSlippage;
| |||
tradeRequest.magic = request.magic;
| |||
tradeRequest.comment = comment;
| |||
tradeRequest.type_filling = ORDER_FILLING_FOK;
| |||
| |||
// Validate the request using the unified validator
| |||
if(!ValidateOrder(symbol, type, volume, price, sl, tp, request.magic, request.maxSlippage, comment))
| |||
{
| |||
m_lastError = "Trade request validation failed: " + m_lastError;
| |||
Print("TradeExecutor Error: ", m_lastError);
| |||
UpdateCircuitBreaker(true);
| |||
return false;
| |||
}
| |||
| |||
// Execute the trade with retry logic
| |||
ulong startTime = GetTickCount();
| |||
for(int attempt = 0; attempt <= m_maxRetries; attempt++)
| |||
{
| |||
// Check for user break
| |||
if(IsStopped())
| |||
{
| |||
m_lastError = "Operation canceled by user";
| |||
Print("TradeExecutor: ", m_lastError);
| |||
return false;
| |||
}
| |||
| |||
// Add delay between retries (except first attempt)
| |||
if(attempt > 0)
| |||
{
| |||
int delay = MathMin(m_retryDelay * (int)MathPow(2, attempt-1), 5000); // Exponential backoff, max 5s
| |||
PrintFormat("TradeExecutor: Retry attempt %d of %d after %d ms",
| |||
attempt, m_maxRetries, delay);
| |||
Sleep(delay);
| |||
}
| |||
| |||
// Execute the trade
| |||
ZeroMemory(tradeResult);
| |||
bool success = OrderSend(tradeRequest, tradeResult);
| |||
| |||
// Log the execution
| |||
ulong duration = GetTickCount() - startTime;
| |||
LogTradeExecution(tradeRequest, tradeResult, duration);
| |||
| |||
// Check the result
| |||
if(success && tradeResult.retcode == TRADE_RETCODE_DONE)
| |||
{
| |||
// Verify the order fill if required
| |||
if(!request.requireVerification || VerifyOrderFill(tradeRequest, tradeResult))
| |||
{
| |||
// Create trade record
| |||
CTradeRecord *record = new CTradeRecord();
| |||
record.Set(tradeResult.order, symbol, type, volume, tradeResult.price,
| |||
sl, tp, tradeResult.deviation, comment);
| |||
AddToHistory(record);
| |||
| |||
// Update metrics
| |||
m_successfulTrades++;
| |||
m_totalSlippage += tradeResult.deviation;
| |||
m_avgExecutionTime = (m_avgExecutionTime * (m_successfulTrades - 1) + duration) / m_successfulTrades;
| |||
| |||
if(InpLogAllTrades)
| |||
{
| |||
PrintFormat("TradeExecutor: Order #%d executed successfully in %d ms. Slippage: %.1f pips",
| |||
tradeResult.order, duration, tradeResult.deviation);
| |||
}
| |||
| |||
result = true;
| |||
break;
| |||
}
| |||
else
| |||
{
| |||
// Verification failed
| |||
m_lastError = "Order fill verification failed";
| |||
Print("TradeExecutor Error: ", m_lastError);
| |||
UpdateCircuitBreaker(true);
| |||
return false;
| |||
}
| |||
}
| |||
else
| |||
{
| |||
// Log the error
| |||
string errorDesc = GetRetcodeDescription(tradeResult.retcode);
| |||
m_lastError = StringFormat("OrderSend failed: %s (code: %d)", errorDesc, tradeResult.retcode);
| |||
Print("TradeExecutor Error: ", m_lastError);
| |||
| |||
// Update error tracking
| |||
m_failedTrades++;
| |||
m_consecutiveErrors++;
| |||
| |||
// Check if we should continue retrying
| |||
if(attempt >= m_maxRetries - 1)
| |||
{
| |||
UpdateCircuitBreaker(true);
| |||
return false;
| |||
}
| |||
}
| |||
}
| |||
| |||
// Update metrics
| |||
m_totalTrades++;
| |||
| |||
// Update last order time for rate limiting
| |||
m_lastOrderTime = TimeCurrent();
| |||
| |||
// Check if we need to trigger circuit breaker
| |||
if(!result)
| |||
else
| |||
{
| |||
//+------------------------------------------------------------------+
| |||
//| Check if market conditions are suitable for trading |
| |||
//+------------------------------------------------------------------+
| |||
bool CTradeExecutor::CheckMarketConditions(const string symbol)
| |||
{
| |||
// Implementation here
| |||
return true;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Static member initialization |
| |||
//+------------------------------------------------------------------+
| |||
int CTradeExecutor::s_ordersThisMinute = 0;
| |||
datetime CTradeExecutor::s_currentMinute = 0;
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Get the number of open positions for a given symbol and magic |
| |||
//+------------------------------------------------------------------+
| |||
int CTradeExecutor::GetOpenPositionsCount(const string symbol, int magic)
| |||
{
| |||
int count = 0;
| |||
| |||
for(int i = PositionsTotal() - 1; i >= 0; i--)
| |||
{
| |||
if(m_position.SelectByIndex(i))
| |||
{
| |||
if((symbol == "" || m_position.Symbol() == symbol) &&
| |||
(magic == 0 || m_position.Magic() == magic))
| |||
{
| |||
count++;
| |||
}
| |||
}
| |||
}
| |||
| |||
return count;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Calculate position size based on risk percentage |
| |||
//+------------------------------------------------------------------+
| |||
| |||
double CTradeExecutor::CalculatePositionSize(const string symbol, double riskPercent, double stopLossPips)
| |||
{
| |||
if(riskPercent <= 0 || stopLossPips <= 0)
| |||
return 0.0;
| |||
| |||
double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
| |||
double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
| |||
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
| |||
double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
| |||
double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
| |||
| |||
if(tickSize == 0 || tickValue == 0 || lotStep == 0)
| |||
{
| |||
Print("Error: Invalid symbol parameters for position size calculation");
| |||
return 0.0;
| |||
}
| |||
| |||
// Calculate position size based on risk percentage
| |||
double riskAmount = AccountInfoDouble(ACCOUNT_BALANCE) * riskPercent / 100.0;
| |||
double pipValue = tickValue / (tickSize / SymbolInfoDouble(symbol, SYMBOL_POINT));
| |||
double stopLossValue = stopLossPips * pipValue;
| |||
| |||
if(stopLossValue <= 0)
| |||
{
| |||
Print("Error: Invalid stop loss value for position size calculation");
| |||
return 0.0;
| |||
}
| |||
| |||
double positionSize = NormalizeDouble(riskAmount / stopLossValue, 2);
| |||
| |||
// Round to nearest lot step
| |||
positionSize = MathFloor(positionSize / lotStep) * lotStep;
| |||
| |||
// Ensure position size is within allowed range
| |||
positionSize = MathMax(minLot, MathMin(maxLot, positionSize));
| |||
| |||
return positionSize;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Calculate take profit level based on risk/reward ratio |
| |||
//+------------------------------------------------------------------+
| |||
double CTradeExecutor::CalculateTakeProfitLevel(double entryPrice, double stopLoss, double riskRewardRatio, bool isBuy)
| |||
{
| |||
double priceDiff = MathAbs(entryPrice - stopLoss) * riskRewardRatio;
| |||
| |||
if(isBuy)
| |||
return entryPrice + priceDiff;
| |||
else
| |||
return entryPrice - priceDiff;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Send a trade signal to external systems |
| |||
//+------------------------------------------------------------------+
| |||
| |||
void CTradeExecutor::SendTradeSignal(const string symbol, ENUM_ORDER_TYPE type, double volume,
| |||
double price, double sl, double tp, const string comment)
| |||
{
| |||
if(!InpEnableTradeSignals)
| |||
return;
| |||
| |||
string signal = StringFormat(
| |||
"TRADE_SIGNAL|%s|%s|%s|%.2f|%.5f|%.5f|%.5f|%s",
| |||
TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS),
| |||
symbol,
| |||
(type == ORDER_TYPE_BUY) ? "BUY" : "SELL",
| |||
volume,
| |||
price,
| |||
sl,
| |||
tp,
| |||
comment
| |||
);
| |||
| |||
// In a real implementation, this could send the signal to a webhook, MQ, etc.
| |||
Print("Trade Signal: ", signal);
| |||
| |||
// Example: Send to a global variable for EA communication
| |||
GlobalVariableSet("LastTradeSignal_" + symbol, TimeCurrent());
| |||
GlobalVariableSet("LastTradeSignal_Data_" + symbol, StringToDouble(signal));
| |||
}
| |||
| |||
#include "TradeExecutor_Impl.mqh"
| |||
// Reset circuit breaker
| |||
ResetCircuitBreaker();
| |||
| |||
// Reset metrics
| |||
m_totalTrades = 0;
| |||
m_successfulTrades = 0;
| |||
m_failedTrades = 0;
| |||
m_totalSlippage = 0.0;
| |||
m_avgExecutionTime = 0.0;
| |||
| |||
// Initialize trade object with current settings
| |||
m_trade.SetDeviationInPoints(InpMaxSlippage);
| |||
| |||
// Load any persisted state if needed
| |||
// ...
| |||
| |||
// Clean up any old records
| |||
CleanupOldRecords();
| |||
| |||
m_isInitialized = true;
| |||
m_isTradingAllowed = true;
| |||
| |||
Print("TradeExecutor: Initialized successfully");
| |||
return true;
| |||
}
|