tastytrade 11.0.5__tar.gz → 11.1.0__tar.gz

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 (80) hide show
  1. {tastytrade-11.0.5 → tastytrade-11.1.0}/PKG-INFO +1 -1
  2. {tastytrade-11.0.5 → tastytrade-11.1.0}/pyproject.toml +1 -0
  3. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/__init__.py +1 -2
  4. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/account.py +0 -171
  5. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/market_sessions.py +19 -0
  6. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/order.py +3 -120
  7. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/session.py +5 -12
  8. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/streamer.py +2 -9
  9. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/utils.py +17 -0
  10. {tastytrade-11.0.5 → tastytrade-11.1.0}/tests/test_account.py +111 -54
  11. {tastytrade-11.0.5 → tastytrade-11.1.0}/tests/test_dxfeed.py +10 -17
  12. {tastytrade-11.0.5 → tastytrade-11.1.0}/tests/test_market_sessions.py +10 -0
  13. {tastytrade-11.0.5 → tastytrade-11.1.0}/tests/test_utils.py +69 -1
  14. {tastytrade-11.0.5 → tastytrade-11.1.0}/uv.lock +48 -0
  15. {tastytrade-11.0.5 → tastytrade-11.1.0}/.github/CONTRIBUTING.md +0 -0
  16. {tastytrade-11.0.5 → tastytrade-11.1.0}/.github/FUNDING.yml +0 -0
  17. {tastytrade-11.0.5 → tastytrade-11.1.0}/.github/pull_request_template.md +0 -0
  18. {tastytrade-11.0.5 → tastytrade-11.1.0}/.github/workflows/python-app.yml +0 -0
  19. {tastytrade-11.0.5 → tastytrade-11.1.0}/.github/workflows/python-publish-test.yml +0 -0
  20. {tastytrade-11.0.5 → tastytrade-11.1.0}/.github/workflows/python-publish.yml +0 -0
  21. {tastytrade-11.0.5 → tastytrade-11.1.0}/.gitignore +0 -0
  22. {tastytrade-11.0.5 → tastytrade-11.1.0}/.python-version +0 -0
  23. {tastytrade-11.0.5 → tastytrade-11.1.0}/.readthedocs.yaml +0 -0
  24. {tastytrade-11.0.5 → tastytrade-11.1.0}/LICENSE +0 -0
  25. {tastytrade-11.0.5 → tastytrade-11.1.0}/Makefile +0 -0
  26. {tastytrade-11.0.5 → tastytrade-11.1.0}/README.md +0 -0
  27. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/Makefile +0 -0
  28. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/account-streamer.rst +0 -0
  29. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/accounts.rst +0 -0
  30. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/api/account.rst +0 -0
  31. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/api/dxfeed.rst +0 -0
  32. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/api/instruments.rst +0 -0
  33. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/api/market-data.rst +0 -0
  34. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/api/market-sessions.rst +0 -0
  35. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/api/metrics.rst +0 -0
  36. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/api/order.rst +0 -0
  37. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/api/search.rst +0 -0
  38. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/api/session.rst +0 -0
  39. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/api/streamer.rst +0 -0
  40. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/api/utils.rst +0 -0
  41. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/api/watchlists.rst +0 -0
  42. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/conf.py +0 -0
  43. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/data-streamer.rst +0 -0
  44. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/img/netliq.png +0 -0
  45. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/index.rst +0 -0
  46. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/installation.rst +0 -0
  47. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/instruments.rst +0 -0
  48. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/make.bat +0 -0
  49. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/market-data.rst +0 -0
  50. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/market-sessions.rst +0 -0
  51. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/orders.rst +0 -0
  52. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/sessions.rst +0 -0
  53. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/sync-async.rst +0 -0
  54. {tastytrade-11.0.5 → tastytrade-11.1.0}/docs/watchlists.rst +0 -0
  55. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/dxfeed/__init__.py +0 -0
  56. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/dxfeed/candle.py +0 -0
  57. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/dxfeed/event.py +0 -0
  58. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/dxfeed/greeks.py +0 -0
  59. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/dxfeed/profile.py +0 -0
  60. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/dxfeed/quote.py +0 -0
  61. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/dxfeed/summary.py +0 -0
  62. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/dxfeed/theoprice.py +0 -0
  63. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/dxfeed/timeandsale.py +0 -0
  64. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/dxfeed/trade.py +0 -0
  65. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/dxfeed/underlying.py +0 -0
  66. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/instruments.py +0 -0
  67. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/market_data.py +0 -0
  68. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/metrics.py +0 -0
  69. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/py.typed +0 -0
  70. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/search.py +0 -0
  71. {tastytrade-11.0.5 → tastytrade-11.1.0}/tastytrade/watchlists.py +0 -0
  72. {tastytrade-11.0.5 → tastytrade-11.1.0}/tests/__init__.py +0 -0
  73. {tastytrade-11.0.5 → tastytrade-11.1.0}/tests/conftest.py +0 -0
  74. {tastytrade-11.0.5 → tastytrade-11.1.0}/tests/test_instruments.py +0 -0
  75. {tastytrade-11.0.5 → tastytrade-11.1.0}/tests/test_market_data.py +0 -0
  76. {tastytrade-11.0.5 → tastytrade-11.1.0}/tests/test_metrics.py +0 -0
  77. {tastytrade-11.0.5 → tastytrade-11.1.0}/tests/test_search.py +0 -0
  78. {tastytrade-11.0.5 → tastytrade-11.1.0}/tests/test_session.py +0 -0
  79. {tastytrade-11.0.5 → tastytrade-11.1.0}/tests/test_streamer.py +0 -0
  80. {tastytrade-11.0.5 → tastytrade-11.1.0}/tests/test_watchlists.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tastytrade
3
- Version: 11.0.5
3
+ Version: 11.1.0
4
4
  Summary: An unofficial, sync/async SDK for Tastytrade!
5
5
  Project-URL: Homepage, https://github.com/tastyware/tastytrade
6
6
  Project-URL: Documentation, https://tastyworks-api.rtfd.io
@@ -66,6 +66,7 @@ dev = [
66
66
  "proxy-py>=2.4.9",
67
67
  "sphinx-immaterial>=0.12.5",
68
68
  "mypy>=1.18.2",
69
+ "pandas-stubs>=2.2.2.240807",
69
70
  ]
70
71
 
71
72
  [tool.setuptools.package-data]
@@ -3,8 +3,7 @@ import logging
3
3
  API_URL = "https://api.tastyworks.com"
