mirror of
https://github.com/dinethlive/dbasket-EA.git
synced 2026-08-18 21:28:15 +00:00
360 lines
13 KiB
Plaintext
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
|
|
//+------------------------------------------------------------------+
|