Files
dbasket-EA/MQL5/Include/DBasket/DBasket_Logger.mqh
T

360 lines
13 KiB
Plaintext

//+------------------------------------------------------------------+
//| DBasket_Logger.mqh |
//| D-Basket Correlation Hedging EA |
//| Logging Utility |
//+------------------------------------------------------------------+
#property copyright "D-Basket EA"
#property version "1.00"
#property strict
#ifndef DBASKET_LOGGER_MQH
#define DBASKET_LOGGER_MQH
#include "DBasket_Defines.mqh"
//+------------------------------------------------------------------+
//| Logger Class |
//| Centralized logging with configurable levels and file output |
//+------------------------------------------------------------------+
class CLogger
{
private:
ENUM_LOG_LEVEL m_logLevel; // Current log level
bool m_logToFile; // Enable file logging
int m_fileHandle; // Log file handle
string m_logFileName; // Log file name
bool m_isInitialized; // Initialization status
// Format timestamp for logging
string FormatTimestamp(datetime time)
{
return TimeToString(time, TIME_DATE | TIME_SECONDS);
}
// Get log level string
string GetLevelString(ENUM_LOG_LEVEL level)
{
switch(level)
{
case LOG_LEVEL_ERROR: return "[ERROR]";
case LOG_LEVEL_WARNING: return "[WARN] ";
case LOG_LEVEL_INFO: return "[INFO] ";
case LOG_LEVEL_DEBUG: return "[DEBUG]";
default: return "[????] ";
}
}
// Write to file if enabled
void WriteToFile(string message)
{
if(!m_logToFile || m_fileHandle == INVALID_HANDLE)
return;
FileWriteString(m_fileHandle, message + "\n");
FileFlush(m_fileHandle);
}
public:
// Constructor
CLogger()
{
m_logLevel = LOG_LEVEL_INFO;
m_logToFile = false;
m_fileHandle = INVALID_HANDLE;
m_logFileName = "";
m_isInitialized = false;
}
// Destructor
~CLogger()
{
Deinitialize();
}
//+------------------------------------------------------------------+
//| Initialize logger |
//+------------------------------------------------------------------+
bool Initialize(ENUM_LOG_LEVEL level, bool logToFile = false)
{
m_logLevel = level;
m_logToFile = logToFile;
if(logToFile)
{
// Create log file with timestamp
m_logFileName = LOG_FILE_PREFIX + TimeToString(TimeCurrent(), TIME_DATE) + ".log";
StringReplace(m_logFileName, ".", "_");
StringReplace(m_logFileName, ":", "_");
m_logFileName = m_logFileName + ".log";
m_fileHandle = FileOpen(m_logFileName, FILE_WRITE | FILE_READ | FILE_TXT | FILE_SHARE_READ);
if(m_fileHandle == INVALID_HANDLE)
{
Print("Logger: Failed to open log file: ", m_logFileName, " Error: ", GetLastError());
m_logToFile = false;
}
else
{
// Move to end of file for appending
FileSeek(m_fileHandle, 0, SEEK_END);
}
}
m_isInitialized = true;
Info("Logger initialized - Level: " + EnumToString(level) + ", File: " + (logToFile ? m_logFileName : "Disabled"));
return true;
}
//+------------------------------------------------------------------+
//| Deinitialize logger |
//+------------------------------------------------------------------+
void Deinitialize()
{
if(m_fileHandle != INVALID_HANDLE)
{
FileClose(m_fileHandle);
m_fileHandle = INVALID_HANDLE;
}
m_isInitialized = false;
}
//+------------------------------------------------------------------+
//| Set log level |
//+------------------------------------------------------------------+
void SetLogLevel(ENUM_LOG_LEVEL level)
{
m_logLevel = level;
}
//+------------------------------------------------------------------+
//| Core logging function |
//+------------------------------------------------------------------+
void Log(ENUM_LOG_LEVEL level, string message)
{
// Check if this level should be logged
if(level > m_logLevel)
return;
// Format message
string timestamp = FormatTimestamp(TimeCurrent());
string levelStr = GetLevelString(level);
string fullMessage = timestamp + " " + levelStr + " " + message;
// Output to terminal
Print(fullMessage);
// Output to file if enabled
WriteToFile(fullMessage);
}
//+------------------------------------------------------------------+
//| Convenience methods |
//+------------------------------------------------------------------+
void Error(string message)
{
Log(LOG_LEVEL_ERROR, message);
}
void Warning(string message)
{
Log(LOG_LEVEL_WARNING, message);
}
void Info(string message)
{
Log(LOG_LEVEL_INFO, message);
}
void Debug(string message)
{
Log(LOG_LEVEL_DEBUG, message);
}
//+------------------------------------------------------------------+
//| Log with formatting (variadic-like using overloads) |
//+------------------------------------------------------------------+
void ErrorF(string format, string arg1)
{
string msg = format;
StringReplace(msg, "%s", arg1);
Error(msg);
}
void ErrorF(string format, string arg1, string arg2)
{
string msg = format;
StringReplace(msg, "%s", arg1);
StringReplace(msg, "%s", arg2);
Error(msg);
}
void InfoF(string format, string arg1)
{
string msg = format;
StringReplace(msg, "%s", arg1);
Info(msg);
}
void InfoF(string format, double value)
{
string msg = format;
StringReplace(msg, "%.2f", DoubleToString(value, 2));
StringReplace(msg, "%.4f", DoubleToString(value, 4));
StringReplace(msg, "%.5f", DoubleToString(value, 5));
StringReplace(msg, "%f", DoubleToString(value, 5));
Info(msg);
}
void DebugF(string format, string arg1)
{
string msg = format;
StringReplace(msg, "%s", arg1);
Debug(msg);
}
void DebugF(string format, double value)
{
string msg = format;
StringReplace(msg, "%.2f", DoubleToString(value, 2));
StringReplace(msg, "%.4f", DoubleToString(value, 4));
StringReplace(msg, "%.5f", DoubleToString(value, 5));
StringReplace(msg, "%f", DoubleToString(value, 5));
Debug(msg);
}
//+------------------------------------------------------------------+
//| Log trade error with context |
//+------------------------------------------------------------------+
void TradeError(string operation, string symbol, int errorCode)
{
string errorDesc = ErrorDescription(errorCode);
Error("Trade Error - Op: " + operation +
", Symbol: " + symbol +
", Code: " + IntegerToString(errorCode) +
", Desc: " + errorDesc);
}
//+------------------------------------------------------------------+
//| Log basket state |
//+------------------------------------------------------------------+
void LogBasketOpen(int basketID, ENUM_BASKET_SIGNAL direction, double zScore, double correlation)
{
string dirStr = (direction == SIGNAL_LONG_BASKET) ? "LONG" : "SHORT";
Info("Basket #" + IntegerToString(basketID) + " OPENED - " +
"Direction: " + dirStr +
", Z-Score: " + DoubleToString(zScore, 2) +
", Correlation: " + DoubleToString(correlation, 4));
}
void LogBasketClose(int basketID, ENUM_EXIT_REASON reason, double pl, int holdBars)
{
string reasonStr;
switch(reason)
{
case EXIT_MEAN_REVERSION: reasonStr = "Mean Reversion"; break;
case EXIT_TAKE_PROFIT: reasonStr = "Take Profit"; break;
case EXIT_STOP_LOSS: reasonStr = "Stop Loss"; break;
case EXIT_MAX_TIME: reasonStr = "Max Time"; break;
case EXIT_CORRELATION_BREAK: reasonStr = "Correlation Break"; break;
case EXIT_RISK_LIMIT: reasonStr = "Risk Limit"; break;
case EXIT_EMERGENCY: reasonStr = "Emergency"; break;
default: reasonStr = "Manual"; break;
}
Info("Basket #" + IntegerToString(basketID) + " CLOSED - " +
"Reason: " + reasonStr +
", P/L: " + DoubleToString(pl, 2) +
", Bars Held: " + IntegerToString(holdBars));
}
//+------------------------------------------------------------------+
//| Convert error code to description |
//+------------------------------------------------------------------+
string ErrorDescription(int errorCode)
{
switch(errorCode)
{
case 0: return "No error";
case 10004: return "Requote";
case 10006: return "Request rejected";
case 10007: return "Request canceled by trader";
case 10010: return "Only part of request completed";
case 10011: return "Request processing error";
case 10012: return "Request canceled by timeout";
case 10013: return "Invalid request";
case 10014: return "Invalid volume";
case 10015: return "Invalid price";
case 10016: return "Invalid stops";
case 10017: return "Trade disabled";
case 10018: return "Market closed";
case 10019: return "Insufficient funds";
case 10020: return "Prices changed";
case 10021: return "No quotes";
case 10022: return "Invalid order expiration";
case 10023: return "Order state changed";
case 10024: return "Too many requests";
case 10025: return "No changes in request";
case 10026: return "Autotrading disabled by server";
case 10027: return "Autotrading disabled by client";
case 10028: return "Request locked for processing";
case 10029: return "Order or position frozen";
case 10030: return "Invalid fill type";
case 10031: return "No connection with trade server";
case 10032: return "Operation allowed only for live accounts";
case 10033: return "Pending orders limit reached";
case 10034: return "Order or position volume limit reached";
case 10035: return "Invalid or prohibited order type";
case 10036: return "Position with specified POSITION_IDENTIFIER already closed";
case 10038: return "Close volume exceeds current position volume";
case 10039: return "Close order already exists";
case 10040: return "Limit of pending orders reached";
case 10041: return "Order or position modification rejected";
case 10042: return "Request rejected by trade context busy";
case 10043: return "Only part of positions closed";
case 10044: return "Position limit reached";
default: return "Unknown error (" + IntegerToString(errorCode) + ")";
}
}
//+------------------------------------------------------------------+
//| Log initialization summary |
//+------------------------------------------------------------------+
void LogInitSummary(string eaName, string version, double balance, int leverage, string server)
{
Info("==================================================");
Info("= " + eaName + " v" + version);
Info("==================================================");
Info("Account Balance: " + DoubleToString(balance, 2));
Info("Leverage: 1:" + IntegerToString(leverage));
Info("Server: " + server);
Info("==================================================");
}
//+------------------------------------------------------------------+
//| Log correlation data |
//+------------------------------------------------------------------+
void LogCorrelationData(const CorrelationData &data)
{
if(m_logLevel < LOG_LEVEL_DEBUG)
return;
Debug("Correlation Data - " +
"Corr: " + DoubleToString(data.corrAUDCAD_NZDCAD, 4) +
", Ratio: " + DoubleToString(data.syntheticRatio, 5) +
", Actual: " + DoubleToString(data.actualAUDNZD, 5) +
", Spread: " + DoubleToString(data.spreadValue, 5) +
", Z: " + DoubleToString(data.spreadZScore, 2) +
", Valid: " + (data.isValid ? "Yes" : "No"));
}
};
//+------------------------------------------------------------------+
//| Global logger instance |
//+------------------------------------------------------------------+
CLogger Logger;
#endif // DBASKET_LOGGER_MQH
//+------------------------------------------------------------------+