4
4
  API_VERSION = "20251101"
5
5
  CERT_URL = "https://api.cert.tastyworks.com"
6
- VAST_URL = "https://vast.tastyworks.com"
7
- VERSION = "11.0.5"
6
+ VERSION = "11.1.0"
8
7
 
9
8
  __version__ = VERSION
10
9
  version_str: str = f"tastyware/tastytrade:v{VERSION}"
@@ -2,17 +2,14 @@ from datetime import date, datetime
2
2
  from decimal import Decimal
3
3
  from typing import Any, Literal, cast, overload
4
4
 
5
- import httpx
6
5
  from pydantic import BaseModel, ConfigDict, model_validator
7
6
  from typing_extensions import Self
8
7
 
9
- from tastytrade import VAST_URL
10
8
  from tastytrade.order import (
11
9
  InstrumentType,
12
10
  NewComplexOrder,
13
11
  NewOrder,
14
12
  OrderAction,
15
- OrderChain,
16
13
  OrderStatus,
17
14
  PlacedComplexOrder,
18
15
  PlacedComplexOrderResponse,
@@ -28,7 +25,6 @@ from tastytrade.utils import (
28
25
  paginate,
29
26
  set_sign_for,
30
27
  today_in_new_york,
31
- validate_response,
32
28
  )
33
29
 
34
30
  TT_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ"
@@ -296,23 +292,6 @@ class MarginReport(TastytradeData):
296
292
  )
297
293
 
298
294
 
299
- class MarginRequirement(TastytradeData):
300
- """
301
- Dataclass containing general margin requirement information for a symbol.
302
- """
303
-
304
- underlying_symbol: str
305
- long_equity_initial: Decimal
306
- short_equity_initial: Decimal
307
- long_equity_maintenance: Decimal
308
- short_equity_maintenance: Decimal
309
- naked_option_standard: Decimal
310
- naked_option_minimum: Decimal
311
- naked_option_floor: Decimal
312
- clearing_identifier: str | None = None
313
- is_deleted: bool | None = None
314
-
315
-
316
295
  class NetLiqOhlc(TastytradeData):
317
296
  """
318
297
  Dataclass containing historical net liquidation data in OHLC format
@@ -334,23 +313,6 @@ class NetLiqOhlc(TastytradeData):
334
313
  time: str
335
314
 
336
315
 
337
- class PositionLimit(TastytradeData):
338
- """
339
- Dataclass containing information about general account limits.
340
- """
341
-
342
- account_number: str
343
- equity_order_size: int
344
- equity_option_order_size: int
345
- future_order_size: int
346
- future_option_order_size: int
347
- underlying_opening_order_limit: int
348
- equity_position_size: int
349
- equity_option_position_size: int
350
- future_position_size: int
351
- future_option_position_size: int
352
-
353
-
354
316
  class TradingStatus(TastytradeData):
355
317
  """
356
318
  Dataclass containing information about an account's trading status, such
@@ -1043,58 +1005,6 @@ class Account(TastytradeData):
1043
1005
  )
1044
1006
  return [NetLiqOhlc(**i) for i in data["items"]]
1045
1007
 
1046
- async def a_get_position_limit(self, session: Session) -> PositionLimit:
1047
- """
1048
- Get the maximum order size information for the account.
1049
-
1050
- :param session: the session to use for the request.
1051
- """
1052
- data = await session._a_get(f"/accounts/{self.account_number}/position-limit")
1053
- return PositionLimit(**data)
1054
-
1055
- def get_position_limit(self, session: Session) -> PositionLimit:
1056
- """
1057
- Get the maximum order size information for the account.
1058
-
1059
- :param session: the session to use for the request.
1060
- """
1061
- data = session._get(f"/accounts/{self.account_number}/position-limit")
1062
- return PositionLimit(**data)
1063
-
1064
- async def a_get_effective_margin_requirements(
1065
- self, session: Session, symbol: str
1066
- ) -> MarginRequirement:
1067
- """
1068
- Get the effective margin requirements for a given symbol.
1069
-
1070
- :param session:
1071
- the session to use for the request, can't be certification
1072
- :param symbol: the symbol to get margin requirements for.
1073
- """
1074
- if symbol:
1075
- symbol = symbol.replace("/", "%2F")
1076
- data = await session._a_get(
1077
- f"/accounts/{self.account_number}/margin-requirements/{symbol}/effective"
1078
- )
1079
- return MarginRequirement(**data)
1080
-
1081
- def get_effective_margin_requirements(
1082
- self, session: Session, symbol: str
1083
- ) -> MarginRequirement:
1084
- """
1085
- Get the effective margin requirements for a given symbol.
1086
-
1087
- :param session:
1088
- the session to use for the request, can't be certification
1089
- :param symbol: the symbol to get margin requirements for.
1090
- """
1091
- if symbol:
1092
- symbol = symbol.replace("/", "%2F")
1093
- data = session._get(
1094
- f"/accounts/{self.account_number}/margin-requirements/{symbol}/effective"
1095
- )
1096
- return MarginRequirement(**data)
1097
-
1098
1008
  async def a_get_margin_requirements(self, session: Session) -> MarginReport:
