forked from animatedread/Warrior_EA
CalendarValueHistory() was called with no country filter at all, so ANY country's economic calendar event vetoed a trade regardless of relevance - a JPY release blocked a EURUSD trade just as readily as a USD one, making NF_MinImpact's fine-tuning far noisier than intended. Adds System/NewsRelevance.mqh (GetRelevantCountryCodes/ ImpactWeightedProximity), a shared utility that cross-references CalendarCountries() against the symbol's base/quote currency to get the actually-relevant ISO country codes, then uses CalendarValueHistory()'s country_code-filtering overload. Shared so the upcoming NN news-input feature reuses the same relevance logic rather than duplicating it. Compiled clean (MetaEditor, 0 errors/0 warnings). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
87 lines
4.3 KiB
MQL5
87 lines
4.3 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| NewsRelevance.mqh |
|
|
//| AnimateDread |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "AnimateDread"
|
|
#property link "https://www.mql5.com"
|
|
//--- Module-scope cache: a symbol's currency composition never changes at runtime, and
|
|
//--- CalendarCountries() enumerating every country in the terminal's calendar database on
|
|
//--- every call (both the live NewsFilter veto and, per-bar, the NN news feature) would be
|
|
//--- pure waste. Single-symbol cache (this codebase runs one symbol per EA instance/chart) -
|
|
//--- rebuilt if a different symbol is ever requested.
|
|
string g_newsRelevanceCachedSymbol = "";
|
|
string g_newsRelevanceCountryCodes[];
|
|
//+------------------------------------------------------------------+
|
|
//| Builds the ISO country codes relevant to symbol's base+quote |
|
|
//| currency (e.g. EURUSD -> every Eurozone country + the US) by |
|
|
//| cross-referencing CalendarCountries() against |
|
|
//| SYMBOL_CURRENCY_BASE/SYMBOL_CURRENCY_PROFIT. This is what |
|
|
//| CalendarValueHistory()'s country_code filter needs to actually |
|
|
//| scope news to the traded symbol, instead of vetoing/scoring on |
|
|
//| ANY country's events regardless of relevance. |
|
|
//+------------------------------------------------------------------+
|
|
void GetRelevantCountryCodes(string symbol, string &countryCodes[])
|
|
{
|
|
if(symbol == g_newsRelevanceCachedSymbol)
|
|
{
|
|
ArrayCopy(countryCodes, g_newsRelevanceCountryCodes);
|
|
return;
|
|
}
|
|
ArrayResize(countryCodes, 0);
|
|
string baseCcy = SymbolInfoString(symbol, SYMBOL_CURRENCY_BASE);
|
|
string profitCcy = SymbolInfoString(symbol, SYMBOL_CURRENCY_PROFIT);
|
|
MqlCalendarCountry countries[];
|
|
int total = CalendarCountries(countries);
|
|
for(int i = 0; i < total; i++)
|
|
{
|
|
if(countries[i].currency == baseCcy || countries[i].currency == profitCcy)
|
|
{
|
|
int n = ArraySize(countryCodes);
|
|
ArrayResize(countryCodes, n + 1);
|
|
countryCodes[n] = countries[i].code;
|
|
}
|
|
}
|
|
g_newsRelevanceCachedSymbol = symbol;
|
|
ArrayCopy(g_newsRelevanceCountryCodes, countryCodes);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Impact-weighted proximity, in [0,1], to the CLOSEST relevant |
|
|
//| (symbol-currency-matched) calendar event on one side of pivotTime: |
|
|
//| searchForward=false looks backward (recency - "how close was the |
|
|
//| last relevant release"), true looks forward (proximity to the |
|
|
//| next SCHEDULED relevant release - legitimate to use even for a |
|
|
//| historical training bar, since release times are publicly known |
|
|
//| in advance; this is NOT the same as knowing the release's actual |
|
|
//| outcome ahead of time, which would be real lookahead bias and is |
|
|
//| deliberately not exposed anywhere here). |
|
|
//| Returns 0.0 if no relevant event falls within windowMinutes on the |
|
|
//| requested side, else (impact_type/3.0) * (1 - minutesAway/window) |
|
|
//| for whichever qualifying event is closest to pivotTime. |
|
|
//+------------------------------------------------------------------+
|
|
double ImpactWeightedProximity(string symbol, datetime pivotTime, int windowMinutes, bool searchForward)
|
|
{
|
|
if(windowMinutes <= 0)
|
|
return 0.0;
|
|
string countryCodes[];
|
|
GetRelevantCountryCodes(symbol, countryCodes);
|
|
if(ArraySize(countryCodes) == 0)
|
|
return 0.0;
|
|
datetime windowStart = searchForward ? pivotTime : pivotTime - windowMinutes * 60;
|
|
datetime windowEnd = searchForward ? pivotTime + windowMinutes * 60 : pivotTime;
|
|
double best = 0.0;
|
|
for(int c = 0; c < ArraySize(countryCodes); c++)
|
|
{
|
|
MqlCalendarValue values[];
|
|
if(!CalendarValueHistory(values, windowStart, windowEnd, countryCodes[c]))
|
|
continue;
|
|
for(int i = 0; i < ArraySize(values); i++)
|
|
{
|
|
double minutesAway = MathAbs((double)(values[i].time - pivotTime)) / 60.0;
|
|
double weight = (values[i].impact_type / 3.0) * (1.0 - minutesAway / windowMinutes);
|
|
if(weight > best)
|
|
best = weight;
|
|
}
|
|
}
|
|
return best;
|
|
}
|