alpha-engine-lib 0.32.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. alpha_engine_lib/__init__.py +3 -0
  2. alpha_engine_lib/agent_schemas.py +663 -0
  3. alpha_engine_lib/alerts.py +576 -0
  4. alpha_engine_lib/arcticdb.py +340 -0
  5. alpha_engine_lib/collector_results.py +69 -0
  6. alpha_engine_lib/cost.py +665 -0
  7. alpha_engine_lib/dates.py +273 -0
  8. alpha_engine_lib/decision_capture.py +462 -0
  9. alpha_engine_lib/ec2_spot.py +363 -0
  10. alpha_engine_lib/email_sender.py +206 -0
  11. alpha_engine_lib/eval_artifacts.py +361 -0
  12. alpha_engine_lib/logging.py +303 -0
  13. alpha_engine_lib/model_pricing.yaml +73 -0
  14. alpha_engine_lib/pillars.py +756 -0
  15. alpha_engine_lib/pipeline_status/__init__.py +70 -0
  16. alpha_engine_lib/pipeline_status/read.py +541 -0
  17. alpha_engine_lib/pipeline_status/registry.py +368 -0
  18. alpha_engine_lib/pipeline_status/templates.py +120 -0
  19. alpha_engine_lib/preflight.py +444 -0
  20. alpha_engine_lib/rag/__init__.py +39 -0
  21. alpha_engine_lib/rag/db.py +96 -0
  22. alpha_engine_lib/rag/embeddings.py +63 -0
  23. alpha_engine_lib/rag/migrations/0001_content_tsv.sql +39 -0
  24. alpha_engine_lib/rag/rerank.py +377 -0
  25. alpha_engine_lib/rag/retrieval.py +465 -0
  26. alpha_engine_lib/rag/schema.sql +65 -0
  27. alpha_engine_lib/reconcile.py +203 -0
  28. alpha_engine_lib/secrets.py +186 -0
  29. alpha_engine_lib/sources/__init__.py +35 -0
  30. alpha_engine_lib/sources/protocols.py +227 -0
  31. alpha_engine_lib/ssm_log_capture.py +274 -0
  32. alpha_engine_lib/telegram.py +165 -0
  33. alpha_engine_lib/trading_calendar.py +236 -0
  34. alpha_engine_lib/transparency.py +746 -0
  35. alpha_engine_lib/transparency_inventory.yaml +260 -0
  36. alpha_engine_lib/universe.py +83 -0
  37. alpha_engine_lib-0.32.0.dist-info/METADATA +217 -0
  38. alpha_engine_lib-0.32.0.dist-info/RECORD +40 -0
  39. alpha_engine_lib-0.32.0.dist-info/WHEEL +5 -0
  40. alpha_engine_lib-0.32.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,273 @@