1099
1009
  """
1100
1010
  Get the margin report for the account, with total margin requirements
@@ -1500,84 +1410,3 @@ class Account(TastytradeData):
1500
1410
  ),
1501
1411
  )
1502
1412
  return PlacedOrder(**data)
1503
-
1504
- async def a_get_order_chains(
1505
- self,
1506
- session: Session,
1507
- symbol: str,
1508
- start_time: datetime,
1509
- end_time: datetime,
1510
- ) -> list[OrderChain]:
1511
- """
1512
- Get a list of order chains (open + rolls + close) for given symbol
1513
- over the given time frame, with total P/L, commissions, etc.
1514
-
1515
- Not supported for OAuth sessions--write Tasty to get this added!
1516
-
1517
- :param session: the session to use for the request.
1518
- :param symbol: the underlying symbol for the chains.
1519
- :param start_time: the beginning time of the query.
1520
- :param end_time: the ending time of the query.
1521
- """
1522
- params = {
1523
- "account-numbers[]": self.account_number,
1524
- "underlying-symbols[]": symbol,
1525
- "start-at": start_time.strftime(TT_DATE_FMT),
1526
- "end-at": end_time.strftime(TT_DATE_FMT),
1527
- "defer-open-winner-loser-filtering-to-frontend": False,
1528
- "per-page": 250,
1529
- }
1530
- headers = {
1531
- "Authorization": session.session_token,
1532
- "Accept": "application/json",
1533
- "Content-Type": "application/json",
1534
- }
1535
- async with httpx.AsyncClient() as client:
1536
- response = await client.get(
1537
- f"{VAST_URL}/order-chains",
1538
- headers=headers,
1539
- params=params, # type: ignore[arg-type]
1540
- )
1541
- validate_response(response)
1542
- chains = response.json()["data"]["items"]
1543
- return [OrderChain(**i) for i in chains]
1544
-
1545
- def get_order_chains(
1546
- self,
1547
- session: Session,
1548
- symbol: str,
1549
- start_time: datetime,
1550
- end_time: datetime,
1551
- ) -> list[OrderChain]:
1552
- """
1553
- Get a list of order chains (open + rolls + close) for given symbol
1554
- over the given time frame, with total P/L, commissions, etc.
1555
-
1556
- Not supported for OAuth sessions--write Tasty to get this added!
1557
-
1558
- :param session: the session to use for the request.
1559
- :param symbol: the underlying symbol for the chains.
1560
- :param start_time: the beginning time of the query.
1561
- :param end_time: the ending time of the query.
1562
- """
1563
- params = {
1564
- "account-numbers[]": self.account_number,
1565
- "underlying-symbols[]": symbol,
1566
- "start-at": start_time.strftime(TT_DATE_FMT),
1567
- "end-at": end_time.strftime(TT_DATE_FMT),
1568
- "defer-open-winner-loser-filtering-to-frontend": False,
1569
- "per-page": 250,
1570
- }
1571
- headers = {
1572
- "Authorization": session.session_token,
1573
- "Accept": "application/json",
1574
- "Content-Type": "application/json",
1575
- }
1576
- response = httpx.get(
1577
- f"{VAST_URL}/order-chains",
1578
- headers=headers,
1579
- params=params, # type: ignore[arg-type]
1580
- )
1581
- validate_response(response)
1582
- chains = response.json()["data"]["items"]
1583
- return [OrderChain(**i) for i in chains]
@@ -118,6 +118,25 @@ def get_market_holidays(session: Session) -> MarketCalendar:
118
118
  return MarketCalendar(**data)
119
119
 
120
120
 
121
+ async def a_get_futures_holidays(
122
+ session: Session, exchange: ExchangeType
123
+ ) -> MarketCalendar:
124
+ """
125
+ Retrieves market calendar for half days and holidays for a futures exchange.
126
+
127
+ :param session: active user session to use
128
+ :param exchange: exchange to fetch calendar for
129
+ """
130
+ data = await session._a_get(f"/market-time/futures/holidays/{exchange.value}")
131
+ return MarketCalendar(**data)
132
+
133
+
121
134
  def get_futures_holidays(session: Session, exchange: ExchangeType) -> MarketCalendar:
135
+ """
136
+ Retrieves market calendar for half days and holidays for a futures exchange.
137
+
138
+ :param session: active user session to use
139
+ :param exchange: exchange to fetch calendar for
140
+ """
122
141
  data = session._get(f"/market-time/futures/holidays/{exchange.value}")
123
142
  return MarketCalendar(**data)
@@ -3,7 +3,7 @@ from decimal import Decimal
3
3
  from enum import Enum
4
4
  from typing import Any
5
5
 
6
- from pydantic import computed_field, field_serializer, model_validator
6
+ from pydantic import ConfigDict, computed_field, field_serializer, model_validator
7
7
 
8
8
  from tastytrade import version_str
9
9
  from tastytrade.utils import (
@@ -237,6 +237,8 @@ class NewOrder(TastytradeData):
237
237
  modifying existing orders.
238
238
  """
239
239
 
240
+ model_config = ConfigDict(extra="allow")
241
+
240
242
  time_in_force: OrderTimeInForce
241
243
  order_type: OrderType
242
244
  source: str = version_str
@@ -435,122 +437,3 @@ class PlacedOrderResponse(TastytradeData):
435
437
  fee_calculation: FeeCalculation | None = None
436
438
  warnings: list[Message] | None = None
437
439
  errors: list[Message] | None = None
438
-
439
-
440
- class OrderChainEntry(TastytradeData):
441
- """
442
- Dataclass containing information about a single order in an order chain.
443
- """
444
-
445
- symbol: str
446
- instrument_type: InstrumentType
447
- quantity: str
448
- quantity_type: str
449
- quantity_numeric: Decimal
450
-
451
-
452
- class OrderChainLeg(TastytradeData):
453
- """
454
- Dataclass containing information about a single leg in an order
455
- from an order chain.
456
- """
457
-
458
- symbol: str
459
- instrument_type: InstrumentType
460
- action: OrderAction
461
- fill_quantity: Decimal
462
- order_quantity: Decimal
463
-
464
-
465
- class OrderChainNode(TastytradeData):
466
- """
467
- Dataclass containing information about a single node in an order chain.
468
- """
469
-
470
- node_type: str
471
- id: str
472
- description: str
473
- occurred_at: datetime | None = None
474
- total_fees: Decimal | None = None
475
- total_fill_cost: Decimal | None = None
476
- gcd_quantity: Decimal | None = None
477
- fill_cost_per_quantity: Decimal | None = None
478
- order_fill_count: int | None = None
479
- roll: bool | None = None
480
- legs: list[OrderChainLeg] | None = None
481
- entries: list[OrderChainEntry] | None = None
482
-
483
- @model_validator(mode="before")
484
- @classmethod
485
- def validate_price_effects(cls, data: Any) -> Any:
486
- return set_sign_for(
487
- data,
488
- [
489
- "total_fees",
490
- "total_fill_cost",
491
- "fill_cost_per_quantity",
492
- ],
493
- )
494
-
495
-
496
- class ComputedData(TastytradeData):
497
- """
498
- Dataclass containing computed data about an order chain.
499
- """
500
-
501
- open: bool
502
- updated_at: datetime
503
- total_fees: Decimal
504
- total_commissions: Decimal
505
- realized_gain: Decimal
506
- realized_gain_with_fees: Decimal
507
- winner_realized_and_closed: bool
508
- winner_realized: bool
509
- winner_realized_with_fees: bool
510
- roll_count: int
511
- opened_at: datetime
512
- last_occurred_at: datetime
513
- started_at_days_to_expiration: int
514
- duration: int
515
- total_opening_cost: Decimal
516
- total_closing_cost: Decimal
517
- total_cost: Decimal
518
- gcd_open_quantity: Decimal
519
- fees_missing: bool
520
- open_entries: list[OrderChainEntry]
521
- total_cost_per_unit: Decimal | None = None
522
-
523
- @model_validator(mode="before")
524
- @classmethod
525
- def validate_price_effects(cls, data: Any) -> Any:
526
- return set_sign_for(
527
- data,
528
- [
529
- "total_fees",
530
- "total_commissions",
531
- "realized_gain",
532
- "realized_gain_with_fees",
533
- "total_opening_cost",
534
- "total_closing_cost",
535
- "total_cost",
536
- "total_cost_per_unit",
537
- ],
538
- )
539
-
540
-
541
- class OrderChain(TastytradeData):
542
- """
543
- Dataclass containing information about an order chain: a group of orders
544
- for a specific underlying, such as total P/L, rolls, current P/L in a
545
- symbol, etc.
546
- """
547
-
548
- id: int
549
- account_number: str
550
- description: str
551
- underlying_symbol: str
552
- computed_data: ComputedData
553
- lite_nodes: list[OrderChainNode]
554
- lite_nodes_sizes: int | None = None
555
- updated_at: datetime | None = None
556
- created_at: datetime | None = None
@@ -233,7 +233,7 @@ class Customer(TastytradeData):
233
233
  desk_customer_id: str | None = None
