mql5/Experts/Advisors/Modules_optimised/SymbolManager.mqh

598 lines
19 KiB
MQL5
Raw Permalink Normal View History

2025-08-08 20:32:34 +01:00
//+------------------------------------------------------------------+
//| SymbolManager.mqh v1.0 |
//| Centralized Symbol Management & Caching |
//| Optimized for Multi-Symbol Trading |
//+------------------------------------------------------------------+
#ifndef SYMBOL_MANAGER_MQH
#define SYMBOL_MANAGER_MQH
#include "DataTypes.mqh"
#include <Trade/SymbolInfo.mqh>
//+------------------------------------------------------------------+
//| Symbol cache entry structure |
//+------------------------------------------------------------------+
struct SymbolCacheEntry
{
string symbol;
CSymbolInfo *info;
double point;
int digits;
double tick_value;
double tick_size;
double min_lot;
double max_lot;
double lot_step;
double min_stop_level;
double contract_size;
double margin_rate;
ENUM_SYMBOL_TRADE_EXECUTION execution_mode;
datetime last_update;
bool is_valid;
//--- Market hours
datetime session_open;
datetime session_close;
bool trading_allowed;
//--- Cached calculations
double pip_value;
double normalized_stop_level;
//--- Performance metrics
int access_count;
datetime last_access;
};
//+------------------------------------------------------------------+
//| Symbol Manager Singleton Class |
//+------------------------------------------------------------------+
class CSymbolManager
{
private:
static CSymbolManager *m_instance;
//--- Symbol cache
SymbolCacheEntry m_cache[];
int m_cache_size;
int m_max_cache_size;
//--- Update intervals
int m_cache_ttl_seconds;
datetime m_last_cleanup;
//--- Statistics
int m_cache_hits;
int m_cache_misses;
//--- Private constructor for singleton
CSymbolManager();
//--- Helper methods
int FindCacheIndex(string symbol);
void UpdateCacheEntry(SymbolCacheEntry &entry, string symbol);
void CleanupCache();
double CalculatePipValue(string symbol);
public:
//--- Singleton access
static CSymbolManager* GetInstance();
static void DeleteInstance();
//--- Destructor
~CSymbolManager();
//--- Symbol information
bool GetSymbolInfo(string symbol, SymbolCacheEntry &info);
CSymbolInfo* GetSymbolInfoObject(string symbol);
//--- Quick access methods
double GetPoint(string symbol);
int GetDigits(string symbol);
double GetTickValue(string symbol);
double GetMinLot(string symbol);
double GetMaxLot(string symbol);
double GetLotStep(string symbol);
double GetMinStopLevel(string symbol);
double GetPipValue(string symbol);
double GetSpread(string symbol);
//--- Price operations
double NormalizePrice(string symbol, double price);
double NormalizeLots(string symbol, double lots);
double CalculateStopDistance(string symbol, double price1, double price2);
bool ValidateStopLevels(string symbol, double price, double sl, double tp);
//--- Multi-symbol operations
void RefreshAll();
int GetCachedSymbolCount() { return m_cache_size; }
void GetCachedSymbols(string &symbols[]);
//--- Market info
bool IsMarketOpen(string symbol);
double GetCurrentBid(string symbol);
double GetCurrentAsk(string symbol);
double GetCurrentPrice(string symbol, ENUM_POSITION_TYPE type);
//--- Statistics
void PrintStatistics();
double GetCacheHitRate() { return (m_cache_hits + m_cache_misses > 0) ?
(double)m_cache_hits / (m_cache_hits + m_cache_misses) * 100 : 0; }
};
//--- Static instance
CSymbolManager* CSymbolManager::m_instance = NULL;
//+------------------------------------------------------------------+
//| Get singleton instance |
//+------------------------------------------------------------------+
CSymbolManager* CSymbolManager::GetInstance()
{
if(m_instance == NULL)
m_instance = new CSymbolManager();
return m_instance;
}
//+------------------------------------------------------------------+
//| Delete singleton instance |
//+------------------------------------------------------------------+
void CSymbolManager::DeleteInstance()
{
if(m_instance != NULL)
{
delete m_instance;
m_instance = NULL;
}
}
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CSymbolManager::CSymbolManager()
{
m_cache_size = 0;
m_max_cache_size = 50;
m_cache_ttl_seconds = 60; // 1 minute cache
m_last_cleanup = 0;
m_cache_hits = 0;
m_cache_misses = 0;
ArrayResize(m_cache, m_max_cache_size);
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CSymbolManager::~CSymbolManager()
{
//--- Clean up symbol info objects
for(int i = 0; i < m_cache_size; i++)
{
if(m_cache[i].info != NULL)
{
delete m_cache[i].info;
m_cache[i].info = NULL;
}
}
}
//+------------------------------------------------------------------+
//| Get symbol information with caching |
//+------------------------------------------------------------------+
bool CSymbolManager::GetSymbolInfo(string symbol, SymbolCacheEntry &info)
{
//--- Find in cache
int index = FindCacheIndex(symbol);
if(index >= 0)
{
//--- Check if cache is still valid
if(TimeCurrent() - m_cache[index].last_update < m_cache_ttl_seconds)
{
info = m_cache[index];
m_cache[index].access_count++;
m_cache[index].last_access = TimeCurrent();
m_cache_hits++;
return true;
}
else
{
//--- Update existing entry
UpdateCacheEntry(m_cache[index], symbol);
info = m_cache[index];
m_cache_hits++;
return m_cache[index].is_valid;
}
}
//--- Not in cache, add new entry
m_cache_misses++;
//--- Check if need to cleanup old entries
if(m_cache_size >= m_max_cache_size ||
TimeCurrent() - m_last_cleanup > 300) // Cleanup every 5 minutes
{
CleanupCache();
}
//--- Add new entry
if(m_cache_size < m_max_cache_size)
{
UpdateCacheEntry(m_cache[m_cache_size], symbol);
if(m_cache[m_cache_size].is_valid)
{
info = m_cache[m_cache_size];
m_cache_size++;
return true;
}
}
return false;
}
//+------------------------------------------------------------------+
//| Find symbol in cache |
//+------------------------------------------------------------------+
int CSymbolManager::FindCacheIndex(string symbol)
{
for(int i = 0; i < m_cache_size; i++)
{
if(m_cache[i].symbol == symbol)
return i;
}
return -1;
}
//+------------------------------------------------------------------+
//| Update cache entry |
//+------------------------------------------------------------------+
void CSymbolManager::UpdateCacheEntry(SymbolCacheEntry &entry, string symbol)
{
//--- Create or update symbol info object
if(entry.info == NULL)
entry.info = new CSymbolInfo();
if(!entry.info.Name(symbol))
{
entry.is_valid = false;
return;
}
//--- Refresh rates
entry.info.RefreshRates();
//--- Update basic info
entry.symbol = symbol;
entry.point = entry.info.Point();
entry.digits = entry.info.Digits();
entry.tick_value = entry.info.TickValue();
entry.tick_size = entry.info.TickSize();
entry.min_lot = entry.info.LotsMin();
entry.max_lot = entry.info.LotsMax();
entry.lot_step = entry.info.LotsStep();
entry.min_stop_level = entry.info.StopsLevel() * entry.point;
entry.contract_size = entry.info.ContractSize();
entry.margin_rate = entry.info.MarginInitial();
entry.execution_mode = entry.info.TradeExecution();
//--- Calculate derived values
entry.pip_value = CalculatePipValue(symbol);
entry.normalized_stop_level = entry.min_stop_level * 1.1; // 10% buffer
//--- Market hours
entry.trading_allowed = entry.info.TradeMode() != SYMBOL_TRADE_MODE_DISABLED;
//--- Update timestamps
entry.last_update = TimeCurrent();
entry.is_valid = true;
//--- Initialize counters if new
if(entry.access_count == 0)
{
entry.access_count = 1;
entry.last_access = TimeCurrent();
}
}
//+------------------------------------------------------------------+
//| Calculate pip value |
//+------------------------------------------------------------------+
double CSymbolManager::CalculatePipValue(string symbol)
{
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
//--- Standard pip calculation
if(digits == 3 || digits == 5)
return point * 10;
else
return point;
}
//+------------------------------------------------------------------+
//| Cleanup old cache entries |
//+------------------------------------------------------------------+
void CSymbolManager::CleanupCache()
{
datetime current_time = TimeCurrent();
int new_size = 0;
//--- Keep frequently accessed and recent entries
for(int i = 0; i < m_cache_size; i++)
{
bool keep = false;
//--- Keep if recently accessed
if(current_time - m_cache[i].last_access < 300) // 5 minutes
keep = true;
//--- Keep if frequently accessed
if(m_cache[i].access_count > 10)
keep = true;
//--- Move entry if keeping
if(keep && new_size != i)
{
m_cache[new_size] = m_cache[i];
m_cache[i].info = NULL; // Prevent double delete
}
else if(!keep && m_cache[i].info != NULL)
{
delete m_cache[i].info;
m_cache[i].info = NULL;
}
if(keep) new_size++;
}
m_cache_size = new_size;
m_last_cleanup = current_time;
}
//+------------------------------------------------------------------+
//| Get symbol info object |
//+------------------------------------------------------------------+
CSymbolInfo* CSymbolManager::GetSymbolInfoObject(string symbol)
{
SymbolCacheEntry info;
if(GetSymbolInfo(symbol, info))
return info.info;
return NULL;
}
//+------------------------------------------------------------------+
//| Quick access methods |
//+------------------------------------------------------------------+
double CSymbolManager::GetPoint(string symbol)
{
SymbolCacheEntry info;
return GetSymbolInfo(symbol, info) ? info.point : 0;
}
int CSymbolManager::GetDigits(string symbol)
{
SymbolCacheEntry info;
return GetSymbolInfo(symbol, info) ? info.digits : 0;
}
double CSymbolManager::GetTickValue(string symbol)
{
SymbolCacheEntry info;
return GetSymbolInfo(symbol, info) ? info.tick_value : 0;
}
double CSymbolManager::GetMinLot(string symbol)
{
SymbolCacheEntry info;
return GetSymbolInfo(symbol, info) ? info.min_lot : 0.01;
}
double CSymbolManager::GetMaxLot(string symbol)
{
SymbolCacheEntry info;
return GetSymbolInfo(symbol, info) ? info.max_lot : 100;
}
double CSymbolManager::GetLotStep(string symbol)
{
SymbolCacheEntry info;
return GetSymbolInfo(symbol, info) ? info.lot_step : 0.01;
}
double CSymbolManager::GetMinStopLevel(string symbol)
{
SymbolCacheEntry info;
return GetSymbolInfo(symbol, info) ? info.normalized_stop_level : 0;
}
double CSymbolManager::GetPipValue(string symbol)
{
SymbolCacheEntry info;
return GetSymbolInfo(symbol, info) ? info.pip_value : 0;
}
double CSymbolManager::GetSpread(string symbol)
{
CSymbolInfo *info = GetSymbolInfoObject(symbol);
if(info != NULL)
{
info.RefreshRates();
return info.Spread() * info.Point();
}
return 0;
}
//+------------------------------------------------------------------+
//| Price operations |
//+------------------------------------------------------------------+
double CSymbolManager::NormalizePrice(string symbol, double price)
{
int digits = GetDigits(symbol);
return NormalizeDouble(price, digits);
}
double CSymbolManager::NormalizeLots(string symbol, double lots)
{
SymbolCacheEntry info;
if(!GetSymbolInfo(symbol, info))
return 0;
//--- Round to lot step
if(info.lot_step > 0)
lots = MathFloor(lots / info.lot_step) * info.lot_step;
//--- Apply limits
lots = MathMax(lots, info.min_lot);
lots = MathMin(lots, info.max_lot);
return NormalizeDouble(lots, 2);
}
double CSymbolManager::CalculateStopDistance(string symbol, double price1, double price2)
{
return MathAbs(price1 - price2);
}
bool CSymbolManager::ValidateStopLevels(string symbol, double price, double sl, double tp)
{
double min_stop = GetMinStopLevel(symbol);
//--- Check stop loss
if(sl > 0)
{
double sl_distance = MathAbs(price - sl);
if(sl_distance < min_stop)
return false;
}
//--- Check take profit
if(tp > 0)
{
double tp_distance = MathAbs(price - tp);
if(tp_distance < min_stop)
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Refresh all cached symbols |
//+------------------------------------------------------------------+
void CSymbolManager::RefreshAll()
{
for(int i = 0; i < m_cache_size; i++)
{
UpdateCacheEntry(m_cache[i], m_cache[i].symbol);
}
}
//+------------------------------------------------------------------+
//| Get list of cached symbols |
//+------------------------------------------------------------------+
void CSymbolManager::GetCachedSymbols(string &symbols[])
{
ArrayResize(symbols, m_cache_size);
for(int i = 0; i < m_cache_size; i++)
{
symbols[i] = m_cache[i].symbol;
}
}
//+------------------------------------------------------------------+
//| Check if market is open |
//+------------------------------------------------------------------+
bool CSymbolManager::IsMarketOpen(string symbol)
{
SymbolCacheEntry info;
if(!GetSymbolInfo(symbol, info))
return false;
return info.trading_allowed && info.info.SessionTrade(SYMBOL_SESSION_TRADE);
}
//+------------------------------------------------------------------+
//| Get current bid price |
//+------------------------------------------------------------------+
double CSymbolManager::GetCurrentBid(string symbol)
{
CSymbolInfo *info = GetSymbolInfoObject(symbol);
if(info != NULL)
{
info.RefreshRates();
return info.Bid();
}
return 0;
}
//+------------------------------------------------------------------+
//| Get current ask price |
//+------------------------------------------------------------------+
double CSymbolManager::GetCurrentAsk(string symbol)
{
CSymbolInfo *info = GetSymbolInfoObject(symbol);
if(info != NULL)
{
info.RefreshRates();
return info.Ask();
}
return 0;
}
//+------------------------------------------------------------------+
//| Get current price based on position type |
//+------------------------------------------------------------------+
double CSymbolManager::GetCurrentPrice(string symbol, ENUM_POSITION_TYPE type)
{
return (type == POSITION_TYPE_BUY) ? GetCurrentBid(symbol) : GetCurrentAsk(symbol);
}
//+------------------------------------------------------------------+
//| Print cache statistics |
//+------------------------------------------------------------------+
void CSymbolManager::PrintStatistics()
{
Print("=== Symbol Manager Statistics ===");
Print("Cached Symbols: ", m_cache_size);
Print("Cache Hits: ", m_cache_hits);
Print("Cache Misses: ", m_cache_misses);
Print("Hit Rate: ", DoubleToString(GetCacheHitRate(), 2), "%");
//--- Most accessed symbols
Print("\nMost Accessed Symbols:");
for(int i = 0; i < MathMin(5, m_cache_size); i++)
{
int max_access = 0;
int max_index = -1;
for(int j = 0; j < m_cache_size; j++)
{
if(m_cache[j].access_count > max_access)
{
bool already_printed = false;
for(int k = 0; k < i; k++)
{
if(m_cache[j].symbol == m_cache[k].symbol)
{
already_printed = true;
break;
}
}
if(!already_printed)
{
max_access = m_cache[j].access_count;
max_index = j;
}
}
}
if(max_index >= 0)
{
Print(" ", m_cache[max_index].symbol,
" - Accesses: ", m_cache[max_index].access_count);
}
}
}
#endif // SYMBOL_MANAGER_MQH