From 4b9834a77597f9be001ddc52c5273687ae8bed00 Mon Sep 17 00:00:00 2001 From: goodboy Date: Tue, 24 Mar 2026 13:24:39 -0400 Subject: [PATCH] Harden `.ib.venues` against unknown exchange cals Deats, - catch `InvalidCalendarName` in `has_holiday()` so venues without an `exchange_calendars` entry (eg. `IDEALPRO` for forex, `PAXOS` for crypto) gracefully return `False` instead of raising. - add `log` via `get_logger()` to emit a warning when skipping the holiday check for an unmapped venue. - fix `std_exch` type annot from `dict` -> `str`. - guard `is_expired()` against empty `.realExpirationDate` strings. - fill in `is_expired()` docstring. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- piker/brokers/ib/venues.py | 39 ++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/piker/brokers/ib/venues.py b/piker/brokers/ib/venues.py index 75981619..85969707 100644 --- a/piker/brokers/ib/venues.py +++ b/piker/brokers/ib/venues.py @@ -33,6 +33,9 @@ from typing import ( ) import exchange_calendars as xcals +from exchange_calendars.errors import ( + InvalidCalendarName, +) from pendulum import ( parse, now, @@ -41,6 +44,10 @@ from pendulum import ( Time, ) +from piker.log import get_logger + +log = get_logger(__name__) + if TYPE_CHECKING: from ib_async import ( TradingSession, @@ -61,10 +68,15 @@ def is_expired( con_deats: ContractDetails, ) -> bool: ''' - Predicate + Simple predicate whether the provided contract-deats match and + already lifetime-terminated instrument. ''' - expiry_dt: datetime = parse(con_deats.realExpirationDate) + expiry_str: str = con_deats.realExpirationDate + if not expiry_str: + return False + + expiry_dt: datetime = parse(expiry_str) return expiry_dt.date() >= now().date() @@ -102,13 +114,28 @@ def has_holiday( con.exchange ) - # XXX, ad-hoc handle any IB exchange which are non-std - # via lookup table.. - std_exch: dict = { + # XXX, ad-hoc handle any IB exchange which are + # non-std via lookup table.. + std_exch: str = { 'ARCA': 'ARCX', }.get(exch, exch) - cal: ExchangeCalendar = xcals.get_calendar(std_exch) + try: + cal: ExchangeCalendar = xcals.get_calendar( + std_exch + ) + except InvalidCalendarName: + # venue has no `exchange_calendars` entry + # (eg. IDEALPRO for forex, PAXOS for + # crypto) -> not a holiday by default since + # weekends are already handled by + # `has_weekend()`. + log.warning( + f'No exchange cal for {std_exch!r},' + f' skipping holiday check..\n' + ) + return False + end: datetime = period.end # _start: datetime = period.start # ?TODO, can rm ya?