653 lines
20 KiB
MQL5
653 lines
20 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| CEconomicCalendar.mqh - MQL5 Economic Calendar Integration |
|
||
|
|
//| Provides direct access to MQL5 Economic Calendar for real-time |
|
||
|
|
//| news event detection and impact analysis |
|
||
|
|
//| Based on MQL5 article: "Using the MQL5 Economic Calendar for News |
|
||
|
|
//| Filter (Part 3): Surviving Terminal Restarts During News Window" |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#ifndef CECONOMICCALENDAR_MQH
|
||
|
|
#define CECONOMICCALENDAR_MQH
|
||
|
|
|
||
|
|
#include "CErrorRecovery.mqh"
|
||
|
|
#include "CUnifiedFileIO.mqh"
|
||
|
|
|
||
|
|
// Event importance levels
|
||
|
|
enum ENUM_EVENT_IMPORTANCE
|
||
|
|
{
|
||
|
|
EVENT_IMPORTANCE_LOW = 0,
|
||
|
|
EVENT_IMPORTANCE_MEDIUM = 1,
|
||
|
|
EVENT_IMPORTANCE_HIGH = 2
|
||
|
|
};
|
||
|
|
|
||
|
|
// Event type
|
||
|
|
enum ENUM_EVENT_TYPE
|
||
|
|
{
|
||
|
|
EVENT_TYPE_NONE = 0,
|
||
|
|
EVENT_TYPE_ECONOMIC = 1,
|
||
|
|
EVENT_TYPE_POLITICAL = 2,
|
||
|
|
EVENT_TYPE_NATURAL = 3,
|
||
|
|
EVENT_TYPE_CENTRAL_BANK = 4
|
||
|
|
};
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Economic Event Structure |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
struct SEconomicEvent
|
||
|
|
{
|
||
|
|
long id;
|
||
|
|
string name;
|
||
|
|
string currency;
|
||
|
|
string country;
|
||
|
|
datetime event_time;
|
||
|
|
int importance; // 0=low, 1=medium, 2=high
|
||
|
|
double actual_value;
|
||
|
|
double forecast_value;
|
||
|
|
double previous_value;
|
||
|
|
bool has_actual;
|
||
|
|
bool has_forecast;
|
||
|
|
bool has_previous;
|
||
|
|
string event_type;
|
||
|
|
|
||
|
|
SEconomicEvent()
|
||
|
|
{
|
||
|
|
id = 0;
|
||
|
|
name = "";
|
||
|
|
currency = "";
|
||
|
|
country = "";
|
||
|
|
event_time = 0;
|
||
|
|
importance = 0;
|
||
|
|
actual_value = 0.0;
|
||
|
|
forecast_value = 0.0;
|
||
|
|
previous_value = 0.0;
|
||
|
|
has_actual = false;
|
||
|
|
has_forecast = false;
|
||
|
|
has_previous = false;
|
||
|
|
event_type = "";
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| News Impact Analysis Result |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
struct SNewsImpact
|
||
|
|
{
|
||
|
|
bool is_news_time;
|
||
|
|
int current_importance;
|
||
|
|
int upcoming_importance;
|
||
|
|
datetime next_event_time;
|
||
|
|
string next_event_name;
|
||
|
|
double volatility_prediction;
|
||
|
|
double confidence_adjustment;
|
||
|
|
string reason;
|
||
|
|
|
||
|
|
SNewsImpact()
|
||
|
|
{
|
||
|
|
is_news_time = false;
|
||
|
|
current_importance = 0;
|
||
|
|
upcoming_importance = 0;
|
||
|
|
next_event_time = 0;
|
||
|
|
next_event_name = "";
|
||
|
|
volatility_prediction = 0.0;
|
||
|
|
confidence_adjustment = 1.0;
|
||
|
|
reason = "";
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Economic Calendar Manager |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
class CEconomicCalendar
|
||
|
|
{
|
||
|
|
private:
|
||
|
|
static CEconomicCalendar* s_instance;
|
||
|
|
|
||
|
|
// Event cache
|
||
|
|
SEconomicEvent m_events[];
|
||
|
|
int m_event_count;
|
||
|
|
int m_max_events;
|
||
|
|
datetime m_last_update;
|
||
|
|
int m_update_interval_sec;
|
||
|
|
|
||
|
|
// Currency filter
|
||
|
|
string m_currencies[];
|
||
|
|
int m_currency_count;
|
||
|
|
|
||
|
|
// Configuration
|
||
|
|
int m_lookback_hours;
|
||
|
|
int m_lookahead_hours;
|
||
|
|
bool m_use_api;
|
||
|
|
bool m_use_fallback_file;
|
||
|
|
string m_fallback_filename;
|
||
|
|
|
||
|
|
// Statistics
|
||
|
|
int m_api_calls;
|
||
|
|
int m_cache_hits;
|
||
|
|
int m_cache_misses;
|
||
|
|
|
||
|
|
// Error recovery
|
||
|
|
CErrorRecovery* m_error_recovery;
|
||
|
|
CUnifiedFileIO* m_file_io;
|
||
|
|
|
||
|
|
// Market watch indices for fast lookup
|
||
|
|
int m_currency_handles[];
|
||
|
|
|
||
|
|
public:
|
||
|
|
CEconomicCalendar()
|
||
|
|
{
|
||
|
|
m_event_count = 0;
|
||
|
|
m_max_events = 500;
|
||
|
|
ArrayResize(m_events, m_max_events);
|
||
|
|
|
||
|
|
m_last_update = 0;
|
||
|
|
m_update_interval_sec = 300; // 5 minutes
|
||
|
|
|
||
|
|
m_currency_count = 0;
|
||
|
|
ArrayResize(m_currencies, 10);
|
||
|
|
|
||
|
|
m_lookback_hours = 2;
|
||
|
|
m_lookahead_hours = 24;
|
||
|
|
m_use_api = true;
|
||
|
|
m_use_fallback_file = true;
|
||
|
|
m_fallback_filename = "DualEA\\economic_calendar.json";
|
||
|
|
|
||
|
|
m_api_calls = 0;
|
||
|
|
m_cache_hits = 0;
|
||
|
|
m_cache_misses = 0;
|
||
|
|
|
||
|
|
m_error_recovery = CErrorRecovery::Instance();
|
||
|
|
m_file_io = CUnifiedFileIO::Instance();
|
||
|
|
}
|
||
|
|
|
||
|
|
~CEconomicCalendar()
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
static CEconomicCalendar* Instance()
|
||
|
|
{
|
||
|
|
if(s_instance == NULL)
|
||
|
|
s_instance = new CEconomicCalendar();
|
||
|
|
return s_instance;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void Destroy()
|
||
|
|
{
|
||
|
|
if(s_instance != NULL)
|
||
|
|
{
|
||
|
|
delete s_instance;
|
||
|
|
s_instance = NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Configuration |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void SetCurrencies(const string ¤cies[])
|
||
|
|
{
|
||
|
|
int count = ArraySize(currencies);
|
||
|
|
ArrayResize(m_currencies, count);
|
||
|
|
for(int i = 0; i < count; i++)
|
||
|
|
{
|
||
|
|
m_currencies[i] = currencies[i];
|
||
|
|
}
|
||
|
|
m_currency_count = count;
|
||
|
|
}
|
||
|
|
|
||
|
|
void SetUpdateInterval(int seconds) { m_update_interval_sec = seconds; }
|
||
|
|
void SetLookbackHours(int hours) { m_lookback_hours = hours; }
|
||
|
|
void SetLookaheadHours(int hours) { m_lookahead_hours = hours; }
|
||
|
|
void SetUseAPI(bool use) { m_use_api = use; }
|
||
|
|
void SetFallbackFile(const string filename) { m_fallback_filename = filename; }
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Update event cache from MQL5 Calendar |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool UpdateEvents()
|
||
|
|
{
|
||
|
|
datetime now = TimeCurrent();
|
||
|
|
|
||
|
|
// Check if update needed
|
||
|
|
if(m_last_update > 0 && (now - m_last_update) < m_update_interval_sec)
|
||
|
|
{
|
||
|
|
m_cache_hits++;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
m_cache_misses++;
|
||
|
|
|
||
|
|
// Clear existing events
|
||
|
|
m_event_count = 0;
|
||
|
|
ArrayResize(m_events, m_max_events);
|
||
|
|
|
||
|
|
bool success = false;
|
||
|
|
|
||
|
|
if(m_use_api)
|
||
|
|
{
|
||
|
|
success = FetchFromAPI();
|
||
|
|
}
|
||
|
|
|
||
|
|
if(!success && m_use_fallback_file)
|
||
|
|
{
|
||
|
|
success = FetchFromFallbackFile();
|
||
|
|
}
|
||
|
|
|
||
|
|
if(success)
|
||
|
|
{
|
||
|
|
m_last_update = now;
|
||
|
|
SortEventsByTime();
|
||
|
|
Log("Updated economic calendar: " + IntegerToString(m_event_count) + " events");
|
||
|
|
}
|
||
|
|
|
||
|
|
return success;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Fetch events from MQL5 Calendar API |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool FetchFromAPI()
|
||
|
|
{
|
||
|
|
// Calculate date range
|
||
|
|
datetime from_date = TimeCurrent() - (m_lookback_hours * 3600);
|
||
|
|
datetime to_date = TimeCurrent() + (m_lookahead_hours * 3600);
|
||
|
|
|
||
|
|
MqlCalendarValue values[];
|
||
|
|
|
||
|
|
// Try to get calendar values
|
||
|
|
// If specific currencies are set, filter by them
|
||
|
|
int retrieved = 0;
|
||
|
|
|
||
|
|
if(m_currency_count > 0)
|
||
|
|
{
|
||
|
|
// Fetch for specific currencies
|
||
|
|
for(int c = 0; c < m_currency_count && m_event_count < m_max_events; c++)
|
||
|
|
{
|
||
|
|
string currency = m_currencies[c];
|
||
|
|
|
||
|
|
// Reset array
|
||
|
|
ArrayResize(values, 0);
|
||
|
|
|
||
|
|
// Get calendar values
|
||
|
|
int count = CalendarValueHistory(values, from_date, to_date, currency);
|
||
|
|
|
||
|
|
if(count > 0)
|
||
|
|
{
|
||
|
|
for(int i = 0; i < count && m_event_count < m_max_events; i++)
|
||
|
|
{
|
||
|
|
if(AddCalendarValue(values[i]))
|
||
|
|
retrieved++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
m_api_calls++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// Get all calendar values
|
||
|
|
int count = CalendarValueHistory(values, from_date, to_date, NULL);
|
||
|
|
|
||
|
|
if(count > 0)
|
||
|
|
{
|
||
|
|
for(int i = 0; i < count && m_event_count < m_max_events; i++))
|
||
|
|
{
|
||
|
|
if(AddCalendarValue(values[i]))
|
||
|
|
retrieved++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
m_api_calls++;
|
||
|
|
}
|
||
|
|
|
||
|
|
return (retrieved > 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Add calendar value to cache |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool AddCalendarValue(const MqlCalendarValue &value)
|
||
|
|
{
|
||
|
|
if(m_event_count >= m_max_events)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
SEconomicEvent event;
|
||
|
|
|
||
|
|
// Get event details
|
||
|
|
MqlCalendarEvent event_info;
|
||
|
|
if(!CalendarEventById(value.event_id, event_info))
|
||
|
|
return false;
|
||
|
|
|
||
|
|
// Get country info
|
||
|
|
MqlCalendarCountry country_info;
|
||
|
|
if(!CalendarCountryById(event_info.country_id, country_info))
|
||
|
|
return false;
|
||
|
|
|
||
|
|
event.id = value.event_id;
|
||
|
|
event.name = event_info.name;
|
||
|
|
event.currency = country_info.currency;
|
||
|
|
event.country = country_info.code;
|
||
|
|
event.event_time = value.time;
|
||
|
|
|
||
|
|
// Determine importance
|
||
|
|
switch(event_info.importance)
|
||
|
|
{
|
||
|
|
case CALENDAR_IMPORTANCE_LOW:
|
||
|
|
event.importance = EVENT_IMPORTANCE_LOW;
|
||
|
|
break;
|
||
|
|
case CALENDAR_IMPORTANCE_MODERATE:
|
||
|
|
event.importance = EVENT_IMPORTANCE_MEDIUM;
|
||
|
|
break;
|
||
|
|
case CALENDAR_IMPORTANCE_HIGH:
|
||
|
|
event.importance = EVENT_IMPORTANCE_HIGH;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Values
|
||
|
|
if(value.actual_type != CALENDAR_VALUE_TYPE_NA)
|
||
|
|
{
|
||
|
|
event.actual_value = value.actual_value;
|
||
|
|
event.has_actual = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(value.forecast_type != CALENDAR_VALUE_TYPE_NA)
|
||
|
|
{
|
||
|
|
event.forecast_value = value.forecast_value;
|
||
|
|
event.has_forecast = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(value.prev_value != 0)
|
||
|
|
{
|
||
|
|
event.previous_value = value.prev_value;
|
||
|
|
event.has_previous = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Event type
|
||
|
|
switch(event_info.type)
|
||
|
|
{
|
||
|
|
case CALENDAR_TYPE_EVENT:
|
||
|
|
event.event_type = "economic";
|
||
|
|
break;
|
||
|
|
case CALENDAR_TYPE_INDICATOR:
|
||
|
|
event.event_type = "indicator";
|
||
|
|
break;
|
||
|
|
case CALENDAR_TYPE_HOLIDAY:
|
||
|
|
event.event_type = "holiday";
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
event.event_type = "other";
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check for duplicates
|
||
|
|
for(int i = 0; i < m_event_count; i++)
|
||
|
|
{
|
||
|
|
if(m_events[i].id == event.id && m_events[i].event_time == event.event_time)
|
||
|
|
return false; // Duplicate
|
||
|
|
}
|
||
|
|
|
||
|
|
m_events[m_event_count] = event;
|
||
|
|
m_event_count++;
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Fetch from fallback file |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool FetchFromFallbackFile()
|
||
|
|
{
|
||
|
|
string content;
|
||
|
|
if(!m_file_io.ReadFile(m_fallback_filename, content, true))
|
||
|
|
return false;
|
||
|
|
|
||
|
|
// Parse JSON (simplified - would need full JSON parser for production)
|
||
|
|
// For now, just log that we loaded the file
|
||
|
|
Log("Loaded fallback calendar file: " + m_fallback_filename);
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Sort events by time |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void SortEventsByTime()
|
||
|
|
{
|
||
|
|
// Simple bubble sort for small arrays
|
||
|
|
for(int i = 0; i < m_event_count - 1; i++)
|
||
|
|
{
|
||
|
|
for(int j = 0; j < m_event_count - i - 1; j++)
|
||
|
|
{
|
||
|
|
if(m_events[j].event_time > m_events[j + 1].event_time)
|
||
|
|
{
|
||
|
|
SEconomicEvent temp = m_events[j];
|
||
|
|
m_events[j] = m_events[j + 1];
|
||
|
|
m_events[j + 1] = temp;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Check if news time (main entry point for trading decisions) |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
SNewsImpact CheckNewsImpact(const string symbol, int buffer_minutes_before = 30, int buffer_minutes_after = 30)
|
||
|
|
{
|
||
|
|
SNewsImpact impact;
|
||
|
|
|
||
|
|
// Ensure events are current
|
||
|
|
UpdateEvents();
|
||
|
|
|
||
|
|
datetime now = TimeCurrent();
|
||
|
|
|
||
|
|
// Extract currency from symbol
|
||
|
|
string base_currency = StringSubstr(symbol, 0, 3);
|
||
|
|
string quote_currency = StringSubstr(symbol, 3, 3);
|
||
|
|
|
||
|
|
// Check for active news events
|
||
|
|
for(int i = 0; i < m_event_count; i++)
|
||
|
|
{
|
||
|
|
SEconomicEvent &event = m_events[i];
|
||
|
|
|
||
|
|
// Skip if not relevant to this symbol
|
||
|
|
if(!IsEventRelevant(event, base_currency, quote_currency))
|
||
|
|
continue;
|
||
|
|
|
||
|
|
// Calculate effective window
|
||
|
|
datetime window_start = event.event_time - (buffer_minutes_before * 60);
|
||
|
|
datetime window_end = event.event_time + (buffer_minutes_after * 60);
|
||
|
|
|
||
|
|
// Check if currently in news window
|
||
|
|
if(now >= window_start && now <= window_end)
|
||
|
|
{
|
||
|
|
impact.is_news_time = true;
|
||
|
|
impact.current_importance = MathMax(impact.current_importance, event.importance);
|
||
|
|
|
||
|
|
if(impact.reason != "") impact.reason += "; ";
|
||
|
|
impact.reason += event.name + " (" + IntegerToString(event.importance) + ")";
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check for upcoming events
|
||
|
|
if(event.event_time > now && event.event_time < impact.next_event_time)
|
||
|
|
{
|
||
|
|
impact.next_event_time = event.event_time;
|
||
|
|
impact.next_event_name = event.name;
|
||
|
|
impact.upcoming_importance = event.importance;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Calculate confidence adjustment
|
||
|
|
impact.confidence_adjustment = CalculateConfidenceAdjustment(impact);
|
||
|
|
impact.volatility_prediction = PredictVolatility(impact);
|
||
|
|
|
||
|
|
return impact;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Check if event is relevant to symbol |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool IsEventRelevant(const SEconomicEvent &event, const string base_currency, const string quote_currency)
|
||
|
|
{
|
||
|
|
// Direct currency match
|
||
|
|
if(event.currency == base_currency || event.currency == quote_currency)
|
||
|
|
return true;
|
||
|
|
|
||
|
|
// Major USD events affect all pairs
|
||
|
|
if(event.currency == "USD" && (base_currency == "USD" || quote_currency == "USD"))
|
||
|
|
return true;
|
||
|
|
|
||
|
|
// Global events (no specific currency)
|
||
|
|
if(event.currency == "" && event.importance >= EVENT_IMPORTANCE_HIGH)
|
||
|
|
return true;
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Calculate confidence adjustment based on news |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
double CalculateConfidenceAdjustment(const SNewsImpact &impact)
|
||
|
|
{
|
||
|
|
if(!impact.is_news_time)
|
||
|
|
return 1.0;
|
||
|
|
|
||
|
|
// Reduce confidence during news
|
||
|
|
double adjustment = 1.0;
|
||
|
|
|
||
|
|
switch(impact.current_importance)
|
||
|
|
{
|
||
|
|
case EVENT_IMPORTANCE_LOW:
|
||
|
|
adjustment = 0.9; // 10% reduction
|
||
|
|
break;
|
||
|
|
case EVENT_IMPORTANCE_MEDIUM:
|
||
|
|
adjustment = 0.75; // 25% reduction
|
||
|
|
break;
|
||
|
|
case EVENT_IMPORTANCE_HIGH:
|
||
|
|
adjustment = 0.5; // 50% reduction
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return adjustment;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Predict volatility based on news |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
double PredictVolatility(const SNewsImpact &impact)
|
||
|
|
{
|
||
|
|
if(!impact.is_news_time)
|
||
|
|
return 0.0;
|
||
|
|
|
||
|
|
// Base prediction on importance
|
||
|
|
double base_volatility = 0.0;
|
||
|
|
|
||
|
|
switch(impact.current_importance)
|
||
|
|
{
|
||
|
|
case EVENT_IMPORTANCE_LOW:
|
||
|
|
base_volatility = 0.2;
|
||
|
|
break;
|
||
|
|
case EVENT_IMPORTANCE_MEDIUM:
|
||
|
|
base_volatility = 0.5;
|
||
|
|
break;
|
||
|
|
case EVENT_IMPORTANCE_HIGH:
|
||
|
|
base_volatility = 1.0;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Could be enhanced with historical volatility data
|
||
|
|
return base_volatility;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Get next significant event |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
SEconomicEvent GetNextEvent(int min_importance = EVENT_IMPORTANCE_MEDIUM)
|
||
|
|
{
|
||
|
|
SEconomicEvent next;
|
||
|
|
|
||
|
|
UpdateEvents();
|
||
|
|
|
||
|
|
datetime now = TimeCurrent();
|
||
|
|
|
||
|
|
for(int i = 0; i < m_event_count; i++)
|
||
|
|
{
|
||
|
|
if(m_events[i].event_time > now && m_events[i].importance >= min_importance)
|
||
|
|
{
|
||
|
|
next = m_events[i];
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return next;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Export events to file (for ML training) |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool ExportEventsToFile(const string filename)
|
||
|
|
{
|
||
|
|
string content = "timestamp,event_name,currency,importance,actual,forecast,previous,type\n";
|
||
|
|
|
||
|
|
for(int i = 0; i < m_event_count; i++)
|
||
|
|
{
|
||
|
|
SEconomicEvent &e = m_events[i];
|
||
|
|
content += StringFormat("%s,%s,%s,%d,%.5f,%.5f,%.5f,%s\n",
|
||
|
|
TimeToString(e.event_time, TIME_DATE|TIME_SECONDS),
|
||
|
|
e.name,
|
||
|
|
e.currency,
|
||
|
|
e.importance,
|
||
|
|
e.actual_value,
|
||
|
|
e.forecast_value,
|
||
|
|
e.previous_value,
|
||
|
|
e.event_type
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return m_file_io.WriteFile(filename, content, true, false);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Statistics |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
string GetStatistics()
|
||
|
|
{
|
||
|
|
string stats = StringFormat(
|
||
|
|
"=== Economic Calendar Statistics ===\n" +
|
||
|
|
"Events cached: %d\n" +
|
||
|
|
"API calls: %d\n" +
|
||
|
|
"Cache hits: %d\n" +
|
||
|
|
"Cache misses: %d\n" +
|
||
|
|
"Last update: %s",
|
||
|
|
m_event_count,
|
||
|
|
m_api_calls,
|
||
|
|
m_cache_hits,
|
||
|
|
m_cache_misses,
|
||
|
|
(m_last_update > 0 ? TimeToString(m_last_update) : "Never")
|
||
|
|
);
|
||
|
|
|
||
|
|
return stats;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Utility functions |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void Log(const string message)
|
||
|
|
{
|
||
|
|
Print("[EconomicCalendar] " + message);
|
||
|
|
}
|
||
|
|
|
||
|
|
int GetEventCount() const { return m_event_count; }
|
||
|
|
bool IsCacheValid() const { return (TimeCurrent() - m_last_update) < m_update_interval_sec; }
|
||
|
|
datetime GetLastUpdate() const { return m_last_update; }
|
||
|
|
};
|
||
|
|
|
||
|
|
// Static instance initialization
|
||
|
|
CEconomicCalendar* CEconomicCalendar::s_instance = NULL;
|
||
|
|
|
||
|
|
// Convenience macros
|
||
|
|
#define CHECK_NEWS_IMPACT(symbol, buffer_before, buffer_after) \
|
||
|
|
CEconomicCalendar::Instance().CheckNewsImpact(symbol, buffer_before, buffer_after)
|
||
|
|
|
||
|
|
#define GET_NEXT_EVENT(min_importance) \
|
||
|
|
CEconomicCalendar::Instance().GetNextEvent(min_importance)
|
||
|
|
|
||
|
|
#define UPDATE_CALENDAR() \
|
||
|
|
CEconomicCalendar::Instance().UpdateEvents()
|
||
|
|
|
||
|
|
#endif // CECONOMICCALENDAR_MQH
|