1
+ """
2
+ dates.py — canonical "current date" attribution + freshness checks for
3
+ trade-related artifacts.
4
+
5
+ Implements the dual-tracking convention from
6
+ ``alpha-engine-docs/private/DATE_CONVENTIONS.md``: every trade-related
7
+ artifact records both a ``calendar_date`` (wall-clock UTC) and a
8
+ ``trading_day`` (last completed NYSE session). The ``trading_day``
9
+ attribution is strictly backward-looking — never ahead of "now" — so
10
+ artifacts produced on weekends, holidays, or pre-open weekday mornings
11
+ attribute to the most recent session that has actually closed.
12
+
13
+ Use this at every artifact-write site::
14
+
15
+ from alpha_engine_lib.dates import now_dual
16
+
17
+ dd = now_dual()
18
+ record = {
19
+ "calendar_date": dd.calendar_date,
20
+ "trading_day": dd.trading_day,
21
+ ...
22
+ }
23
+
24
+ For backfilling historical rows that only have a wall-clock timestamp::
25
+
26
+ from alpha_engine_lib.dates import session_for_timestamp
27
+
28
+ trading_day = session_for_timestamp(row["created_at"])
29
+
30
+ For freshness checks across the system, use the trading-day-aware helpers
31
+ rather than calendar-day arithmetic::
32
+
33
+ from alpha_engine_lib.dates import is_fresh_in_trading_days
34
+
35
+ # Producer-side postflight: did macro.SPY land the most recent close?
36
+ if not is_fresh_in_trading_days(spy_last_date, run_date):
37
+ raise PostflightError(...)
38
+
39
+ # Consumer-side preflight: was the data refreshed within ≤1 trading day?
40
+ if not is_fresh_in_trading_days(ticker_last_date, today, max_stale=1):
41
+ raise PreflightError(...)
42
+
43
+ The freshness helpers replace the calendar-day-arithmetic patterns that bit
44
+ the 2026-05-24 SF recovery — every post-Saturday redrive trips a calendar-
45
+ day gate even when the data carries the most recent NYSE close. Calendar
46
+ days only happen to work on Saturday because Fri→Sat is +1 in both calendar
47
+ and trading-day arithmetic. See [[feedback_lift_invariants_to_chokepoint
48
+ _after_second_recurrence]] + [[feedback_sota_institutional_default_no_shortcuts]].
49
+
50
+ This module is a thin wrapper over
51
+ ``alpha_engine_lib.trading_calendar.{last_closed_trading_day,count_trading_days}``
52
+ — its purpose is to standardize the *output shape* (DualDate) and provide a
53
+ single canonical entry point so every consumer sees the same semantics.
54
+ """
55
+
56
+ from __future__ import annotations
57
+
58
+ from dataclasses import dataclass
59
+ from datetime import date, datetime, time, timezone
60
+
61
+ from .trading_calendar import count_trading_days, last_closed_trading_day
62
+
63
+
64
+ @dataclass(frozen=True)
65
+ class DualDate:
66
+ """Calendar + trading_day attribution for a moment in time.
67
+
68
+ Both fields are ISO ``yyyy-mm-dd`` strings — easy to serialize across
69
+ JSON / SQLite / parquet boundaries with no timezone ambiguity.
70
+
71
+ Attributes:
72
+ calendar_date: wall-clock UTC date when the artifact was produced.
73
+ Audit trail. Same on holidays/weekends as any other day; reflects
74
+ *when* the process ran, not which session the data is about.
75
+ trading_day: last NYSE trading session whose 4:00 PM ET close has
76
+ occurred at or before the given moment. Strictly backward-looking;
77
+ equals ``last_closed_trading_day(now)``. Never ahead of "now".
78
+
79
+ Example::
80
+
81
+ >>> from datetime import datetime, timezone
82
+ >>> from alpha_engine_lib.dates import now_dual
83
+ >>> sat = datetime(2026, 4, 25, 0, 0, tzinfo=timezone.utc)
84
+ >>> dd = now_dual(now=sat)
85
+ >>> dd.calendar_date
86
+ '2026-04-25'
87
+ >>> dd.trading_day # Sat isn't a session; walk back to Fri
88
+ '2026-04-24'
89
+ """
90
+
91
+ calendar_date: str
92
+ trading_day: str
93
+
94
+
95
+ def now_dual(*, now: datetime | None = None) -> DualDate:
96
+ """Canonical current-date attribution for the alpha-engine system.
97
+
98
+ Every artifact-write site should call this to populate the
99
+ ``calendar_date`` and ``trading_day`` columns/fields, rather than
100
+ reaching for ``date.today()`` or ``datetime.now().date()``. Calling
101
+ here ensures consistent semantics across modules and prevents the
102
+ drift that motivated the convention (see
103
+ ``alpha-engine-docs/private/DATE_CONVENTIONS.md``).
104
+
105
+ Args:
106
+ now: timezone-aware datetime. Defaults to current UTC time.
107
+ Naive datetimes are interpreted as UTC for ``calendar_date``
108
+ and forwarded to ``last_closed_trading_day`` (which itself
109
+ assumes NYSE-local for naive inputs — this is intentional;
110
+ the helper hides the conversion).
111
+
112
+ Returns:
113
+ DualDate where ``calendar_date`` is the UTC date of ``now`` and
114
+ ``trading_day`` is the last NYSE session that has fully closed at
115
+ or before ``now``.
116
+ """
117
+ if now is None:
118
+ now = datetime.now(timezone.utc)
119
+ elif now.tzinfo is None:
120
+ now = now.replace(tzinfo=timezone.utc)
121
+
122
+ cal_utc = now.astimezone(timezone.utc).date()
123
+ td = last_closed_trading_day(now)
124
+
125
+ return DualDate(
126
+ calendar_date=cal_utc.isoformat(),
127
+ trading_day=td.isoformat(),
128
+ )
129
+
130
+
131
+ # ── Freshness checks (trading-day-aware) ─────────────────────────────────────
132
+
133
+
134
+ def expected_last_close(run_date: date | str) -> date:
135
+ """The most recent NYSE close that exists as of ``run_date``.
136
+
137
+ For Saturday/Sunday/holiday-Monday → the prior Friday (or earlier if a
138
+ holiday-adjacent week). For trading days → the same date (the day's close
139
+ has settled, anchored at end-of-day for the purpose of staleness checks).
140
+
141
+ This is the canonical reference point for "what's the freshest data we
142
+ could expect to see at this run_date?" — used by every producer-side
143
+ postflight + consumer-side preflight in the system.
144
+
145
+ Args:
146
+ run_date: ISO ``yyyy-mm-dd`` string OR a ``datetime.date``.
147
+
148
+ Returns:
149
+ The expected last-closed NYSE session date as a ``datetime.date``.
150
+
151
+ Example::
152
+
153
+ >>> from datetime import date
154
+ >>> expected_last_close(date(2026, 5, 24)) # Sunday
155
+ datetime.date(2026, 5, 22)
156
+ >>> expected_last_close("2026-05-25") # Memorial Day (Mon)
157
+ datetime.date(2026, 5, 22)
158
+ >>> expected_last_close(date(2026, 5, 26)) # Tuesday after Memorial Day
159
+ datetime.date(2026, 5, 26)
160
+ """
161
+ if isinstance(run_date, str):
162
+ run_date = datetime.strptime(run_date, "%Y-%m-%d").date()
163
+ # Anchor at 23:00 UTC of run_date so the resolver sees that day's NYSE
164
+ # close as settled if run_date is itself a trading day. (NYSE close is
165
+ # 4 PM ET = 20-21 UTC depending on DST; 23 UTC is unambiguously after.)
166
+ anchor = datetime.combine(run_date, time(23, 0), tzinfo=timezone.utc)
167
+ return last_closed_trading_day(anchor)
168
+
169
+
170
+ def trading_days_stale(last_date: date, reference: date | str) -> int:
171
+ """Number of NYSE trading sessions ``last_date`` is behind ``reference``.
172
+
173
+ Returns 0 when ``last_date`` is at or ahead of the reference's expected
174
+ last-closed trading day. Holiday-aware (NYSE calendar, not US Federal).
175
+
176
+ Semantically the canonical staleness metric for any "is this artifact
177
+ carrying the most recent close that exists?" check across the system.
178
+ Replaces the calendar-day arithmetic (``(reference - last_date).days``)
179
+ that breaks on every non-Saturday redrive.
180
+
181
+ Args:
182
+ last_date: the artifact's stored last_date (``datetime.date``).
183
+ reference: ISO ``yyyy-mm-dd`` string OR ``datetime.date`` — the
184
+ run_date or "today" against which freshness is being asked.
185
+
186
+ Returns:
187
+ Integer count of NYSE sessions in
188
+ ``(last_date, expected_last_close(reference)]``. Zero means
189
+ "carries the most recent available close." Positive means
190
+ "missed N sessions."
191
+
192
+ Example::
193
+
194
+ >>> from datetime import date
195
+ >>> trading_days_stale(date(2026, 5, 22), date(2026, 5, 24)) # Fri vs Sun
196
+ 0
197
+ >>> trading_days_stale(date(2026, 5, 22), date(2026, 5, 25)) # Fri vs Memorial-Mon
198
+ 0
199
+ >>> trading_days_stale(date(2026, 5, 22), date(2026, 5, 26)) # Fri vs Tue close
200
+ 1
201
+ >>> trading_days_stale(date(2026, 5, 13), date(2026, 5, 22)) # Wed 5/13 vs Fri 5/22
202
+ 7
203
+ """
204
+ expected = expected_last_close(reference)
205
+ if last_date >= expected:
206
+ return 0
207
+ return count_trading_days(last_date, expected)
208
+
209
+
210
+ def is_fresh_in_trading_days(
211
+ last_date: date,
212
+ reference: date | str,
213
+ *,
214
+ max_stale: int = 0,
215
+ ) -> bool:
216
+ """Canonical freshness predicate: is ``last_date`` ≤ ``max_stale`` trading days behind ``reference``?
217
+
218
+ Default ``max_stale=0`` means "must carry the most recent NYSE close that
219
+ exists as of reference" — the strictest gate, used by producer-side
220
+ postflights where the producer just wrote and should be current.
221
+ ``max_stale=1`` tolerates one missing session — used by consumer-side
222
+ preflights that need to survive the T+1 latency of polygon's daily
223
+ aggregate publish.
224
+
225
+ Args:
226
+ last_date: artifact's stored last_date.
227
+ reference: run_date the freshness is being asked at.
228
+ max_stale: max permitted trading-day lag. Keyword-only to force
229
+ explicit semantics at every call site.
230
+
231
+ Returns:
232
+ ``True`` iff the artifact carries data within ``max_stale`` trading
233
+ days of the most recent NYSE close that exists as of ``reference``.
234
+
235
+ Example::
236
+
237
+ >>> from datetime import date
238
+ >>> is_fresh_in_trading_days(date(2026, 5, 22), date(2026, 5, 24)) # Fri vs Sun
239
+ True
240
+ >>> is_fresh_in_trading_days(date(2026, 5, 13), date(2026, 5, 22)) # 7-session lag
241
+ False
242
+ >>> is_fresh_in_trading_days(date(2026, 5, 13), date(2026, 5, 22), max_stale=10)
243
+ True
244
+ """
245
+ return trading_days_stale(last_date, reference) <= max_stale
246
+
247
+
248
+ def session_for_timestamp(ts: datetime) -> str:
249
+ """Trading day a timestamp belongs to under the dual-tracking convention.
250
+
251
+ Backward-looking: returns the most recent NYSE trading session whose
252
+ 4:00 PM ET close has occurred at or before ``ts``. Used to backfill the
253
+ ``trading_day`` column on historical rows that only have a wall-clock
254
+ timestamp (``created_at``, ``fill_time``, etc.).
255
+
256
+ Args:
257
+ ts: timezone-aware datetime. Naive timestamps are assumed UTC.
258
+
259
+ Returns:
260
+ ISO ``yyyy-mm-dd`` string of the trading day.
261
+
262
+ Example::
263
+
264
+ >>> from datetime import datetime
265
+ >>> from zoneinfo import ZoneInfo
266
+ >>> # Mon 9 AM ET — session not yet closed
267
+ >>> ts = datetime(2026, 4, 27, 9, 0, tzinfo=ZoneInfo("America/New_York"))
268
+ >>> session_for_timestamp(ts)
269
+ '2026-04-24'
270
+ """
271
+ if ts.tzinfo is None:
272
+ ts = ts.replace(tzinfo=timezone.utc)
273
+ return last_closed_trading_day(ts).isoformat()