Skip to content

Commit 76fbda2

Browse files
userFRMclaude
andauthored
fix(python): complete type stubs for string enums and the util submodule (#887)
The type-stub surface left several runtime exports resolving only through the module-level `__getattr__` fallback to `Any`, so mypy users typing against `Interval.M_1`, `Right.CALL`, or the `thetadatadx.util` lookups got no checking at all. Add explicit class declarations for the six string-valued parameter enums (`Right`, `Venue`, `Interval`, `RateType`, `RequestType`, `Version`) with their exact members, the `value` accessor, and the `__str__` / `__repr__` / `__eq__` / `__hash__` surface. Ship a `util.pyi` for the `thetadatadx.util` submodule declaring all fourteen public lookup and conversion functions with their real signatures and return types. Add the missing `__bool__` and `__repr__` to `FlatFileRowList`. Verified with `mypy.stubtest` against the built extension under the repo's CI flags: the newly declared symbols match the runtime exactly, with no new discrepancies. The cross-binding parity gate stays clean. Co-authored-by: preview <noreply@anthropic.com>
1 parent 58981cc commit 76fbda2

2 files changed

Lines changed: 372 additions & 0 deletions

File tree

sdks/python/python/thetadatadx/__init__.pyi

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,255 @@ class SecType:
394394
...
395395

396396

397+
# ─────────────────────────────────────────────────────────────────────
398+
# String-valued parameter enums
399+
# ─────────────────────────────────────────────────────────────────────
400+
#
401+
# Each class is a frozen, value-comparable handle whose members are the
402+
# only valid instances. The backing ``value`` is the lowercase wire token
403+
# the endpoints expect; ``str(member)`` returns that token and ``repr``
404+
# returns ``"<Class>.<token>"``. Instances are hashable and compare by
405+
# value, so they are usable as dict keys and in sets.
406+
407+
408+
@final
409+
class Right:
410+
"""Option right accepted by the contract and request builders."""
411+
412+
CALL: Right
413+
"""Call options."""
414+
PUT: Right
415+
"""Put options."""
416+
BOTH: Right
417+
"""Both calls and puts."""
418+
419+
@property
420+
def value(self) -> str:
421+
"""The wire token for this right (e.g. ``"call"``)."""
422+
...
423+
424+
def __str__(self) -> str:
425+
"""Return the wire token (e.g. ``"call"``)."""
426+
...
427+
428+
def __repr__(self) -> str:
429+
"""Return a representation (e.g. ``"Right.call"``)."""
430+
...
431+
432+
def __eq__(self, other: object) -> bool:
433+
"""Return whether ``other`` is the same right."""
434+
...
435+
436+
def __hash__(self) -> int:
437+
"""Return a hash consistent with :meth:`__eq__`."""
438+
...
439+
440+
441+
@final
442+
class Venue:
443+
"""Quote-venue selector for venue-scoped quote requests."""
444+
445+
NQB: Venue
446+
"""The national best bid and offer composite venue."""
447+
UTP_CTA: Venue
448+
"""The combined UTP / CTA tape venue."""
449+
450+
@property
451+
def value(self) -> str:
452+
"""The wire token for this venue (e.g. ``"nqb"``)."""
453+
...
454+
455+
def __str__(self) -> str:
456+
"""Return the wire token (e.g. ``"nqb"``)."""
457+
...
458+
459+
def __repr__(self) -> str:
460+
"""Return a representation (e.g. ``"Venue.nqb"``)."""
461+
...
462+
463+
def __eq__(self, other: object) -> bool:
464+
"""Return whether ``other`` is the same venue."""
465+
...
466+
467+
def __hash__(self) -> int:
468+
"""Return a hash consistent with :meth:`__eq__`."""
469+
...
470+
471+
472+
@final
473+
class Interval:
474+
"""Aggregation interval for bar / OHLC historical requests."""
475+
476+
TICK: Interval
477+
"""Per-tick, no aggregation."""
478+
MS_10: Interval
479+
"""10-millisecond bars."""
480+
MS_100: Interval
481+
"""100-millisecond bars."""
482+
MS_500: Interval
483+
"""500-millisecond bars."""
484+
S_1: Interval
485+
"""1-second bars."""
486+
S_5: Interval
487+
"""5-second bars."""
488+
S_10: Interval
489+
"""10-second bars."""
490+
S_15: Interval
491+
"""15-second bars."""
492+
S_30: Interval
493+
"""30-second bars."""
494+
M_1: Interval
495+
"""1-minute bars."""
496+
M_5: Interval
497+
"""5-minute bars."""
498+
M_10: Interval
499+
"""10-minute bars."""
500+
M_15: Interval
501+
"""15-minute bars."""
502+
M_30: Interval
503+
"""30-minute bars."""
504+
H_1: Interval
505+
"""1-hour bars."""
506+
507+
@property
508+
def value(self) -> str:
509+
"""The wire token for this interval (e.g. ``"1m"``)."""
510+
...
511+
512+
def __str__(self) -> str:
513+
"""Return the wire token (e.g. ``"1m"``)."""
514+
...
515+
516+
def __repr__(self) -> str:
517+
"""Return a representation (e.g. ``"Interval.1m"``)."""
518+
...
519+
520+
def __eq__(self, other: object) -> bool:
521+
"""Return whether ``other`` is the same interval."""
522+
...
523+
524+
def __hash__(self) -> int:
525+
"""Return a hash consistent with :meth:`__eq__`."""
526+
...
527+
528+
529+
@final
530+
class RateType:
531+
"""Reference-rate selector for interest-rate requests."""
532+
533+
SOFR: RateType
534+
"""The Secured Overnight Financing Rate."""
535+
TREASURY_M1: RateType
536+
"""1-month Treasury rate."""
537+
TREASURY_M3: RateType
538+
"""3-month Treasury rate."""
539+
TREASURY_M6: RateType
540+
"""6-month Treasury rate."""
541+
TREASURY_Y1: RateType
542+
"""1-year Treasury rate."""
543+
TREASURY_Y2: RateType
544+
"""2-year Treasury rate."""
545+
TREASURY_Y3: RateType
546+
"""3-year Treasury rate."""
547+
TREASURY_Y5: RateType
548+
"""5-year Treasury rate."""
549+
TREASURY_Y7: RateType
550+
"""7-year Treasury rate."""
551+
TREASURY_Y10: RateType
552+
"""10-year Treasury rate."""
553+
TREASURY_Y20: RateType
554+
"""20-year Treasury rate."""
555+
TREASURY_Y30: RateType
556+
"""30-year Treasury rate."""
557+
558+
@property
559+
def value(self) -> str:
560+
"""The wire token for this rate (e.g. ``"sofr"``)."""
561+
...
562+
563+
def __str__(self) -> str:
564+
"""Return the wire token (e.g. ``"sofr"``)."""
565+
...
566+
567+
def __repr__(self) -> str:
568+
"""Return a representation (e.g. ``"RateType.sofr"``)."""
569+
...
570+
571+
def __eq__(self, other: object) -> bool:
572+
"""Return whether ``other`` is the same rate type."""
573+
...
574+
575+
def __hash__(self) -> int:
576+
"""Return a hash consistent with :meth:`__eq__`."""
577+
...
578+
579+
580+
@final
581+
class RequestType:
582+
"""Per-row request kind for the flat-file and bar request builders."""
583+
584+
TRADE: RequestType
585+
"""Trade rows."""
586+
QUOTE: RequestType
587+
"""Quote rows."""
588+
EOD: RequestType
589+
"""End-of-day summary rows."""
590+
OHLC: RequestType
591+
"""Open / high / low / close bar rows."""
592+
593+
@property
594+
def value(self) -> str:
595+
"""The wire token for this request type (e.g. ``"trade"``)."""
596+
...
597+
598+
def __str__(self) -> str:
599+
"""Return the wire token (e.g. ``"trade"``)."""
600+
...
601+
602+
def __repr__(self) -> str:
603+
"""Return a representation (e.g. ``"RequestType.trade"``)."""
604+
...
605+
606+
def __eq__(self, other: object) -> bool:
607+
"""Return whether ``other`` is the same request type."""
608+
...
609+
610+
def __hash__(self) -> int:
611+
"""Return a hash consistent with :meth:`__eq__`."""
612+
...
613+
614+
615+
@final
616+
class Version:
617+
"""Endpoint schema-version selector."""
618+
619+
LATEST: Version
620+
"""The latest schema version the server serves."""
621+
V1: Version
622+
"""The pinned first schema version."""
623+
624+
@property
625+
def value(self) -> str:
626+
"""The wire token for this version (e.g. ``"latest"``)."""
627+
...
628+
629+
def __str__(self) -> str:
630+
"""Return the wire token (e.g. ``"latest"``)."""
631+
...
632+
633+
def __repr__(self) -> str:
634+
"""Return a representation (e.g. ``"Version.latest"``)."""
635+
...
636+
637+
def __eq__(self, other: object) -> bool:
638+
"""Return whether ``other`` is the same version."""
639+
...
640+
641+
def __hash__(self) -> int:
642+
"""Return a hash consistent with :meth:`__eq__`."""
643+
...
644+
645+
397646
@final
398647
class Subscription:
399648
"""Typed market-data subscription (per-contract or full-stream)."""
@@ -1740,6 +1989,14 @@ class FlatFileRowList:
17401989
"""Return the number of rows."""
17411990
...
17421991

1992+
def __bool__(self) -> bool:
1993+
"""Return whether the list holds at least one row."""
1994+
...
1995+
1996+
def __repr__(self) -> str:
1997+
"""Return a representation (e.g. ``"FlatFileRowList(128 rows)"``)."""
1998+
...
1999+
17432000
def to_list(self) -> List[Any]:
17442001
"""Return the rows as a list of dicts, one dict per row."""
17452002
...
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
"""Cross-language lookup and conversion helpers.
2+
3+
The ``thetadatadx.util`` submodule exposes the same finite lookup tables
4+
and tick-field conversions the other language bindings share: trade and
5+
quote condition vocabulary, exchange name / symbol lookups, the
6+
calendar-day status vocabulary, the ``(date, ms-of-day)`` to epoch-
7+
milliseconds conversion, and the signed / unsigned trade-sequence
8+
re-encoding.
9+
10+
All functions are pure and side-effect free.
11+
12+
Example:
13+
>>> import thetadatadx.util as util
14+
>>> util.condition_name(0)
15+
'REGULAR'
16+
>>> util.exchange_symbol(3)
17+
'NYSE'
18+
"""
19+
20+
from __future__ import annotations
21+
22+
from typing import Optional
23+
24+
25+
def condition_name(code: int) -> str:
26+
"""Return the trade-condition name for ``code`` (e.g. ``"REGULAR"``).
27+
28+
Returns a placeholder name for codes outside the known table.
29+
"""
30+
...
31+
32+
33+
def condition_description(code: int) -> str:
34+
"""Return the human-readable trade-condition description for ``code``."""
35+
...
36+
37+
38+
def is_cancel(code: int) -> bool:
39+
"""Return whether the trade-condition ``code`` marks a cancellation."""
40+
...
41+
42+
43+
def updates_volume(code: int) -> bool:
44+
"""Return whether a trade with condition ``code`` updates daily volume."""
45+
...
46+
47+
48+
def quote_condition_name(code: int) -> str:
49+
"""Return the quote-condition name for ``code``."""
50+
...
51+
52+
53+
def quote_condition_description(code: int) -> str:
54+
"""Return the human-readable quote-condition description for ``code``."""
55+
...
56+
57+
58+
def is_firm(code: int) -> bool:
59+
"""Return whether the quote-condition ``code`` is a firm quote."""
60+
...
61+
62+
63+
def is_halted(code: int) -> bool:
64+
"""Return whether the quote-condition ``code`` marks a trading halt."""
65+
...
66+
67+
68+
def exchange_name(code: int) -> str:
69+
"""Return the exchange name for ``code`` (e.g. ``"NewYorkStockExchange"``)."""
70+
...
71+
72+
73+
def exchange_symbol(code: int) -> str:
74+
"""Return the short exchange symbol for ``code`` (e.g. ``"NYSE"``)."""
75+
...
76+
77+
78+
def calendar_status_name(code: int) -> str:
79+
"""Return the calendar-day status text for ``code``.
80+
81+
Maps ``0`` to ``"open"``, ``1`` to ``"early_close"``, ``2`` to
82+
``"full_close"``, and ``3`` to ``"weekend"``; returns ``"UNKNOWN"``
83+
for codes outside the table.
84+
"""
85+
...
86+
87+
88+
def timestamp_ms(date: int, ms_of_day: int) -> Optional[int]:
89+
"""Combine an Eastern-Time ``YYYYMMDD`` ``date`` and ``ms_of_day``.
90+
91+
Returns Unix epoch milliseconds (UTC, DST-aware), or ``None`` when
92+
``date`` is ``0`` or either input is out of domain. Usable with any
93+
``(date, *_ms_of_day)`` pair on the tick structs.
94+
"""
95+
...
96+
97+
98+
def sequence_signed_to_unsigned(signed_value: int) -> int:
99+
"""Convert a signed wire-encoded trade sequence to its unsigned form.
100+
101+
``signed_value`` must lie in the i32 wire range
102+
(``-2_147_483_648 ..= 2_147_483_647``); a value outside that domain
103+
is rejected with :class:`ValueError`.
104+
"""
105+
...
106+
107+
108+
def sequence_unsigned_to_signed(unsigned_value: int) -> int:
109+
"""Convert an unsigned monotonic trade sequence back to signed wire form.
110+
111+
``unsigned_value`` must lie in the unsigned wire range
112+
(``0 ..= 2**32 - 1``); a value above that domain is rejected with
113+
:class:`ValueError`.
114+
"""
115+
...

0 commit comments

Comments
 (0)