mql5/Experts/Advisors/Modules/Utilities.mqh
darashikoh 63fab7676b The complete Utilities.mqh now includes all the missing implementations:
GetMA() - Moving average calculation
GetBollingerBand() - Bollinger band values
CalculateRiskAmount() - Risk amount from balance and percent
CalculatePipValue() - Pip value calculation
IsSessionActive() - Trading session checking
GetBarTime() - Bar time retrieval
TimeframeToString() - Timeframe conversion
SaveToFile() and LoadFromFile() - File I/O operations
All other utility functions
2025-08-27 16:22:33 +01:00

688 lines
No EOL
26 KiB
MQL5

//+------------------------------------------------------------------+
//| Utilities.mqh |
//| Utility Functions Module 2.3 |
//| Fixed: Remove Duplicates & Add Missing Members |
//+------------------------------------------------------------------+
#ifndef UTILITIES_MQH
#define UTILITIES_MQH
#include <Object.mqh>
#include <Files/FileTxt.mqh>
#include <Indicators/Indicators.mqh>
#include "DataTypes.mqh"
//+------------------------------------------------------------------+
//| Log Levels |
//+------------------------------------------------------------------+
enum ENUM_LOG_LEVEL
{
LOG_DEBUG = 0,
LOG_INFO = 1,
LOG_WARNING = 2,
LOG_ERROR = 3,
LOG_CRITICAL = 4
};
//+------------------------------------------------------------------+
//| Utilities Class |
//+------------------------------------------------------------------+
class CUtilities : public CObject
{
private:
// Logging
ENUM_LOG_LEVEL m_log_level;
string m_log_prefix;
int m_log_handle;
bool m_save_log;
string m_log_filename;
// Performance tracking
ulong m_last_tick;
double m_tick_duration;
// ATR Indicator
int m_atr_handle;
int m_atr_period;
public:
// Constructor/Destructor
CUtilities();
~CUtilities();
// Initialization
bool Initialize(ENUM_LOG_LEVEL level = LOG_INFO);
void SetLogLevel(ENUM_LOG_LEVEL level) { m_log_level = level; }
void EnableFileLogging(bool enable) { m_save_log = enable; }
// Logging methods
void Log(string message, ENUM_LOG_LEVEL level = LOG_INFO);
void LogTrade(const TradeInfo &trade);
void LogExternalTrade(ulong ticket, string action);
void LogRiskEvent(ENUM_RISK_EVENT event, string details);
void LogPerformance(const PerformanceMetrics &metrics);
// Report generation
string GenerateReport(const PerformanceMetrics &metrics);
string GenerateReportEnhanced(const PerformanceMetrics &metrics,
int external_count, double external_profit);
void SaveTradeHistory(const TradeInfo &trades[], int count);
// Technical indicators
double GetATR(int period);
double GetRSI(string symbol, ENUM_TIMEFRAMES timeframe, int period);
double GetMA(string symbol, ENUM_TIMEFRAMES timeframe, int period,
ENUM_MA_METHOD method, ENUM_APPLIED_PRICE price);
double GetBollingerBand(string symbol, ENUM_TIMEFRAMES timeframe,
int period, double deviation, int band_type);
// Price calculations
double NormalizePrice(double price, string symbol);
double NormalizeLot(double lot, string symbol);
double CalculatePipValue(string symbol, double lot_size);
double CalculateSpread(string symbol);
int GetDigits(string symbol) { return (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); }
// Risk calculations
double CalculatePositionSize(double risk_amount, double stop_distance, string symbol);
double CalculateRiskAmount(double balance, double risk_percent);
double CalculateRiskReward(double entry, double sl, double tp, ENUM_ORDER_TYPE type);
// Time functions
bool IsMarketOpen();
bool IsSessionActive(string session_name);
datetime GetBarTime(string symbol, ENUM_TIMEFRAMES timeframe, int shift);
int GetDayOfWeek() { MqlDateTime dt; TimeCurrent(dt); return dt.day_of_week; }
// String utilities
string FormatMoney(double amount);
string FormatPercent(double percent);
string OrderTypeToString(ENUM_ORDER_TYPE type);
string TimeframeToString(ENUM_TIMEFRAMES tf);
// File operations
bool SaveToFile(string filename, string content);
string LoadFromFile(string filename);
void CloseExternalLog();
// Performance monitoring
void StartTimer() { m_last_tick = GetMicrosecondCount(); }
double GetElapsedTime() { return (GetMicrosecondCount() - m_last_tick) / 1000.0; }
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CUtilities::CUtilities()
{
m_log_level = LOG_INFO;
m_log_prefix = "EA";
m_log_handle = INVALID_HANDLE;
m_save_log = false;
m_log_filename = "";
m_last_tick = 0;
m_tick_duration = 0;
m_atr_handle = INVALID_HANDLE;
m_atr_period = 14;
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CUtilities::~CUtilities()
{
if(m_log_handle != INVALID_HANDLE)
FileClose(m_log_handle);
if(m_atr_handle != INVALID_HANDLE)
IndicatorRelease(m_atr_handle);
}
//+------------------------------------------------------------------+
//| Initialize |
//+------------------------------------------------------------------+
bool CUtilities::Initialize(ENUM_LOG_LEVEL level)
{
m_log_level = level;
// Create log file if enabled
if(m_save_log)
{
m_log_filename = StringFormat("EA_Log_%s.txt", TimeToString(TimeCurrent(), TIME_DATE));
m_log_handle = FileOpen(m_log_filename, FILE_WRITE|FILE_TXT|FILE_ANSI);
if(m_log_handle == INVALID_HANDLE)
{
m_save_log = false;
Print("Failed to create log file");
}
}
// Initialize ATR indicator
m_atr_handle = iATR(_Symbol, PERIOD_CURRENT, m_atr_period);
if(m_atr_handle == INVALID_HANDLE)
{
Log("Failed to initialize ATR indicator", LOG_ERROR);
return false;
}
Log("Utilities initialized successfully", LOG_INFO);
return true;
}
//+------------------------------------------------------------------+
//| Main Logging Function |
//+------------------------------------------------------------------+
void CUtilities::Log(string message, ENUM_LOG_LEVEL level)
{
if(level < m_log_level) return;
string level_str = "";
switch(level)
{
case LOG_DEBUG: level_str = "DEBUG"; break;
case LOG_INFO: level_str = "INFO"; break;
case LOG_WARNING: level_str = "WARN"; break;
case LOG_ERROR: level_str = "ERROR"; break;
case LOG_CRITICAL: level_str = "CRITICAL"; break;
}
string full_message = StringFormat("[%s] [%s] %s: %s",
TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS),
level_str, m_log_prefix, message);
// Print to terminal
Print(full_message);
// Save to file if enabled
if(m_save_log && m_log_handle != INVALID_HANDLE)
{
FileWriteString(m_log_handle, full_message + "\n");
FileFlush(m_log_handle);
}
}
//+------------------------------------------------------------------+
//| Log Trade Information |
//+------------------------------------------------------------------+
void CUtilities::LogTrade(const TradeInfo &trade)
{
string message = StringFormat("Trade %d: %s %s %.2f @ %.5f, SL=%.5f, TP=%.5f, P/L=%.2f",
trade.ticket,
OrderTypeToString(trade.type),
trade.symbol,
trade.volume,
trade.entry_price,
trade.sl,
trade.tp,
trade.profit);
Log(message, LOG_INFO);
}
//+------------------------------------------------------------------+
//| Log External Trade |
//+------------------------------------------------------------------+
void CUtilities::LogExternalTrade(ulong ticket, string action)
{
string message = StringFormat("External Trade %d: %s", ticket, action);
Log(message, LOG_INFO);
}
//+------------------------------------------------------------------+
//| Log Risk Event |
//+------------------------------------------------------------------+
void CUtilities::LogRiskEvent(ENUM_RISK_EVENT event, string details)
{
string event_str = "";
switch(event)
{
case RISK_OVERLEVERAGED: event_str = "Overleveraged"; break;
case RISK_CORRELATION_HIGH: event_str = "High Correlation"; break;
case RISK_DRAWDOWN_LIMIT: event_str = "Drawdown Limit"; break;
case RISK_DAILY_LOSS_LIMIT: event_str = "Daily Loss Limit"; break;
case RISK_MARGIN_CALL: event_str = "Margin Call Risk"; break;
case RISK_VOLATILITY_SPIKE: event_str = "Volatility Spike"; break;
case RISK_LIQUIDITY_ISSUE: event_str = "Liquidity Issue"; break;
default: event_str = "Unknown Risk"; break;
}
string message = StringFormat("Risk Event: %s - %s", event_str, details);
Log(message, LOG_WARNING);
}
//+------------------------------------------------------------------+
//| Log Performance Metrics |
//+------------------------------------------------------------------+
void CUtilities::LogPerformance(const PerformanceMetrics &metrics)
{
string message = StringFormat(
"Performance: Trades=%d, Win Rate=%.1f%%, PF=%.2f, DD=%.2f%%",
metrics.total_trades,
metrics.win_rate,
metrics.profit_factor,
metrics.max_drawdown
);
Log(message, LOG_INFO);
}
//+------------------------------------------------------------------+
//| Generate Performance Report |
//+------------------------------------------------------------------+
string CUtilities::GenerateReport(const PerformanceMetrics &metrics)
{
string report = "\n========== PERFORMANCE REPORT ==========\n";
report += StringFormat("Total Profit/Loss: %s\n", FormatMoney(metrics.total_profit));
report += StringFormat("Total Trades: %d\n", metrics.total_trades);
report += StringFormat("Winning Trades: %d (%.1f%%)\n",
metrics.winning_trades, metrics.win_rate);
report += StringFormat("Losing Trades: %d\n", metrics.losing_trades);
report += StringFormat("Average Win: %s\n", FormatMoney(metrics.average_win));
report += StringFormat("Average Loss: %s\n", FormatMoney(metrics.average_loss));
report += StringFormat("Profit Factor: %.2f\n", metrics.profit_factor);
report += StringFormat("Max Drawdown: %.2f%%\n", metrics.max_drawdown);
report += StringFormat("Current Drawdown: %.2f%%\n", metrics.current_drawdown);
report += StringFormat("Sharpe Ratio: %.2f\n", metrics.sharpe_ratio);
report += StringFormat("Expected Value: %s\n", FormatMoney(metrics.expected_value));
report += "==========================================\n";
return report;
}
//+------------------------------------------------------------------+
//| Generate Enhanced Report with External Trades |
//+------------------------------------------------------------------+
string CUtilities::GenerateReportEnhanced(const PerformanceMetrics &metrics,
int external_count, double external_profit)
{
string report = GenerateReport(metrics);
// Add external trade information
report += "\n========== EXTERNAL TRADES ==========\n";
report += StringFormat("External Positions: %d\n", external_count);
report += StringFormat("External P/L: %s\n", FormatMoney(external_profit));
report += StringFormat("Total Combined P/L: %s\n",
FormatMoney(metrics.total_profit + external_profit));
report += "======================================\n";
return report;
}
//+------------------------------------------------------------------+
//| Save Trade History to File |
//+------------------------------------------------------------------+
void CUtilities::SaveTradeHistory(const TradeInfo &trades[], int count)
{
string filename = StringFormat("TradeHistory_%s.csv",
TimeToString(TimeCurrent(), TIME_DATE));
int handle = FileOpen(filename, FILE_WRITE|FILE_CSV);
if(handle == INVALID_HANDLE)
{
Log("Failed to create trade history file", LOG_ERROR);
return;
}
// Write header
FileWrite(handle, "Ticket", "Symbol", "Type", "Volume", "Entry", "Exit",
"SL", "TP", "Profit", "Open Time", "Close Time");
// Write trade data
for(int i = 0; i < count; i++)
{
FileWrite(handle,
trades[i].ticket,
trades[i].symbol,
OrderTypeToString(trades[i].type),
trades[i].volume,
trades[i].entry_price,
trades[i].current_price,
trades[i].sl,
trades[i].tp,
trades[i].profit,
TimeToString(trades[i].open_time),
TimeToString(trades[i].last_update));
}
FileClose(handle);
Log(StringFormat("Trade history saved to %s", filename), LOG_INFO);
}
//+------------------------------------------------------------------+
//| Get ATR Value |
//+------------------------------------------------------------------+
double CUtilities::GetATR(int period)
{
if(period != m_atr_period)
{
// Reinitialize with new period
if(m_atr_handle != INVALID_HANDLE)
IndicatorRelease(m_atr_handle);
m_atr_period = period;
m_atr_handle = iATR(_Symbol, PERIOD_CURRENT, m_atr_period);
}
if(m_atr_handle == INVALID_HANDLE) return 0;
double atr_buffer[1];
if(CopyBuffer(m_atr_handle, 0, 0, 1, atr_buffer) <= 0) return 0;
return atr_buffer[0];
}
//+------------------------------------------------------------------+
//| Get RSI Value |
//+------------------------------------------------------------------+
double CUtilities::GetRSI(string symbol, ENUM_TIMEFRAMES timeframe, int period)
{
int handle = iRSI(symbol, timeframe, period, PRICE_CLOSE);
if(handle == INVALID_HANDLE) return 50; // Return neutral value
double rsi_buffer[1];
if(CopyBuffer(handle, 0, 0, 1, rsi_buffer) <= 0)
{
IndicatorRelease(handle);
return 50;
}
IndicatorRelease(handle);
return rsi_buffer[0];
}
//+------------------------------------------------------------------+
//| Get Moving Average Value |
//+------------------------------------------------------------------+
double CUtilities::GetMA(string symbol, ENUM_TIMEFRAMES timeframe, int period,
ENUM_MA_METHOD method, ENUM_APPLIED_PRICE price)
{
int handle = iMA(symbol, timeframe, period, 0, method, price);
if(handle == INVALID_HANDLE) return 0;
double ma_buffer[1];
if(CopyBuffer(handle, 0, 0, 1, ma_buffer) <= 0)
{
IndicatorRelease(handle);
return 0;
}
IndicatorRelease(handle);
return ma_buffer[0];
}
//+------------------------------------------------------------------+
//| Get Bollinger Band Value |
//+------------------------------------------------------------------+
double CUtilities::GetBollingerBand(string symbol, ENUM_TIMEFRAMES timeframe,
int period, double deviation, int band_type)
{
int handle = iBands(symbol, timeframe, period, 0, deviation, PRICE_CLOSE);
if(handle == INVALID_HANDLE) return 0;
double band_buffer[1];
// band_type: 0=middle, 1=upper, 2=lower
if(CopyBuffer(handle, band_type, 0, 1, band_buffer) <= 0)
{
IndicatorRelease(handle);
return 0;
}
IndicatorRelease(handle);
return band_buffer[0];
}
//+------------------------------------------------------------------+
//| Calculate Position Size |
//+------------------------------------------------------------------+
double CUtilities::CalculatePositionSize(double risk_amount, double stop_distance, string symbol)
{
if(stop_distance <= 0) return 0;
double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
if(tick_value == 0 || tick_size == 0) return 0;
double lot_size = (risk_amount * tick_size) / (stop_distance * tick_value);
return NormalizeLot(lot_size, symbol);
}
//+------------------------------------------------------------------+
//| Calculate Risk Amount |
//+------------------------------------------------------------------+
double CUtilities::CalculateRiskAmount(double balance, double risk_percent)
{
return balance * risk_percent / 100;
}
//+------------------------------------------------------------------+
//| Calculate Risk Reward Ratio |
//+------------------------------------------------------------------+
double CUtilities::CalculateRiskReward(double entry, double sl, double tp, ENUM_ORDER_TYPE type)
{
if(sl == 0 || tp == 0) return 0;
double risk = MathAbs(entry - sl);
double reward = MathAbs(tp - entry);
if(risk == 0) return 0;
return reward / risk;
}
//+------------------------------------------------------------------+
//| Normalize Price |
//+------------------------------------------------------------------+
double CUtilities::NormalizePrice(double price, string symbol)
{
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
return NormalizeDouble(price, digits);
}
//+------------------------------------------------------------------+
//| Normalize Lot Size |
//+------------------------------------------------------------------+
double CUtilities::NormalizeLot(double lot, string symbol)
{
double min_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double max_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
double lot_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
if(lot < min_lot) lot = min_lot;
if(lot > max_lot) lot = max_lot;
int steps = (int)MathRound(lot / lot_step);
return steps * lot_step;
}
//+------------------------------------------------------------------+
//| Calculate Pip Value |
//+------------------------------------------------------------------+
double CUtilities::CalculatePipValue(string symbol, double lot_size)
{
double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double pip_size = (digits == 3 || digits == 5) ? tick_size * 10 : tick_size;
double pip_value = (tick_value * pip_size) / tick_size * lot_size;
return pip_value;
}
//+------------------------------------------------------------------+
//| Calculate Spread |
//+------------------------------------------------------------------+
double CUtilities::CalculateSpread(string symbol)
{
double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
if(point == 0) return 0;
return (ask - bid) / point;
}
//+------------------------------------------------------------------+
//| Check if Market is Open |
//+------------------------------------------------------------------+
bool CUtilities::IsMarketOpen()
{
datetime current = TimeCurrent();
MqlDateTime dt;
TimeToStruct(current, dt);
// Skip weekends
if(dt.day_of_week == 0 || dt.day_of_week == 6) return false;
// Check session times
long session_deals = SymbolInfoInteger(_Symbol, SYMBOL_SESSION_DEALS);
if(session_deals == 0) return false;
return true;
}
//+------------------------------------------------------------------+
//| Check if Session is Active |
//+------------------------------------------------------------------+
bool CUtilities::IsSessionActive(string session_name)
{
MqlDateTime current;
TimeToStruct(TimeCurrent(), current);
int hour = current.hour;
// Define session times (simplified)
if(session_name == "ASIAN")
return (hour >= 0 && hour < 9);
else if(session_name == "EUROPEAN")
return (hour >= 8 && hour < 17);
else if(session_name == "AMERICAN")
return (hour >= 13 && hour < 22);
else if(session_name == "PACIFIC")
return (hour >= 21 || hour < 6);
return false;
}
//+------------------------------------------------------------------+
//| Get Bar Time |
//+------------------------------------------------------------------+
datetime CUtilities::GetBarTime(string symbol, ENUM_TIMEFRAMES timeframe, int shift)
{
datetime time_array[1];
if(CopyTime(symbol, timeframe, shift, 1, time_array) > 0)
return time_array[0];
return 0;
}
//+------------------------------------------------------------------+
//| Format Money |
//+------------------------------------------------------------------+
string CUtilities::FormatMoney(double amount)
{
string sign = amount < 0 ? "-" : "";
amount = MathAbs(amount);
return StringFormat("%s$%.2f", sign, amount);
}
//+------------------------------------------------------------------+
//| Format Percent |
//+------------------------------------------------------------------+
string CUtilities::FormatPercent(double percent)
{
return StringFormat("%.2f%%", percent);
}
//+------------------------------------------------------------------+
//| Order Type to String |
//+------------------------------------------------------------------+
string CUtilities::OrderTypeToString(ENUM_ORDER_TYPE type)
{
switch(type)
{
case ORDER_TYPE_BUY: return "BUY";
case ORDER_TYPE_SELL: return "SELL";
case ORDER_TYPE_BUY_LIMIT: return "BUY LIMIT";
case ORDER_TYPE_SELL_LIMIT: return "SELL LIMIT";
case ORDER_TYPE_BUY_STOP: return "BUY STOP";
case ORDER_TYPE_SELL_STOP: return "SELL STOP";
default: return "UNKNOWN";
}
}
//+------------------------------------------------------------------+
//| Timeframe to String |
//+------------------------------------------------------------------+
string CUtilities::TimeframeToString(ENUM_TIMEFRAMES tf)
{
switch(tf)
{
case PERIOD_M1: return "M1";
case PERIOD_M5: return "M5";
case PERIOD_M15: return "M15";
case PERIOD_M30: return "M30";
case PERIOD_H1: return "H1";
case PERIOD_H4: return "H4";
case PERIOD_D1: return "D1";
case PERIOD_W1: return "W1";
case PERIOD_MN1: return "MN1";
default: return "Current";
}
}
//+------------------------------------------------------------------+
//| Save Content to File |
//+------------------------------------------------------------------+
bool CUtilities::SaveToFile(string filename, string content)
{
int handle = FileOpen(filename, FILE_WRITE|FILE_TXT|FILE_ANSI);
if(handle == INVALID_HANDLE)
{
Log(StringFormat("Failed to open file %s for writing", filename), LOG_ERROR);
return false;
}
FileWriteString(handle, content);
FileClose(handle);
Log(StringFormat("Content saved to %s", filename), LOG_INFO);
return true;
}
//+------------------------------------------------------------------+
//| Load Content from File |
//+------------------------------------------------------------------+
string CUtilities::LoadFromFile(string filename)
{
int handle = FileOpen(filename, FILE_READ|FILE_TXT|FILE_ANSI);
if(handle == INVALID_HANDLE)
{
Log(StringFormat("Failed to open file %s for reading", filename), LOG_ERROR);
return "";
}
string content = "";
while(!FileIsEnding(handle))
{
content += FileReadString(handle) + "\n";
}
FileClose(handle);
return content;
}
//+------------------------------------------------------------------+
//| Close External Log File |
//+------------------------------------------------------------------+
void CUtilities::CloseExternalLog()
{
if(m_log_handle != INVALID_HANDLE)
{
FileClose(m_log_handle);
m_log_handle = INVALID_HANDLE;
Log("External log file closed", LOG_INFO);
}
}
#endif // UTILITIES_MQH