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

863 lines
31 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
//+------------------------------------------------------------------+
//| Utilities_v71.mqh |
//| Enhanced Utilities Module v7.1 |
//| Optimized Logging, Performance Tools, Helpers |
//+------------------------------------------------------------------+
#ifndef UTILITIES_V71_MQH
#define UTILITIES_V71_MQH
#include "DataTypes_v71.mqh"
#include <Files/FileTxt.mqh>
#include <Files/FileBin.mqh>
//+------------------------------------------------------------------+
//| Performance Timer |
//+------------------------------------------------------------------+
struct PerformanceTimer
{
string name;
ulong start_time;
ulong total_time;
int call_count;
double avg_time_ms;
double max_time_ms;
double min_time_ms;
};
//+------------------------------------------------------------------+
//| Log Entry |
//+------------------------------------------------------------------+
struct LogEntry
{
datetime timestamp;
ENUM_LOG_LEVEL level;
string source;
string message;
int error_code;
};
//+------------------------------------------------------------------+
//| Utilities Class - Enhanced Version |
//+------------------------------------------------------------------+
class CUtilitiesV71
{
private:
//--- Configuration
ENUM_LOG_LEVEL m_log_level;
bool m_is_testing;
bool m_ultra_low_latency;
string m_log_filename;
string m_report_filename;
//--- Performance monitoring
PerformanceTimer m_timers[];
int m_timer_count;
//--- Log buffer for low latency mode
LogEntry m_log_buffer[];
int m_log_buffer_size;
int m_log_write_index;
bool m_buffered_logging;
//--- File handles
int m_log_handle;
int m_report_handle;
//--- Memory pool for string operations
string m_string_pool[];
int m_string_pool_size;
//--- Helper methods
string GetLogPrefix(ENUM_LOG_LEVEL level);
void WriteLogEntry(const LogEntry &entry);
void FlushLogBuffer();
public:
CUtilitiesV71();
~CUtilitiesV71();
//--- Initialization
bool Initialize(ENUM_LOG_LEVEL level);
bool InitializeOptimized(ENUM_LOG_LEVEL level, bool ultra_low_latency);
//--- Logging with minimal overhead
void Log(ENUM_LOG_LEVEL level, string message);
void LogOptimized(ENUM_LOG_LEVEL level, string message);
void LogError(string function, int error_code);
void LogTrade(const ManagedTradeV71 &trade, string action);
void LogPerformance(string operation, double time_ms);
//--- Performance monitoring
void StartTimer(string name);
void StopTimer(string name);
double GetAverageTime(string name);
void ReportTimings();
//--- Terminal and connection checks
bool CheckTerminalConnection();
bool CheckAutoTradingEnabled();
bool IsTradingAllowed();
bool IsMarketClosed(string symbol);
//--- Time utilities
bool IsAsianSession();
bool IsEuropeanSession();
bool IsAmericanSession();
bool IsNewsTime(string symbol);
int GetMarketSession();
//--- Mathematical utilities
double NormalizeVolume(string symbol, double volume);
double NormalizePricepredictable(string symbol, double price);
double CalculateCommission(ulong ticket);
double CalculateCommissionOptimized(ulong ticket);
double GetSpreadCost(string symbol, double volume);
//--- String utilities optimized
string FormatCurrency(double value);
string FormatPercentage(double value, int digits = 2);
string FormatDuration(int seconds);
string GetUninitReasonText(int reason);
//--- File operations
void GenerateReport(PerformanceMetricsV71 &performance,
ManagedTradeV71 &trades[]);
void SaveTradeHistory(const ManagedTradeV71 &trade);
void SaveTradeHistoryOptimized(const ManagedTradeV71 &trade);
bool LoadConfiguration(string filename);
bool SaveConfiguration(string filename);
//--- Data export
void ExportToCSV(string filename, ManagedTradeV71 &trades[]);
void ExportPerformanceMetrics(string filename, PerformanceMetricsV71 &metrics);
void ExportRiskMetrics(string filename, SystemStateV71 &state);
//--- System utilities
void PrintSystemInfo();
int GetCPUCores();
long GetAvailableMemory();
double GetCPUUsage();
//--- Market data utilities
double GetSymbolPointValue(string symbol);
double GetSymbolTickValue(string symbol, double volume = 1.0);
int GetSymbolDigits(string symbol);
double ConvertToAccountCurrency(string symbol, double amount);
//--- Array utilities (optimized)
template<typename T>
void ArrayShuffle(T &array[]);
template<typename T>
int ArrayFindMax(const T &array[]);
template<typename T>
int ArrayFindMin(const T &array[]);
template<typename T>
double ArrayMean(const T &array[]);
template<typename T>
double ArrayStdDev(const T &array[]);
//--- Notification utilities
void SendNotification(string title, string message);
void SendEmail(string subject, string body);
void PlayAlert(string sound_file);
//--- Cleanup
void FlushLogs();
void CloseAllFiles();
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CUtilitiesV71::CUtilitiesV71()
{
m_log_level = LOG_INFO;
m_is_testing = MQLInfoInteger(MQL_TESTER);
m_ultra_low_latency = false;
m_log_filename = "ERMT71_Log.txt";
m_report_filename = "ERMT71_Report.txt";
m_timer_count = 0;
m_log_buffer_size = 1000;
m_log_write_index = 0;
m_buffered_logging = false;
m_log_handle = INVALID_HANDLE;
m_report_handle = INVALID_HANDLE;
m_string_pool_size = 100;
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CUtilitiesV71::~CUtilitiesV71()
{
FlushLogs();
CloseAllFiles();
}
//+------------------------------------------------------------------+
//| Initialize with optimization |
//+------------------------------------------------------------------+
bool CUtilitiesV71::InitializeOptimized(ENUM_LOG_LEVEL level, bool ultra_low_latency)
{
m_log_level = level;
m_ultra_low_latency = ultra_low_latency;
//--- Set up buffered logging for low latency
if(m_ultra_low_latency)
{
m_buffered_logging = true;
ArrayResize(m_log_buffer, m_log_buffer_size);
ArrayResize(m_string_pool, m_string_pool_size);
}
//--- Initialize performance timers
ArrayResize(m_timers, 20);
//--- Open log file if not testing
if(!m_is_testing && !m_ultra_low_latency)
{
m_log_handle = FileOpen(m_log_filename, FILE_WRITE|FILE_TXT|FILE_ANSI|FILE_SHARE_READ);
}
Log(LOG_INFO, "UtilitiesV71 initialized: UltraLowLatency=" + (string)m_ultra_low_latency);
return true;
}
//+------------------------------------------------------------------+
//| Optimized logging for low latency |
//+------------------------------------------------------------------+
void CUtilitiesV71::LogOptimized(ENUM_LOG_LEVEL level, string message)
{
if(level < m_log_level) return;
if(m_buffered_logging)
{
//--- Add to buffer
LogEntry entry;
entry.timestamp = TimeCurrent();
entry.level = level;
entry.source = "ERMT71";
entry.message = message;
entry.error_code = 0;
m_log_buffer[m_log_write_index] = entry;
m_log_write_index = (m_log_write_index + 1) % m_log_buffer_size;
//--- Flush buffer if critical
if(level >= LOG_ERROR)
FlushLogBuffer();
}
else
{
Log(level, message);
}
}
//+------------------------------------------------------------------+
//| Standard logging |
//+------------------------------------------------------------------+
void CUtilitiesV71::Log(ENUM_LOG_LEVEL level, string message)
{
if(level < m_log_level) return;
string log_entry = TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS) + " " +
GetLogPrefix(level) + " " + message;
//--- Print to terminal
if(level >= LOG_WARNING)
Print(log_entry);
//--- Write to file if not testing
if(!m_is_testing && m_log_handle != INVALID_HANDLE)
{
FileWrite(m_log_handle, log_entry);
FileFlush(m_log_handle);
}
}
//+------------------------------------------------------------------+
//| Get log level prefix |
//+------------------------------------------------------------------+
string CUtilitiesV71::GetLogPrefix(ENUM_LOG_LEVEL level)
{
switch(level)
{
case LOG_DEBUG: return "[DEBUG]";
case LOG_INFO: return "[INFO ]";
case LOG_WARNING: return "[WARN ]";
case LOG_ERROR: return "[ERROR]";
case LOG_CRITICAL: return "[CRIT ]";
default: return "[?????]";
}
}
//+------------------------------------------------------------------+
//| Flush log buffer to file |
//+------------------------------------------------------------------+
void CUtilitiesV71::FlushLogBuffer()
{
if(!m_buffered_logging || m_is_testing) return;
//--- Open file if needed
if(m_log_handle == INVALID_HANDLE)
{
m_log_handle = FileOpen(m_log_filename, FILE_WRITE|FILE_TXT|FILE_ANSI|FILE_SHARE_READ);
if(m_log_handle == INVALID_HANDLE) return;
}
//--- Write all buffered entries
for(int i = 0; i < m_log_buffer_size; i++)
{
if(m_log_buffer[i].timestamp > 0)
{
WriteLogEntry(m_log_buffer[i]);
m_log_buffer[i].timestamp = 0; // Clear entry
}
}
FileFlush(m_log_handle);
}
//+------------------------------------------------------------------+
//| Write single log entry |
//+------------------------------------------------------------------+
void CUtilitiesV71::WriteLogEntry(const LogEntry &entry)
{
if(m_log_handle == INVALID_HANDLE) return;
string log_line = TimeToString(entry.timestamp, TIME_DATE|TIME_SECONDS) + " " +
GetLogPrefix(entry.level) + " [" + entry.source + "] " +
entry.message;
if(entry.error_code != 0)
log_line += " (Error: " + IntegerToString(entry.error_code) + ")";
FileWrite(m_log_handle, log_line);
}
//+------------------------------------------------------------------+
//| Start performance timer |
//+------------------------------------------------------------------+
void CUtilitiesV71::StartTimer(string name)
{
//--- Find existing timer
int index = -1;
for(int i = 0; i < m_timer_count; i++)
{
if(m_timers[i].name == name)
{
index = i;
break;
}
}
//--- Create new timer if not found
if(index < 0)
{
if(m_timer_count >= ArraySize(m_timers))
ArrayResize(m_timers, m_timer_count + 10);
index = m_timer_count;
m_timers[index].name = name;
m_timers[index].total_time = 0;
m_timers[index].call_count = 0;
m_timers[index].avg_time_ms = 0;
m_timers[index].max_time_ms = 0;
m_timers[index].min_time_ms = DBL_MAX;
m_timer_count++;
}
//--- Start timing
m_timers[index].start_time = GetMicrosecondCount();
}
//+------------------------------------------------------------------+
//| Stop performance timer |
//+------------------------------------------------------------------+
void CUtilitiesV71::StopTimer(string name)
{
//--- Find timer
for(int i = 0; i < m_timer_count; i++)
{
if(m_timers[i].name == name)
{
//--- Calculate elapsed time
ulong elapsed = GetMicrosecondCount() - m_timers[i].start_time;
double elapsed_ms = elapsed / 1000.0;
//--- Update statistics
m_timers[i].total_time += elapsed;
m_timers[i].call_count++;
m_timers[i].avg_time_ms = (m_timers[i].total_time / 1000.0) / m_timers[i].call_count;
if(elapsed_ms > m_timers[i].max_time_ms)
m_timers[i].max_time_ms = elapsed_ms;
if(elapsed_ms < m_timers[i].min_time_ms)
m_timers[i].min_time_ms = elapsed_ms;
break;
}
}
}
//+------------------------------------------------------------------+
//| Get average execution time |
//+------------------------------------------------------------------+
double CUtilitiesV71::GetAverageTime(string name)
{
for(int i = 0; i < m_timer_count; i++)
{
if(m_timers[i].name == name)
return m_timers[i].avg_time_ms;
}
return 0;
}
//+------------------------------------------------------------------+
//| Report all timings |
//+------------------------------------------------------------------+
void CUtilitiesV71::ReportTimings()
{
Log(LOG_INFO, "=== Performance Report ===");
for(int i = 0; i < m_timer_count; i++)
{
string report = StringFormat("%s: Calls=%d, Avg=%.3fms, Min=%.3fms, Max=%.3fms",
m_timers[i].name,
m_timers[i].call_count,
m_timers[i].avg_time_ms,
m_timers[i].min_time_ms,
m_timers[i].max_time_ms);
Log(LOG_INFO, report);
}
}
//+------------------------------------------------------------------+
//| Check terminal connection |
//+------------------------------------------------------------------+
bool CUtilitiesV71::CheckTerminalConnection()
{
if(!TerminalInfoInteger(TERMINAL_CONNECTED))
{
LogOptimized(LOG_WARNING, "Terminal not connected");
return false;
}
if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
{
LogOptimized(LOG_WARNING, "Trading not allowed in terminal");
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Check if auto trading is enabled |
//+------------------------------------------------------------------+
bool CUtilitiesV71::CheckAutoTradingEnabled()
{
if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
return false;
if(!MQLInfoInteger(MQL_TRADE_ALLOWED))
return false;
if(!AccountInfoInteger(ACCOUNT_TRADE_EXPERT))
return false;
if(!AccountInfoInteger(ACCOUNT_TRADE_ALLOWED))
return false;
return true;
}
//+------------------------------------------------------------------+
//| Calculate position commission optimized |
//+------------------------------------------------------------------+
double CUtilitiesV71::CalculateCommissionOptimized(ulong ticket)
{
static ulong last_ticket = 0;
static double last_commission = 0;
//--- Cache hit
if(ticket == last_ticket)
return last_commission;
double commission = 0;
//--- Get deals for position
if(HistorySelectByPosition(ticket))
{
int deals = HistoryDealsTotal();
for(int i = 0; i < deals; i++)
{
ulong deal_ticket = HistoryDealGetTicket(i);
if(deal_ticket > 0)
{
commission += HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION);
}
}
}
//--- Update cache
last_ticket = ticket;
last_commission = commission;
return commission;
}
//+------------------------------------------------------------------+
//| Format currency with optimization |
//+------------------------------------------------------------------+
string CUtilitiesV71::FormatCurrency(double value)
{
//--- Use string pool for common values
if(value == 0) return "0.00";
string sign = (value < 0) ? "-" : "";
value = MathAbs(value);
if(value >= 1000000)
return sign + DoubleToString(value/1000000, 2) + "M";
else if(value >= 1000)
return sign + DoubleToString(value/1000, 2) + "K";
else
return sign + DoubleToString(value, 2);
}
//+------------------------------------------------------------------+
//| Get market session |
//+------------------------------------------------------------------+
int CUtilitiesV71::GetMarketSession()
{
MqlDateTime time;
TimeToStruct(TimeCurrent(), time);
int hour = time.hour;
//--- Asian session (Tokyo: 00:00 - 09:00 GMT)
if(hour >= 0 && hour < 9)
return 1;
//--- European session (London: 08:00 - 17:00 GMT)
if(hour >= 8 && hour < 17)
return 2;
//--- American session (New York: 13:00 - 22:00 GMT)
if(hour >= 13 && hour < 22)
return 3;
return 0;
}
//+------------------------------------------------------------------+
//| Generate performance report |
//+------------------------------------------------------------------+
void CUtilitiesV71::GenerateReport(PerformanceMetricsV71 &performance,
ManagedTradeV71 &trades[])
{
string report = "\n";
report += "════════════════════════════════════════════════════════════════\n";
report += " ERMT 7.1 PERFORMANCE REPORT \n";
report += "════════════════════════════════════════════════════════════════\n\n";
//--- Account Summary
report += "ACCOUNT SUMMARY\n";
report += "───────────────\n";
report += "Starting Balance: " + FormatCurrency(AccountInfoDouble(ACCOUNT_BALANCE) - performance.net_profit) + "\n";
report += "Current Balance: " + FormatCurrency(AccountInfoDouble(ACCOUNT_BALANCE)) + "\n";
report += "Net Profit: " + FormatCurrency(performance.net_profit) + "\n";
report += "Return: " + FormatPercentage((performance.net_profit / (AccountInfoDouble(ACCOUNT_BALANCE) - performance.net_profit)) * 100) + "\n\n";
//--- Performance Metrics
report += "PERFORMANCE METRICS\n";
report += "──────────────────\n";
report += "Total Trades: " + IntegerToString(performance.total_trades) + "\n";
report += "Win Rate: " + FormatPercentage(performance.win_rate) + "\n";
report += "Profit Factor: " + DoubleToString(performance.profit_factor, 2) + "\n";
report += "Sharpe Ratio: " + DoubleToString(performance.sharpe_ratio, 2) + "\n";
report += "Sortino Ratio: " + DoubleToString(performance.sortino_ratio, 2) + "\n";
report += "Information Ratio: " + DoubleToString(performance.information_ratio, 2) + "\n\n";
//--- Risk Metrics
report += "RISK METRICS\n";
report += "────────────\n";
report += "Max Drawdown: " + FormatPercentage(performance.max_drawdown_percent) + "\n";
report += "VaR (95%): " + FormatPercentage(performance.var_95) + "\n";
report += "CVaR (95%): " + FormatPercentage(performance.cvar_95) + "\n";
report += "Recovery Factor: " + DoubleToString(performance.recovery_factor, 2) + "\n";
report += "Calmar Ratio: " + DoubleToString(performance.calmar_ratio, 2) + "\n\n";
//--- Advanced Metrics
report += "ADVANCED METRICS\n";
report += "───────────────\n";
report += "Alpha: " + DoubleToString(performance.alpha, 4) + "\n";
report += "Beta: " + DoubleToString(performance.beta, 2) + "\n";
report += "Treynor Ratio: " + DoubleToString(performance.treynor_ratio, 2) + "\n";
report += "Omega Ratio: " + DoubleToString(performance.omega_ratio, 2) + "\n";
report += "Hit Rate: " + FormatPercentage(performance.hit_rate) + "\n";
report += "Avg Holding Time: " + FormatDuration((int)performance.avg_holding_time) + "\n\n";
//--- Trade Analysis
report += "TRADE ANALYSIS\n";
report += "─────────────\n";
report += "Winning Trades: " + IntegerToString(performance.winning_trades) + "\n";
report += "Losing Trades: " + IntegerToString(performance.losing_trades) + "\n";
report += "Average Win: " + FormatCurrency(performance.avg_win) + "\n";
report += "Average Loss: " + FormatCurrency(performance.avg_loss) + "\n";
report += "Largest Win: " + FormatCurrency(performance.largest_win) + "\n";
report += "Largest Loss: " + FormatCurrency(performance.largest_loss) + "\n";
report += "Max Consec Wins: " + IntegerToString(performance.max_consecutive_wins) + "\n";
report += "Max Consec Losses: " + IntegerToString(performance.max_consecutive_losses) + "\n\n";
//--- Current Positions
int trade_count = ArraySize(trades);
if(trade_count > 0)
{
report += "CURRENT POSITIONS\n";
report += "────────────────\n";
report += "Symbol Type Volume Entry Current P/L Risk% R-Mult\n";
report += "─────────────────────────────────────────────────────────────────\n";
for(int i = 0; i < trade_count && i < 10; i++)
{
double current_price = (trades[i].type == POSITION_TYPE_BUY) ?
SymbolInfoDouble(trades[i].symbol, SYMBOL_BID) :
SymbolInfoDouble(trades[i].symbol, SYMBOL_ASK);
report += StringFormat("%-8s %-5s %6.2f %7.5f %7.5f %-8s %5.1f %6.2f\n",
trades[i].symbol,
(trades[i].type == POSITION_TYPE_BUY) ? "BUY" : "SELL",
trades[i].volume,
trades[i].open_price,
current_price,
FormatCurrency(trades[i].profit),
trades[i].risk_percent,
trades[i].r_multiple);
}
report += "\n";
}
//--- Performance timings
if(m_timer_count > 0)
{
report += "PERFORMANCE TIMINGS\n";
report += "──────────────────\n";
for(int i = 0; i < m_timer_count && i < 5; i++)
{
report += StringFormat("%-20s: %6.3f ms (avg), %d calls\n",
m_timers[i].name,
m_timers[i].avg_time_ms,
m_timers[i].call_count);
}
report += "\n";
}
report += "════════════════════════════════════════════════════════════════\n";
report += "Report generated: " + TimeToString(TimeCurrent()) + "\n";
report += "════════════════════════════════════════════════════════════════\n";
//--- Output report
if(m_is_testing)
{
Print(report);
}
else
{
//--- Save to file
int handle = FileOpen(m_report_filename, FILE_WRITE|FILE_TXT|FILE_ANSI);
if(handle != INVALID_HANDLE)
{
FileWrite(handle, report);
FileClose(handle);
Log(LOG_INFO, "Performance report saved to " + m_report_filename);
}
}
}
//+------------------------------------------------------------------+
//| Save trade history optimized |
//+------------------------------------------------------------------+
void CUtilitiesV71::SaveTradeHistoryOptimized(const ManagedTradeV71 &trade)
{
if(m_is_testing) return;
string filename = "TradeHistory_" + trade.symbol + ".csv";
//--- Check if file exists
bool file_exists = FileIsExist(filename);
int handle = FileOpen(filename, FILE_WRITE|FILE_CSV|FILE_ANSI|FILE_SHARE_READ, ',');
if(handle == INVALID_HANDLE) return;
//--- Write header if new file
if(!file_exists)
{
FileWrite(handle, "Ticket", "Symbol", "Type", "OpenTime", "CloseTime",
"OpenPrice", "ClosePrice", "Volume", "Profit", "Commission",
"Swap", "RMultiple", "MAE", "MFE", "VaR", "MLConfidence",
"ExecutionQuality", "EntryAlgo", "Comment");
}
//--- Move to end
FileSeek(handle, 0, SEEK_END);
//--- Write trade data
FileWrite(handle,
trade.ticket,
trade.symbol,
(trade.type == POSITION_TYPE_BUY) ? "BUY" : "SELL",
TimeToString(trade.open_time),
TimeToString(TimeCurrent()),
trade.open_price,
(trade.type == POSITION_TYPE_BUY) ?
SymbolInfoDouble(trade.symbol, SYMBOL_BID) :
SymbolInfoDouble(trade.symbol, SYMBOL_ASK),
trade.volume,
trade.profit,
trade.commission,
trade.swap,
trade.r_multiple,
trade.mae,
trade.mfe,
trade.var_contribution,
trade.ml_confidence,
trade.execution_quality,
trade.entry_algo,
trade.comment);
FileClose(handle);
}
//+------------------------------------------------------------------+
//| Format percentage |
//+------------------------------------------------------------------+
string CUtilitiesV71::FormatPercentage(double value, int digits)
{
return DoubleToString(value, digits) + "%";
}
//+------------------------------------------------------------------+
//| Format duration |
//+------------------------------------------------------------------+
string CUtilitiesV71::FormatDuration(int seconds)
{
if(seconds < 60)
return IntegerToString(seconds) + "s";
else if(seconds < 3600)
return IntegerToString(seconds/60) + "m " + IntegerToString(seconds%60) + "s";
else if(seconds < 86400)
return IntegerToString(seconds/3600) + "h " + IntegerToString((seconds%3600)/60) + "m";
else
return IntegerToString(seconds/86400) + "d " + IntegerToString((seconds%86400)/3600) + "h";
}
//+------------------------------------------------------------------+
//| Print system information |
//+------------------------------------------------------------------+
void CUtilitiesV71::PrintSystemInfo()
{
Print("=== SYSTEM INFORMATION ===");
Print("Terminal Build: ", TerminalInfoInteger(TERMINAL_BUILD));
Print("CPU Cores: ", GetCPUCores());
Print("Available Memory: ", GetAvailableMemory() / (1024*1024), " MB");
Print("Screen DPI: ", TerminalInfoInteger(TERMINAL_SCREEN_DPI));
Print("Max Bars: ", TerminalInfoInteger(TERMINAL_MAXBARS));
Print("Code Page: ", TerminalInfoInteger(TERMINAL_CODEPAGE));
Print("Community Account: ", TerminalInfoInteger(TERMINAL_COMMUNITY_ACCOUNT) ? "Yes" : "No");
Print("Connected: ", TerminalInfoInteger(TERMINAL_CONNECTED) ? "Yes" : "No");
Print("Trade Allowed: ", TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) ? "Yes" : "No");
}
//+------------------------------------------------------------------+
//| Get CPU cores |
//+------------------------------------------------------------------+
int CUtilitiesV71::GetCPUCores()
{
return (int)TerminalInfoInteger(TERMINAL_CPU_CORES);
}
//+------------------------------------------------------------------+
//| Get available memory |
//+------------------------------------------------------------------+
long CUtilitiesV71::GetAvailableMemory()
{
return TerminalInfoInteger(TERMINAL_MEMORY_AVAILABLE);
}
//+------------------------------------------------------------------+
//| Array mean calculation |
//+------------------------------------------------------------------+
template<typename T>
double CUtilitiesV71::ArrayMean(const T &array[])
{
int size = ArraySize(array);
if(size == 0) return 0;
double sum = 0;
for(int i = 0; i < size; i++)
sum += array[i];
return sum / size;
}
//+------------------------------------------------------------------+
//| Array standard deviation |
//+------------------------------------------------------------------+
template<typename T>
double CUtilitiesV71::ArrayStdDev(const T &array[])
{
int size = ArraySize(array);
if(size <= 1) return 0;
double mean = ArrayMean(array);
double sum_sq = 0;
for(int i = 0; i < size; i++)
sum_sq += MathPow(array[i] - mean, 2);
return MathSqrt(sum_sq / (size - 1));
}
//+------------------------------------------------------------------+
//| Close all files |
//+------------------------------------------------------------------+
void CUtilitiesV71::CloseAllFiles()
{
if(m_log_handle != INVALID_HANDLE)
{
FileClose(m_log_handle);
m_log_handle = INVALID_HANDLE;
}
if(m_report_handle != INVALID_HANDLE)
{
FileClose(m_report_handle);
m_report_handle = INVALID_HANDLE;
}
}
//+------------------------------------------------------------------+
//| Flush all logs |
//+------------------------------------------------------------------+
void CUtilitiesV71::FlushLogs()
{
if(m_buffered_logging)
FlushLogBuffer();
if(m_log_handle != INVALID_HANDLE)
FileFlush(m_log_handle);
}
//+------------------------------------------------------------------+
//| Standard initialization (compatibility) |
//+------------------------------------------------------------------+
bool CUtilitiesV71::Initialize(ENUM_LOG_LEVEL level)
{
return InitializeOptimized(level, false);
}
#endif // UTILITIES_V71_MQH