234
234
  entity: CustomerEntity | None = None
235
235
  family_member_names: str | None = None
236
- has_institutional_assets: str | None = None
236
+ has_institutional_assets: str | bool | None = None
237
237
  industry_affiliation_firm: str | None = None
238
238
  is_investment_adviser: bool | None = None
239
239
  listed_affiliation_symbol: str | None = None
@@ -289,9 +289,7 @@ class Session:
289
289
  self.streamer_expiration = now_in_new_york()
290
290
  self.refresh()
291
291
 
292
- def _streamer_refresh(self) -> None:
293
- # Pull streamer tokens and urls
294
- data = self._get("/api-quote-tokens")
292
+ def _streamer_refresh(self, data: Any) -> None:
295
293
  # Auth token for dxfeed websocket
296
294
  self.streamer_token = data["token"]
297
295
  # URL for dxfeed websocket
@@ -331,7 +329,8 @@ class Session:
331
329
  self.async_client.headers.update(auth_headers)
332
330
  # update the streamer token if necessary
333
331
  if not self.is_test and self.streamer_expiration < self.session_expiration:
334
- self._streamer_refresh()
332
+ data = self._get("/api-quote-tokens")
333
+ self._streamer_refresh(data)
335
334
 
336
335
  async def a_refresh(self) -> None:
337
336
  """
@@ -365,13 +364,7 @@ class Session:
365
364
  if not self.is_test and self.streamer_expiration < self.session_expiration:
366
365
  # Pull streamer tokens and urls
367
366
  data = await self._a_get("/api-quote-tokens")
368
- # Auth token for dxfeed websocket
369
- self.streamer_token = data["token"]
370
- # URL for dxfeed websocket
371
- self.dxlink_url = data["dxlink-url"]
372
- self.streamer_expiration = datetime.fromisoformat(
373
- data["expires-at"].replace("Z", "+00:00")
374
- )
367
+ self._streamer_refresh(data)
375
368
 
376
369
  async def a_get_customer(self) -> Customer:
377
370
  """
@@ -29,12 +29,7 @@ from tastytrade.dxfeed import (
29
29
  Trade,
30
30
  Underlying,
31
31
  )
32
- from tastytrade.order import (
33
- InstrumentType,
34
- OrderChain,
35
- PlacedComplexOrder,
36
- PlacedOrder,
37
- )
32
+ from tastytrade.order import InstrumentType, PlacedComplexOrder, PlacedOrder
38
33
  from tastytrade.session import Session
39
34
  from tastytrade.utils import TastytradeData, TastytradeError, set_sign_for
40
35
  from tastytrade.watchlists import Watchlist
@@ -133,7 +128,6 @@ AlertType: TypeAlias = (
133
128
  | ExternalTransaction
134
129
  | PlacedComplexOrder
135
130
  | PlacedOrder
136
- | OrderChain
137
131
  | CurrentPosition
138
132
  | QuoteAlert
139
133
  | TradingStatus
@@ -146,7 +140,6 @@ MAP_ALERTS: dict[str, type[AlertType]] = {
146
140
  "ComplexOrder": PlacedComplexOrder,
147
141
  "ExternalTransaction": ExternalTransaction,
148
142
  "Order": PlacedOrder,
149
- "OrderChain": OrderChain,
150
143
  "CurrentPosition": CurrentPosition,
151
144
  "QuoteAlert": QuoteAlert,
152
145
  "TradingStatus": TradingStatus,
@@ -761,7 +754,7 @@ class DXLinkStreamer:
761
754
  }
762
755
 
763
756
  def dict_from_schema(event_class: Any) -> dict[str, list[Any]]:
764
- schema = event_class.schema()
757
+ schema = event_class.model_json_schema()
765
758
  return {schema["title"]: list(schema["properties"].keys())}
766
759
 
767
760
  cls = MAP_EVENTS[event_type]
@@ -6,6 +6,7 @@ from typing import Any, Type, TypeVar, cast
6
6
  from zoneinfo import ZoneInfo
7
7
 
8
8
  from httpx import AsyncClient, Client, Response
9
+ from pandas import Timestamp
9
10
  from pandas_market_calendars import get_calendar # type: ignore[import-untyped]
10
11
  from pydantic import BaseModel, ConfigDict
11
12
 
@@ -40,6 +41,22 @@ def today_in_new_york() -> date:
40
41
  return now_in_new_york().date()
41
42
 
42
43
 
44
+ def is_market_open_now() -> bool:
45
+ """
46
+ Check if the market is currently open.
47
+ """
48
+ today = today_in_new_york()
49
+ sched = NYSE.schedule(start_date=today, end_date=today)
50
+ if sched.empty:
51
+ # Closed full day (weekend or holiday)
52
+ return False
53
+
54
+ # Use iloc[0] since schedule has only one row for a single day
55
+ market_open: Timestamp = sched.iloc[0]["market_open"]
56
+ market_close: Timestamp = sched.iloc[0]["market_close"]
57
+ return market_open <= now_in_new_york() < market_close
58
+
59
+
43
60
  def is_market_open_on(day: date | None = None) -> bool:
44
61
  """
45
62
  Returns whether the market was/is/will be open at ANY point
@@ -3,7 +3,7 @@ from datetime import datetime
3
3
  from decimal import Decimal
4
4
  from time import sleep
5
5
 
6
- from pytest import fixture, raises
6
+ import pytest
7
7
 
8
8
  from tastytrade import Account, Session
9
9
  from tastytrade.instruments import Equity
@@ -15,17 +15,16 @@ from tastytrade.order import (
15
15
  OrderType,
16
16
  PlacedOrder,
17
17
  )
18
- from tastytrade.utils import TastytradeError
19
18
 
20
19
 
21
- @fixture(scope="module")
20
+ @pytest.fixture(scope="module")
22
21
  def account_number() -> str:
23
22
  account_number = os.getenv("TT_ACCOUNT")
24
23
  assert account_number is not None
25
24
  return account_number
26
25
 
27
26
 
28
- @fixture(scope="module")
27
+ @pytest.fixture(scope="module")
29
28
  async def account(session: Session, account_number: str, aiolib: str) -> Account:
30
29
  return Account.get(session, account_number)
31
30
 
@@ -55,27 +54,37 @@ def test_get_positions(session: Session, account: Account):
55
54
 
56
55
 
57
56
  def test_get_history(session: Session, account: Account):
58
- account.get_history(session, page_offset=0)
57
+ hist = account.get_history(session, page_offset=0)
58
+ tid = hist[0].id
59
+ account.get_transaction(session, tid)
59
60
 
60
61
 
61
62
  def test_get_total_fees(session: Session, account: Account):
62
63
  account.get_total_fees(session)
63
64
 
64
65
 
65
- def test_get_position_limit(session: Session, account: Account):
66
- account.get_position_limit(session)
67
-
68
-
69
66
  def test_get_margin_requirements(session: Session, account: Account):
70
67
  account.get_margin_requirements(session)
71
68
 
72
69
 
73
- def test_get_net_liquidating_value_history(session: Session, account: Account):
74
- account.get_net_liquidating_value_history(session, time_back="1y")
75
-
76
-
77
- def test_get_effective_margin_requirements(session: Session, account: Account):
78
- account.get_effective_margin_requirements(session, "SPY")
70
+ @pytest.mark.parametrize(
71
+ "time_back, start_time",
72
+ [
73
+ ("1y", None),
74
+ (None, datetime(2024, 1, 1)),
75
+ pytest.param(None, None, marks=pytest.mark.xfail, id="intentional_fail"),
76
+ ],
77
+ )
78
+ def test_get_net_liquidating_value_history(
79
+ session: Session,
80
+ account: Account,
81
+ time_back: str | None,
82
+ start_time: datetime | None,
83
+ ):
84
+ sleep(1)
85
+ account.get_net_liquidating_value_history(
86
+ session, time_back=time_back, start_time=start_time
87
+ )
79
88
 
80
89
 
81
90
  def test_get_order_history(session: Session, account: Account):
@@ -116,31 +125,37 @@ async def test_get_positions_async(session: Session, account: Account):
116
125
 
117
126
 
118
127
  async def test_get_history_async(session: Session, account: Account):
119
- await account.a_get_history(session, page_offset=0)
128
+ hist = await account.a_get_history(session, page_offset=0)
129
+ tid = hist[0].id
130
+ await account.a_get_transaction(session, tid)
120
131
 
121
132
 
122
133
  async def test_get_total_fees_async(session: Session, account: Account):
123
134
  await account.a_get_total_fees(session)
124
135
 
125
136
 
126
- async def test_get_position_limit_async(session: Session, account: Account):
127
- await account.a_get_position_limit(session)
128
-
129
-
130
137
  async def test_get_margin_requirements_async(session: Session, account: Account):
131
138
  await account.a_get_margin_requirements(session)
132
139
 
133
140
 
141
+ @pytest.mark.parametrize(
142
+ "time_back, start_time",
143
+ [
144
+ ("1y", None),
145
+ (None, datetime(2024, 1, 1)),
146
+ pytest.param(None, None, marks=pytest.mark.xfail, id="intentional_fail"),
147
+ ],
148
+ )
134
149
  async def test_get_net_liquidating_value_history_async(
135
- session: Session, account: Account
136
- ):
137
- await account.a_get_net_liquidating_value_history(session, time_back="1y")
138
-
139
-
140
- async def test_get_effective_margin_requirements_async(
141
- session: Session, account: Account
150
+ session: Session,
151
+ account: Account,
152
+ time_back: str | None,
153
+ start_time: datetime | None,
142
154
  ):
143
- await account.a_get_effective_margin_requirements(session, "SPY")
155
+ sleep(1)
156
+ await account.a_get_net_liquidating_value_history(
157
+ session, time_back=time_back, start_time=start_time
158
+ )
144
159
 
145
160
 
146
161
  async def test_get_order_history_async(session: Session, account: Account):
@@ -155,26 +170,10 @@ async def test_get_live_orders_async(session: Session, account: Account):
155
170
  await account.a_get_live_orders(session)
156
171
 
157
172
 
158
- def test_get_order_chains(session: Session, account: Account):
159
- start_time = datetime(2024, 1, 1, 0, 0, 0)
160
- end_time = datetime.now()
161
- with raises(TastytradeError):
162
- account.get_order_chains(session, "F", start_time=start_time, end_time=end_time)
163
-
164
-
165
- async def test_get_order_chains_async(session: Session, account: Account):
166
- start_time = datetime(2024, 1, 1, 0, 0, 0)
167
- end_time = datetime.now()
168
- with raises(TastytradeError):
169
- await account.a_get_order_chains(
170
- session, "F", start_time=start_time, end_time=end_time
171
- )
172
-
173
-
174
- @fixture(scope="module")
173
+ @pytest.fixture(scope="module")
175
174
  def new_order(session: Session) -> NewOrder:
176
175
  symbol = Equity.get(session, "F")
177
- leg = symbol.build_leg(Decimal(1), OrderAction.BUY_TO_OPEN)
176
+ leg = symbol.build_leg(1, OrderAction.BUY_TO_OPEN)
178
177
  return NewOrder(
179
178
  time_in_force=OrderTimeInForce.DAY,
180
179
  order_type=OrderType.LIMIT,
@@ -183,7 +182,7 @@ def new_order(session: Session) -> NewOrder:
183
182
  )
184
183
 
185
184
 
186
- @fixture(scope="module")
185
+ @pytest.fixture(scope="module")
187
186
  def notional_order(session: Session) -> NewOrder:
188
187
  symbol = Equity.get(session, "AAPL")
189
188
  leg = symbol.build_leg(None, OrderAction.BUY_TO_OPEN)
@@ -195,7 +194,7 @@ def notional_order(session: Session) -> NewOrder:
195
194
  )
196
195
 
197
196
 
198
- @fixture(scope="module")
197
+ @pytest.fixture(scope="module")
199
198
  def placed_order(
200
199
  session: Session, account: Account, new_order: NewOrder
201
200
  ) -> PlacedOrder:
@@ -230,7 +229,7 @@ def test_replace_and_delete_order(
230
229
  def test_place_oco_order(session: Session, account: Account):
231
230
  # account must have a share of F for this to work
232
231
  symbol = Equity.get(session, "F")
233
- closing = symbol.build_leg(Decimal(1), OrderAction.SELL_TO_CLOSE)
232
+ closing = symbol.build_leg(1, OrderAction.SELL_TO_CLOSE)
234
233
  oco = NewComplexOrder(
235
234
  orders=[
236
235
  NewOrder(
@@ -256,8 +255,8 @@ def test_place_oco_order(session: Session, account: Account):
256
255
 
257
256
  def test_place_otoco_order(session: Session, account: Account):
258
257
  symbol = Equity.get(session, "AAPL")
259
- opening = symbol.build_leg(Decimal(1), OrderAction.BUY_TO_OPEN)
260
- closing = symbol.build_leg(Decimal(1), OrderAction.SELL_TO_CLOSE)
258
+ opening = symbol.build_leg(1, OrderAction.BUY_TO_OPEN)
259
+ closing = symbol.build_leg(1, OrderAction.SELL_TO_CLOSE)
261
260
  otoco = NewComplexOrder(
262
261
  trigger_order=NewOrder(
263
262
  time_in_force=OrderTimeInForce.DAY,
@@ -290,7 +289,7 @@ def test_get_live_complex_orders(session: Session, account: Account):
290
289
  assert orders != []
291
290
 
292
291
 
293
- @fixture(scope="module")
292
+ @pytest.fixture(scope="module")
294
293
  async def placed_order_async(
295
294
  session: Session, account: Account, new_order: NewOrder
296
295
  ) -> PlacedOrder:
@@ -328,8 +327,8 @@ async def test_replace_and_delete_order_async(
328
327
  async def test_place_complex_order_async(session: Session, account: Account):
329
328
  sleep(3)
330
329
  symbol = Equity.get(session, "AAPL")
331
- opening = symbol.build_leg(Decimal(1), OrderAction.BUY_TO_OPEN)
332
- closing = symbol.build_leg(Decimal(1), OrderAction.SELL_TO_CLOSE)
330
+ opening = symbol.build_leg(1, OrderAction.BUY_TO_OPEN)
331
+ closing = symbol.build_leg(1, OrderAction.SELL_TO_CLOSE)
333
332
  otoco = NewComplexOrder(
334
333
  trigger_order=NewOrder(
335
334
  time_in_force=OrderTimeInForce.DAY,
@@ -360,3 +359,61 @@ async def test_place_complex_order_async(session: Session, account: Account):
360
359
  async def test_get_live_complex_orders_async(session: Session, account: Account):
361
360
  orders = await account.a_get_live_complex_orders(session)
362
361
  assert orders != []
362
+
363
+
364
+ async def test_place_oco_order_async(session: Session, account: Account):
365
+ # account must have a share of F for this to work
366
+ symbol = await Equity.a_get(session, "F")
367
+ closing = symbol.build_leg(1, OrderAction.SELL_TO_CLOSE)
368
+ oco = NewComplexOrder(
369
+ orders=[
370
+ NewOrder(
371
+ time_in_force=OrderTimeInForce.GTC,
372
+ order_type=OrderType.LIMIT,
373
+ legs=[closing],
374
+ price=Decimal(1000), # will never fill
375
+ ),
376
+ NewOrder(
377
+ time_in_force=OrderTimeInForce.GTC,
378
+ order_type=OrderType.STOP,
379
+ legs=[closing],
380
+ stop_trigger=Decimal(1), # will never fill
381
+ ),
382
+ ]
383
+ )
384
+ resp2 = await account.a_place_complex_order(session, oco, dry_run=False)
385
+ sleep(3)
386
+ # test get complex order
387
+ _ = await account.a_get_complex_order(session, resp2.complex_order.id)
388
+ await account.a_delete_complex_order(session, resp2.complex_order.id)
389
+
390
+
391
+ async def test_place_otoco_order_async(session: Session, account: Account):
392
+ symbol = await Equity.a_get(session, "AAPL")
393
+ opening = symbol.build_leg(1, OrderAction.BUY_TO_OPEN)
394
+ closing = symbol.build_leg(1, OrderAction.SELL_TO_CLOSE)
395
+ otoco = NewComplexOrder(
396
+ trigger_order=NewOrder(
397
+ time_in_force=OrderTimeInForce.DAY,
398
+ order_type=OrderType.LIMIT,
399
+ legs=[opening],
400
+ price=Decimal("-2"), # won't fill
401
+ ),
402
+ orders=[
403
+ NewOrder(
404
+ time_in_force=OrderTimeInForce.GTC,
405
+ order_type=OrderType.LIMIT,
406
+ legs=[closing],
407
+ price=Decimal(400), # won't fill
408
+ ),
409
+ NewOrder(
410
+ time_in_force=OrderTimeInForce.GTC,
411
+ order_type=OrderType.STOP,
412
+ legs=[closing],
413
+ stop_trigger=Decimal("1.5"), # won't fill
414
+ ),
415
+ ],
416
+ )
417
+ resp = await account.a_place_complex_order(session, otoco, dry_run=False)
418
+ sleep(3)
419
+ await account.a_delete_complex_order(session, resp.complex_order.id)
@@ -29,21 +29,14 @@ def test_parse_infinities_and_nan():
29
29
  assert summary.day_high_price is None
30
30
 
31
31
 
32
- def test_malformatted_data():
32
+ quote_data = ["SPY", 0, 0, 0, 0, "Q", 0, "Q", 576.88, 576.9, 230.0, 300.0]
33
+
34
+
35
+ def test_wrong_number_data_fields():
33
36
  with pytest.raises(TastytradeError):
34
- quote_data = [
35
- "SPY",
36
- 0,
37
- 0,
38
- 0,
39
- 0,
40
- "Q",
41
- 0,
42
- "Q",
43
- 576.88,
44
- 576.9,
45
- 230.0,
46
- 300.0,
47
- "extra",
48
- ]
49
- _ = Quote.from_stream(quote_data)
37
+ _ = Quote.from_stream(quote_data + ["extra"])
38
+
39
+
40
+ def test_bad_extra_data():
41
+ extra_data = ["SPY", 0, "bad", 0, 0, "Q", 0, "Q", 576.88, 576.9, 230.0, 300.0]
42
+ _ = Quote.from_stream(quote_data + extra_data)
@@ -3,8 +3,10 @@ from pytest import fixture
3
3
  from tastytrade import Session
4
4
  from tastytrade.market_sessions import (
5
5
  ExchangeType,
6
+ a_get_futures_holidays,
6
7
  a_get_market_holidays,
7
8
  a_get_market_sessions,
9
+ get_futures_holidays,
8
10
  get_market_holidays,
9
11
  get_market_sessions,
10
12
  )
@@ -25,9 +27,17 @@ async def test_get_market_holidays_async(session: Session):
25
27
  await a_get_market_holidays(session)
26
28
 
27
29
 
30
+ async def test_get_future_holidays_async(session: Session):
31
+ await a_get_futures_holidays(session, ExchangeType.CME)
32
+
33
+
28
34
  def test_get_market_sessions(session: Session, exchanges: list[ExchangeType]):
29
35
  get_market_sessions(session, exchanges=exchanges)
30
36
 
31
37
 
32
38
  def test_get_market_holidays(session: Session):
33
39
  get_market_holidays(session)
40
+
41
+
42
+ def test_get_future_holidays(session: Session):
43
+ get_futures_holidays(session, ExchangeType.CME)
@@ -1,6 +1,8 @@
1
- from datetime import date
1
+ from datetime import date, datetime
2
+ from unittest.mock import patch
2
3
 
3
4
  from tastytrade.utils import (
5
+ TZ,
4
6
  get_future_fx_monthly,
5
7
  get_future_grain_monthly,
6
8
  get_future_index_monthly,
@@ -9,6 +11,7 @@ from tastytrade.utils import (
9
11
  get_future_treasury_monthly,
10
12
  get_tasty_monthly,
11
13
  get_third_friday,
14
+ is_market_open_now,
12
15
  today_in_new_york,
13
16
  )
14
17
 
@@ -158,3 +161,68 @@ def test_get_future_index_monthly():
158
161
  ]
159
162
  for exp in exps:
160
163
  assert get_future_index_monthly(exp) == exp
164
+
165
+
166
+ def test_is_market_open_now():
167
+ # Use a known Tuesday (March 12, 2024) for weekday tests
168
+ tuesday = datetime(2024, 3, 12, 10, 0, 0, tzinfo=TZ)
169
+
170
+ # Test market open during trading hours (10:00 AM on a weekday)
171
+ with patch("tastytrade.utils.datetime") as mock_datetime:
172
+ mock_datetime.now.return_value = tuesday
173
+ assert is_market_open_now() is True
174
+
175
+ # Test market closed before opening (8:00 AM on a weekday)
176
+ with patch("tastytrade.utils.datetime") as mock_datetime:
177
+ mock_datetime.now.return_value = datetime(2024, 3, 12, 8, 0, 0, tzinfo=TZ)
178
+ assert is_market_open_now() is False
179
+
180
+ # Test market closed after closing (5:00 PM on a weekday)
181
+ with patch("tastytrade.utils.datetime") as mock_datetime:
182
+ mock_datetime.now.return_value = datetime(2024, 3, 12, 17, 0, 0, tzinfo=TZ)
183
+ assert is_market_open_now() is False
184
+
185
+ # Test market closed on weekend (Saturday, March 16, 2024)
186
+ with patch("tastytrade.utils.datetime") as mock_datetime:
187
+ mock_datetime.now.return_value = datetime(2024, 3, 16, 12, 0, 0, tzinfo=TZ)
188
+ assert is_market_open_now() is False
189
+
190
+ # Test edge case: exactly 9:30 AM (market opens)
191
+ with patch("tastytrade.utils.datetime") as mock_datetime:
192
+ mock_datetime.now.return_value = datetime(2024, 3, 12, 9, 30, 0, tzinfo=TZ)
193
+ assert is_market_open_now() is True
194
+
195
+ # Test edge case: just before 9:30 AM (9:29 AM)
196
+ with patch("tastytrade.utils.datetime") as mock_datetime:
197
+ mock_datetime.now.return_value = datetime(2024, 3, 12, 9, 29, 0, tzinfo=TZ)
198
+ assert is_market_open_now() is False
199
+
200
+ # Test edge case: exactly 4:00 PM (market closes, should be False)
201
+ with patch("tastytrade.utils.datetime") as mock_datetime:
202
+ mock_datetime.now.return_value = datetime(2024, 3, 12, 16, 0, 0, tzinfo=TZ)
203
+ assert is_market_open_now() is False
204
+
205
+ # Test edge case: just before 4:00 PM (3:59 PM)
206
+ with patch("tastytrade.utils.datetime") as mock_datetime:
207
+ mock_datetime.now.return_value = datetime(2024, 3, 12, 15, 59, 0, tzinfo=TZ)
208
+ assert is_market_open_now() is True
209
+
210
+ # Test edge case: Black Friday just before market open
211
+ with patch("tastytrade.utils.datetime") as mock_datetime:
212
+ mock_datetime.now.return_value = datetime(2024, 11, 29, 9, 29, 0, tzinfo=TZ)
213
+ assert is_market_open_now() is False
214
+
215
+ # Test edge case: Black Friday just after market open
216
+ with patch("tastytrade.utils.datetime") as mock_datetime:
217
+ mock_datetime.now.return_value = datetime(2024, 11, 29, 9, 30, 0, tzinfo=TZ)
218
+ assert is_market_open_now() is True
219
+
220
+ # Test edge case: Black Friday just before market close half day
221
+ with patch("tastytrade.utils.datetime") as mock_datetime:
222
+ mock_datetime.now.return_value = datetime(2024, 11, 29, 12, 59, 0, tzinfo=TZ)
223
+ assert is_market_open_now() is True
224
+
225
+ # Test edge case: Black Friday just after market close half day
226
+ with patch("tastytrade.utils.datetime") as mock_datetime:
227
+ mock_datetime.now.return_value = datetime(2024, 11, 29, 13, 0, 0, tzinfo=TZ)
228
+ assert is_market_open_now() is False
@@ -1161,6 +1161,42 @@ wheels = [
1161
1161
  { url = "https://files.pythonhosted.org/packages/09/c3/4410606429c38a5c62597b903c65706de4b163a3c40a6a0308636a43cc30/pandas_market_calendars-5.1.1-py3-none-any.whl", hash = "sha256:efe85e335c927824e2714a70f8ba769ef4bbebab924bd13e7f80180262d5f7ee", size = 127359, upload-time = "2025-06-22T17:38:16.657Z" },
1162
1162
  ]
1163
1163
 
1164
+ [[package]]
1165
+ name = "pandas-stubs"
1166
+ version = "2.2.2.240807"
1167
+ source = { registry = "https://pypi.org/simple" }
1168
+ resolution-markers = [
1169
+ "python_full_version < '3.10'",
1170
+ ]
1171
+ dependencies = [
1172
+ { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
1173
+ { name = "types-pytz", marker = "python_full_version < '3.10'" },
1174
+ ]
1175
+ sdist = { url = "https://files.pythonhosted.org/packages/1f/df/0da95bc75c76f1e012e0bc0b76da31faaf4254e94b9870f25e6311145e98/pandas_stubs-2.2.2.240807.tar.gz", hash = "sha256:64a559725a57a449f46225fbafc422520b7410bff9252b661a225b5559192a93", size = 103095, upload-time = "2024-08-07T12:30:54.538Z" }
1176
+ wheels = [
1177
+ { url = "https://files.pythonhosted.org/packages/0a/f9/22c91632ea1b4c6165952f677bf9ad95f9ac36ffd7ef3e6450144e6d8b1a/pandas_stubs-2.2.2.240807-py3-none-any.whl", hash = "sha256:893919ad82be4275f0d07bb47a95d08bae580d3fdea308a7acfcb3f02e76186e", size = 157069, upload-time = "2024-08-07T12:30:51.868Z" },
1178
+ ]
1179
+
1180
+ [[package]]
1181
+ name = "pandas-stubs"
1182
+ version = "2.3.3.251201"
1183
+ source = { registry = "https://pypi.org/simple" }
1184
+ resolution-markers = [
1185
+ "python_full_version >= '3.13'",
1186
+ "python_full_version == '3.12.*'",
1187
+ "python_full_version == '3.11.*'",
1188
+ "python_full_version == '3.10.*'",
1189
+ ]
1190
+ dependencies = [
1191
+ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" },
1192
+ { name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
1193
+ { name = "types-pytz", marker = "python_full_version >= '3.10'" },
1194
+ ]
1195
+ sdist = { url = "https://files.pythonhosted.org/packages/ee/a6/491b2af2cb3ee232765a73fb273a44cc1ac33b154f7745b2df2ee1dc4d01/pandas_stubs-2.3.3.251201.tar.gz", hash = "sha256:7a980f4f08cff2a6d7e4c6d6d26f4c5fcdb82a6f6531489b2f75c81567fe4536", size = 107787, upload-time = "2025-12-01T18:29:22.403Z" }
1196
+ wheels = [
1197
+ { url = "https://files.pythonhosted.org/packages/e2/68/78a3c253f146254b8e2c19f4a4768f272e12ef11001d9b45ec7b165db054/pandas_stubs-2.3.3.251201-py3-none-any.whl", hash = "sha256:eb5c9b6138bd8492fd74a47b09c9497341a278fcfbc8633ea4b35b230ebf4be5", size = 164638, upload-time = "2025-12-01T18:29:21.006Z" },
1198
+ ]
1199
+
1164
1200
  [[package]]
1165
1201
  name = "pathspec"
1166
1202
  version = "0.12.1"
@@ -1994,6 +2030,8 @@ dev = [
1994
2030
  { name = "autodoc-pydantic" },
1995
2031
  { name = "enum-tools", extra = ["sphinx"] },
1996
2032
  { name = "mypy" },
2033
+ { name = "pandas-stubs", version = "2.2.2.240807", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
2034
+ { name = "pandas-stubs", version = "2.3.3.251201", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
1997
2035
  { name = "proxy-py" },
1998
2036
  { name = "pyright" },
1999
2037
  { name = "pytest" },
@@ -2020,6 +2058,7 @@ dev = [
2020
2058
  { name = "autodoc-pydantic", specifier = ">=2.2.0" },
2021
2059
  { name = "enum-tools", extras = ["sphinx"], specifier = ">=0.12.0" },
2022
2060
  { name = "mypy", specifier = ">=1.18.2" },
2061
+ { name = "pandas-stubs", specifier = ">=2.2.2.240807" },
2023
2062
  { name = "proxy-py", specifier = ">=2.4.9" },
2024
2063
  { name = "pyright", specifier = ">=1.1.401" },
2025
2064
  { name = "pytest", specifier = ">=8.3.3" },
@@ -2078,6 +2117,15 @@ wheels = [
2078
2117
  { url = "https://files.pythonhosted.org/packages/03/98/eb27cc78ad3af8e302c9d8ff4977f5026676e130d28dd7578132a457170c/toolz-1.0.0-py3-none-any.whl", hash = "sha256:292c8f1c4e7516bf9086f8850935c799a874039c8bcf959d47b600e4c44a6236", size = 56383, upload-time = "2024-10-04T16:17:01.533Z" },
2079
2118
  ]
2080
2119
 
2120
+ [[package]]
2121
+ name = "types-pytz"
2122
+ version = "2025.2.0.20251108"
2123
+ source = { registry = "https://pypi.org/simple" }
2124
+ sdist = { url = "https://files.pythonhosted.org/packages/40/ff/c047ddc68c803b46470a357454ef76f4acd8c1088f5cc4891cdd909bfcf6/types_pytz-2025.2.0.20251108.tar.gz", hash = "sha256:fca87917836ae843f07129567b74c1929f1870610681b4c92cb86a3df5817bdb", size = 10961, upload-time = "2025-11-08T02:55:57.001Z" }
2125
+ wheels = [
2126
+ { url = "https://files.pythonhosted.org/packages/e7/c1/56ef16bf5dcd255155cc736d276efa6ae0a5c26fd685e28f0412a4013c01/types_pytz-2025.2.0.20251108-py3-none-any.whl", hash = "sha256:0f1c9792cab4eb0e46c52f8845c8f77cf1e313cb3d68bf826aa867fe4717d91c", size = 10116, upload-time = "2025-11-08T02:55:56.194Z" },
2127
+ ]
2128
+
2081
2129
  [[package]]
2082
2130
  name = "typing-extensions"
2083
2131
  version = "4.15.0"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes