mql5/Shared Projects/ERMT-ML/Modules-ML8x/Utilities_v71.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

863 lines
No EOL
31 KiB
MQL5

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