Cache economic calendar for 1 hour, fix SCAN_BUTTONS test for EUR-only

The weekly calendar JSON was re-fetched on every scan cycle (~288
calls/day). Now cached per-instance with 1-hour TTL, cutting to ~24.
Currency filtering still applied on each read so cached events stay
correct across pairs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
harwinder
2026-05-21 19:36:15 -04:00
parent 0593e455bc
commit 5ad5640ad1
2 changed files with 13 additions and 1 deletions
+12
View File
@@ -127,6 +127,7 @@ class NewsEngine:
} }
_calendar_backoff_until: float = 0.0 _calendar_backoff_until: float = 0.0
_CALENDAR_CACHE_TTL: float = 3600.0
def __init__(self, config: NewsConfig): def __init__(self, config: NewsConfig):
self.config = config self.config = config
@@ -134,6 +135,8 @@ class NewsEngine:
self._session.headers.update({ self._session.headers.update({
"User-Agent": "Mozilla/5.0 (compatible; ForexIntel/1.0)" "User-Agent": "Mozilla/5.0 (compatible; ForexIntel/1.0)"
}) })
self._calendar_cache: List[EconomicEvent] = []
self._calendar_cache_time: float = 0.0
def get_fundamental_context( def get_fundamental_context(
self, currencies: Optional[List[str]] = None self, currencies: Optional[List[str]] = None
@@ -278,11 +281,20 @@ class NewsEngine:
Tries Forex Factory first, falls back to MyFXBook RSS. Tries Forex Factory first, falls back to MyFXBook RSS.
Returns an empty list (with a log warning) if unavailable — Returns an empty list (with a log warning) if unavailable —
we never generate fake events since that causes constant false alarms. we never generate fake events since that causes constant false alarms.
Results are cached for 1 hour since the weekly calendar rarely changes.
""" """
now = time.time()
if self._calendar_cache and (now - self._calendar_cache_time) < self._CALENDAR_CACHE_TTL:
return [e for e in self._calendar_cache if e.currency in currencies]
for fetcher in (self._try_fetch_calendar_api, self._try_fetch_myfxbook_rss): for fetcher in (self._try_fetch_calendar_api, self._try_fetch_myfxbook_rss):
try: try:
events = fetcher() events = fetcher()
if events: if events:
self._calendar_cache = events
self._calendar_cache_time = now
logger.debug(f"Calendar cached: {len(events)} events (TTL {self._CALENDAR_CACHE_TTL:.0f}s)")
return [e for e in events if e.currency in currencies] return [e for e in events if e.currency in currencies]
except Exception as e: except Exception as e:
logger.debug(f"Calendar fetch exception ({fetcher.__name__}): {e}") logger.debug(f"Calendar fetch exception ({fetcher.__name__}): {e}")
+1 -1
View File
@@ -1475,7 +1475,7 @@ def _():
@test("SCAN_BUTTONS and SCAN_LOOKUP consistency") @test("SCAN_BUTTONS and SCAN_LOOKUP consistency")
def _(): def _():
assert len(SCAN_BUTTONS) == 10 # 5 per pair (4 TFs + All) × 2 pairs assert len(SCAN_BUTTONS) == 5 # 4 TFs + All for EUR/USD
for btn in SCAN_BUTTONS: for btn in SCAN_BUTTONS:
assert btn["cb"] in SCAN_LOOKUP assert btn["cb"] in SCAN_LOOKUP
assert "symbol" in btn assert "symbol" in btn