diff --git a/data/sample/events_sample.json b/data/sample/events_sample.json index fabad55..78c1ac7 100644 --- a/data/sample/events_sample.json +++ b/data/sample/events_sample.json @@ -1,14 +1,14 @@ [ - {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-000000000001", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-01-27T21:00:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "MSFT"}]}, - {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-000000000002", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-01-27T21:05:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "GOOGL"}]}, - {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-000000000003", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-01-28T12:10:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "BA"}]}, - {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-000000000004", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-01-28T21:00:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "META"}]}, - {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-000000000005", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-01-28T21:02:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "TSLA"}]}, - {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-000000000006", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-01-28T21:10:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "IBM"}]}, - {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-000000000007", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-01-29T21:00:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "AAPL"}]}, - {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-000000000008", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-01-29T21:03:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "AMZN"}]}, - {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-000000000009", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-01-30T12:00:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "CVX"}]}, - {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-00000000000a", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-02-03T21:00:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "AMD"}]}, - {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-00000000000b", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-02-04T21:00:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "NVDA"}]}, - {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-00000000000c", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-02-04T21:05:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "QCOM"}]} + {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-000000000001", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-01-27T21:00:00Z", "knowledge_cutoff": "2026-01-26T21:00:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "MSFT"}]}, + {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-000000000002", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-01-27T21:05:00Z", "knowledge_cutoff": "2026-01-27T21:00:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "GOOGL"}]}, + {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-000000000003", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-01-28T12:10:00Z", "knowledge_cutoff": "2026-01-27T21:00:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "BA"}]}, + {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-000000000004", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-01-28T21:00:00Z", "knowledge_cutoff": "2026-01-27T21:00:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "META"}]}, + {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-000000000005", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-01-28T21:02:00Z", "knowledge_cutoff": "2026-01-28T21:00:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "TSLA"}]}, + {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-000000000006", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-01-28T21:10:00Z", "knowledge_cutoff": "2026-01-28T21:00:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "IBM"}]}, + {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-000000000007", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-01-29T21:00:00Z", "knowledge_cutoff": "2026-01-28T21:00:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "AAPL"}]}, + {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-000000000008", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-01-29T21:03:00Z", "knowledge_cutoff": "2026-01-29T21:00:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "AMZN"}]}, + {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-000000000009", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-01-30T12:00:00Z", "knowledge_cutoff": "2026-01-29T21:00:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "CVX"}]}, + {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-00000000000a", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-02-03T21:00:00Z", "knowledge_cutoff": "2026-02-02T21:00:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "AMD"}]}, + {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-00000000000b", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-02-04T21:00:00Z", "knowledge_cutoff": "2026-02-03T21:00:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "NVDA"}]}, + {"event_id": "0f8c2a10-1b2c-4d5e-8a9b-00000000000c", "event_type": "EARNINGS_RELEASE", "timing_category": "SCHEDULED", "event_datetime": "2026-02-04T21:05:00Z", "knowledge_cutoff": "2026-02-04T21:00:00Z", "focal_assets": [{"identifier_type": "TICKER", "identifier_value": "QCOM"}]} ] diff --git a/src/examples/frames.py b/src/examples/frames.py index 1be5cc9..3e2fbb9 100644 --- a/src/examples/frames.py +++ b/src/examples/frames.py @@ -13,6 +13,7 @@ # Columns, in reading order, for the events table. _EVENT_COLUMNS = [ "event_datetime", + "knowledge_cutoff", "event_type", "timing_category", "tickers", @@ -31,6 +32,7 @@ def events_frame(events: list[CalendarEvent]) -> pd.DataFrame: rows = [ { "event_datetime": ev.event_datetime, + "knowledge_cutoff": ev.knowledge_cutoff, "event_type": ev.event_type, "timing_category": ev.timing_category, "tickers": ", ".join(a.identifier_value for a in ev.focal_assets), @@ -42,6 +44,7 @@ def events_frame(events: list[CalendarEvent]) -> pd.DataFrame: df = pd.DataFrame(rows, columns=_EVENT_COLUMNS) if not df.empty: df["event_datetime"] = pd.to_datetime(df["event_datetime"], utc=True, format="ISO8601") + df["knowledge_cutoff"] = pd.to_datetime(df["knowledge_cutoff"], utc=True, format="ISO8601") df = df.sort_values("event_datetime").reset_index(drop=True) return df diff --git a/src/examples/schemas.py b/src/examples/schemas.py index 9c48629..a95bfa0 100644 --- a/src/examples/schemas.py +++ b/src/examples/schemas.py @@ -35,6 +35,10 @@ class CalendarEvent(_Model): event_type: str timing_category: str # "SCHEDULED" | "UNSCHEDULED" event_datetime: str + # The event's knowledge cutoff: an agent must not use information from + # after this instant (ISO 8601, UTC). Optional so older cached/sample + # payloads from before the field existed still parse. + knowledge_cutoff: str | None = None focal_assets: list[AssetIdentifier] = [] diff --git a/tests/test_frames.py b/tests/test_frames.py index 803719d..dcbb7ca 100644 --- a/tests/test_frames.py +++ b/tests/test_frames.py @@ -21,6 +21,7 @@ def test_events_frame_sorts_and_flattens(sample_events: list[dict]) -> None: assert list(df.columns) == [ "event_datetime", + "knowledge_cutoff", "event_type", "timing_category", "tickers", @@ -42,7 +43,7 @@ def test_events_frame_sorts_and_flattens(sample_events: list[dict]) -> None: def test_events_frame_empty() -> None: df = events_frame([]) assert df.empty - assert list(df.columns)[:2] == ["event_datetime", "event_type"] + assert list(df.columns)[:2] == ["event_datetime", "knowledge_cutoff"] def test_manifest_frame_hides_urls_and_sorts() -> None: