tastytrade 11.0.4__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.4 → tastytrade-11.1.0}/PKG-INFO +1 -1
  2. {tastytrade-11.0.4 → tastytrade-11.1.0}/pyproject.toml +1 -0
  3. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/__init__.py +1 -2
  4. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/account.py +0 -173
  5. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/dxfeed/event.py +0 -2
  6. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/instruments.py +1 -1
  7. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/market_sessions.py +19 -0
  8. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/order.py +5 -124
  9. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/session.py +8 -28
  10. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/streamer.py +2 -9
  11. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/utils.py +17 -24
  12. {tastytrade-11.0.4 → tastytrade-11.1.0}/tests/test_account.py +111 -54
  13. {tastytrade-11.0.4 → tastytrade-11.1.0}/tests/test_dxfeed.py +10 -17
  14. {tastytrade-11.0.4 → tastytrade-11.1.0}/tests/test_market_sessions.py +10 -0
  15. {tastytrade-11.0.4 → tastytrade-11.1.0}/tests/test_utils.py +69 -1
  16. {tastytrade-11.0.4 → tastytrade-11.1.0}/uv.lock +48 -0
  17. {tastytrade-11.0.4 → tastytrade-11.1.0}/.github/CONTRIBUTING.md +0 -0
  18. {tastytrade-11.0.4 → tastytrade-11.1.0}/.github/FUNDING.yml +0 -0
  19. {tastytrade-11.0.4 → tastytrade-11.1.0}/.github/pull_request_template.md +0 -0
  20. {tastytrade-11.0.4 → tastytrade-11.1.0}/.github/workflows/python-app.yml +0 -0
  21. {tastytrade-11.0.4 → tastytrade-11.1.0}/.github/workflows/python-publish-test.yml +0 -0
  22. {tastytrade-11.0.4 → tastytrade-11.1.0}/.github/workflows/python-publish.yml +0 -0
  23. {tastytrade-11.0.4 → tastytrade-11.1.0}/.gitignore +0 -0
  24. {tastytrade-11.0.4 → tastytrade-11.1.0}/.python-version +0 -0
  25. {tastytrade-11.0.4 → tastytrade-11.1.0}/.readthedocs.yaml +0 -0
  26. {tastytrade-11.0.4 → tastytrade-11.1.0}/LICENSE +0 -0
  27. {tastytrade-11.0.4 → tastytrade-11.1.0}/Makefile +0 -0
  28. {tastytrade-11.0.4 → tastytrade-11.1.0}/README.md +0 -0
  29. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/Makefile +0 -0
  30. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/account-streamer.rst +0 -0
  31. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/accounts.rst +0 -0
  32. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/api/account.rst +0 -0
  33. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/api/dxfeed.rst +0 -0
  34. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/api/instruments.rst +0 -0
  35. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/api/market-data.rst +0 -0
  36. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/api/market-sessions.rst +0 -0
  37. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/api/metrics.rst +0 -0
  38. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/api/order.rst +0 -0
  39. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/api/search.rst +0 -0
  40. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/api/session.rst +0 -0
  41. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/api/streamer.rst +0 -0
  42. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/api/utils.rst +0 -0
  43. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/api/watchlists.rst +0 -0
  44. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/conf.py +0 -0
  45. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/data-streamer.rst +0 -0
  46. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/img/netliq.png +0 -0
  47. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/index.rst +0 -0
  48. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/installation.rst +0 -0
  49. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/instruments.rst +0 -0
  50. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/make.bat +0 -0
  51. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/market-data.rst +0 -0
  52. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/market-sessions.rst +0 -0
  53. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/orders.rst +0 -0
  54. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/sessions.rst +0 -0
  55. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/sync-async.rst +0 -0
  56. {tastytrade-11.0.4 → tastytrade-11.1.0}/docs/watchlists.rst +0 -0
  57. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/dxfeed/__init__.py +0 -0
  58. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/dxfeed/candle.py +0 -0
  59. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/dxfeed/greeks.py +0 -0
  60. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/dxfeed/profile.py +0 -0
  61. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/dxfeed/quote.py +0 -0
  62. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/dxfeed/summary.py +0 -0
  63. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/dxfeed/theoprice.py +0 -0
  64. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/dxfeed/timeandsale.py +0 -0
  65. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/dxfeed/trade.py +0 -0
  66. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/dxfeed/underlying.py +0 -0
  67. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/market_data.py +0 -0
  68. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/metrics.py +0 -0
  69. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/py.typed +0 -0
  70. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/search.py +0 -0
  71. {tastytrade-11.0.4 → tastytrade-11.1.0}/tastytrade/watchlists.py +0 -0
  72. {tastytrade-11.0.4 → tastytrade-11.1.0}/tests/__init__.py +0 -0
  73. {tastytrade-11.0.4 → tastytrade-11.1.0}/tests/conftest.py +0 -0
  74. {tastytrade-11.0.4 → tastytrade-11.1.0}/tests/test_instruments.py +0 -0
  75. {tastytrade-11.0.4 → tastytrade-11.1.0}/tests/test_market_data.py +0 -0
  76. {tastytrade-11.0.4 → tastytrade-11.1.0}/tests/test_metrics.py +0 -0
  77. {tastytrade-11.0.4 → tastytrade-11.1.0}/tests/test_search.py +0 -0
  78. {tastytrade-11.0.4 → tastytrade-11.1.0}/tests/test_session.py +0 -0
  79. {tastytrade-11.0.4 → tastytrade-11.1.0}/tests/test_streamer.py +0 -0
  80. {tastytrade-11.0.4 → 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.4
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.4"
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
@@ -546,8 +508,6 @@ class Account(TastytradeData):
546
508
  :param session: the session to use for the request.
547
509
  :param account_number: the account ID to get.
548
510
  :param include_closed: whether to include closed accounts in the results
549
-
550
- :return: an account if an ID was provided; otherwise, a single account.
551
511
  """
552
512
  if account_number:
553
513
  data = session._get(f"/customers/me/accounts/{account_number}")
@@ -1045,58 +1005,6 @@ class Account(TastytradeData):
1045
1005
  )
1046
1006
  return [NetLiqOhlc(**i) for i in data["items"]]
1047
1007
 
1048
- async def a_get_position_limit(self, session: Session) -> PositionLimit:
1049
- """
1050
- Get the maximum order size information for the account.
1051
-
1052
- :param session: the session to use for the request.
1053
- """
1054
- data = await session._a_get(f"/accounts/{self.account_number}/position-limit")
1055
- return PositionLimit(**data)
1056
-
1057
- def get_position_limit(self, session: Session) -> PositionLimit:
1058
- """
1059
- Get the maximum order size information for the account.
1060
-
1061
- :param session: the session to use for the request.
1062
- """
1063
- data = session._get(f"/accounts/{self.account_number}/position-limit")
1064
- return PositionLimit(**data)
1065
-
1066
- async def a_get_effective_margin_requirements(
1067
- self, session: Session, symbol: str
1068
- ) -> MarginRequirement:
1069
- """
1070
- Get the effective margin requirements for a given symbol.
1071
-
1072
- :param session:
1073
- the session to use for the request, can't be certification
1074
- :param symbol: the symbol to get margin requirements for.
1075
- """
1076
- if symbol:
1077
- symbol = symbol.replace("/", "%2F")
1078
- data = await session._a_get(
1079
- f"/accounts/{self.account_number}/margin-requirements/{symbol}/effective"
1080
- )
1081
- return MarginRequirement(**data)
1082
-
1083
- def get_effective_margin_requirements(
1084
- self, session: Session, symbol: str
1085
- ) -> MarginRequirement:
1086
- """
1087
- Get the effective margin requirements for a given symbol.
1088
-
1089
- :param session:
1090
- the session to use for the request, can't be certification
1091
- :param symbol: the symbol to get margin requirements for.
1092
- """
1093
- if symbol:
1094
- symbol = symbol.replace("/", "%2F")
1095
- data = session._get(
1096
- f"/accounts/{self.account_number}/margin-requirements/{symbol}/effective"
1097
- )
1098
- return MarginRequirement(**data)
1099
-
1100
1008
  async def a_get_margin_requirements(self, session: Session) -> MarginReport:
1101
1009
  """
1102
1010
  Get the margin report for the account, with total margin requirements
@@ -1502,84 +1410,3 @@ class Account(TastytradeData):
1502
1410
  ),
1503
1411
  )
1504
1412
  return PlacedOrder(**data)
1505
-
1506
- async def a_get_order_chains(
1507
- self,
1508
- session: Session,
1509
- symbol: str,
1510
- start_time: datetime,
1511
- end_time: datetime,
1512
- ) -> list[OrderChain]:
1513
- """
1514
- Get a list of order chains (open + rolls + close) for given symbol
1515
- over the given time frame, with total P/L, commissions, etc.
1516
-
1517
- Not supported for OAuth sessions--write Tasty to get this added!
1518
-
1519
- :param session: the session to use for the request.
1520
- :param symbol: the underlying symbol for the chains.
1521
- :param start_time: the beginning time of the query.
1522
- :param end_time: the ending time of the query.
1523
- """
1524
- params = {
1525
- "account-numbers[]": self.account_number,
1526
- "underlying-symbols[]": symbol,
1527
- "start-at": start_time.strftime(TT_DATE_FMT),
1528
- "end-at": end_time.strftime(TT_DATE_FMT),
1529
- "defer-open-winner-loser-filtering-to-frontend": False,
1530
- "per-page": 250,
1531
- }
1532
- headers = {
1533
- "Authorization": session.session_token,
1534
- "Accept": "application/json",
1535
- "Content-Type": "application/json",
1536
- }
1537
- async with httpx.AsyncClient() as client:
1538
- response = await client.get(
1539
- f"{VAST_URL}/order-chains",
1540
- headers=headers,
1541
- params=params, # type: ignore[arg-type]
1542
- )
1543
- validate_response(response)
1544
- chains = response.json()["data"]["items"]
1545
- return [OrderChain(**i) for i in chains]
1546
-
1547
- def get_order_chains(
1548
- self,
1549
- session: Session,
1550
- symbol: str,
1551
- start_time: datetime,
1552
- end_time: datetime,
1553
- ) -> list[OrderChain]:
1554
- """
1555
- Get a list of order chains (open + rolls + close) for given symbol
1556
- over the given time frame, with total P/L, commissions, etc.
1557
-
1558
- Not supported for OAuth sessions--write Tasty to get this added!
1559
-
1560
- :param session: the session to use for the request.
1561
- :param symbol: the underlying symbol for the chains.
1562
- :param start_time: the beginning time of the query.
1563
- :param end_time: the ending time of the query.
1564
- """
1565
- params = {
1566
- "account-numbers[]": self.account_number,
1567
- "underlying-symbols[]": symbol,
1568
- "start-at": start_time.strftime(TT_DATE_FMT),
1569
- "end-at": end_time.strftime(TT_DATE_FMT),
1570
- "defer-open-winner-loser-filtering-to-frontend": False,
1571
- "per-page": 250,
1572
- }
1573
- headers = {
1574
- "Authorization": session.session_token,
1575
- "Accept": "application/json",
1576
- "Content-Type": "application/json",
1577
- }
1578
- response = httpx.get(
1579
- f"{VAST_URL}/order-chains",
1580
- headers=headers,
1581
- params=params, # type: ignore[arg-type]
1582
- )
1583
- validate_response(response)
1584
- chains = response.json()["data"]["items"]
1585
- return [OrderChain(**i) for i in chains]
@@ -41,8 +41,6 @@ class Event(BaseModel):
41
41
  a :class:`~tastyworks.streamer.DXFeedStreamer`.
42
42
 
43
43
  :param data: list of raw quote data from streamer
44
-
45
- :return: list of event objects from data
46
44
  """
47
45
  objs: list[Event] = []
48
46
  size = len(cls.model_fields)
@@ -490,8 +490,8 @@ class Option(TradeableTastytradeData):
490
490
  stops_trading_at: datetime
491
491
  market_time_instrument_collection: str
492
492
  days_to_expiration: int
493
- expires_at: datetime
494
493
  is_closing_only: bool
494
+ expires_at: datetime | None = None
495
495
  streamer_symbol: str = ""
496
496
  listed_market: str | None = None
497
497
  halted_at: datetime | None = None
@@ -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 (
@@ -133,7 +133,7 @@ class Leg(TastytradeData):
133
133
  instrument_type: InstrumentType
134
134
  symbol: str
135
135
  action: OrderAction
136
- quantity: Decimal | None = None
136
+ quantity: Decimal | int | None = None
137
137
  remaining_quantity: Decimal | None = None
138
138
  fills: list[FillInfo] | None = None
139
139
 
@@ -149,15 +149,13 @@ class TradeableTastytradeData(TastytradeData):
149
149
  instrument_type: InstrumentType
150
150
  symbol: str
151
151
 
152
- def build_leg(self, quantity: Decimal | None, action: OrderAction) -> Leg:
152
+ def build_leg(self, quantity: Decimal | int | None, action: OrderAction) -> Leg:
153
153
  """
154
154
  Builds an order :class:`Leg` from the dataclass.
155
155
 
156
156
  :param quantity:
157
157
  the quantity of the symbol to trade, set this as `None` for notional orders
158
158
  :param action: :class:`OrderAction` to perform, e.g. BUY_TO_OPEN
159
-
160
- :return: a :class:`Leg` object
161
159
  """
162
160
  return Leg(
163
161
  instrument_type=self.instrument_type,
@@ -239,6 +237,8 @@ class NewOrder(TastytradeData):
239
237
  modifying existing orders.
240
238
  """
241
239
 
240
+ model_config = ConfigDict(extra="allow")
241
+
242
242
  time_in_force: OrderTimeInForce
243
243
  order_type: OrderType
244
244
  source: str = version_str
@@ -437,122 +437,3 @@ class PlacedOrderResponse(TastytradeData):
437
437
  fee_calculation: FeeCalculation | None = None
438
438
  warnings: list[Message] | None = None
439
439
  errors: list[Message] | None = None
440
-
441
-
442
- class OrderChainEntry(TastytradeData):
443
- """
444
- Dataclass containing information about a single order in an order chain.
445
- """
446
-
447
- symbol: str
448
- instrument_type: InstrumentType
449
- quantity: str
450
- quantity_type: str
451
- quantity_numeric: Decimal
452
-
453
-
454
- class OrderChainLeg(TastytradeData):
455
- """
456
- Dataclass containing information about a single leg in an order
457
- from an order chain.
458
- """
459
-
460
- symbol: str
461
- instrument_type: InstrumentType
462
- action: OrderAction
463
- fill_quantity: Decimal
464
- order_quantity: Decimal
465
-
466
-
467
- class OrderChainNode(TastytradeData):
468
- """
469
- Dataclass containing information about a single node in an order chain.
470
- """
471
-
472
- node_type: str
473
- id: str
474
- description: str
475
- occurred_at: datetime | None = None
476
- total_fees: Decimal | None = None
477
- total_fill_cost: Decimal | None = None
478
- gcd_quantity: Decimal | None = None
479
- fill_cost_per_quantity: Decimal | None = None
480
- order_fill_count: int | None = None
481
- roll: bool | None = None
482
- legs: list[OrderChainLeg] | None = None
483
- entries: list[OrderChainEntry] | None = None
484
-
485
- @model_validator(mode="before")
486
- @classmethod
487
- def validate_price_effects(cls, data: Any) -> Any:
488
- return set_sign_for(
489
- data,
490
- [
491
- "total_fees",
492
- "total_fill_cost",
493
- "fill_cost_per_quantity",
494
- ],
495
- )
496
-
497
-
498
- class ComputedData(TastytradeData):
499
- """
500
- Dataclass containing computed data about an order chain.
501
- """
502
-
503
- open: bool
504
- updated_at: datetime
505
- total_fees: Decimal
506
- total_commissions: Decimal
507
- realized_gain: Decimal
508
- realized_gain_with_fees: Decimal
509
- winner_realized_and_closed: bool
510
- winner_realized: bool
511
- winner_realized_with_fees: bool
512
- roll_count: int
513
- opened_at: datetime
514
- last_occurred_at: datetime
515
- started_at_days_to_expiration: int
516
- duration: int
517
- total_opening_cost: Decimal
518
- total_closing_cost: Decimal
519
- total_cost: Decimal
520
- gcd_open_quantity: Decimal
521
- fees_missing: bool
522
- open_entries: list[OrderChainEntry]
523
- total_cost_per_unit: Decimal | None = None
524
-
525
- @model_validator(mode="before")
526
- @classmethod
527
- def validate_price_effects(cls, data: Any) -> Any:
528
- return set_sign_for(
529
- data,
530
- [
531
- "total_fees",
532
- "total_commissions",
533
- "realized_gain",
534
- "realized_gain_with_fees",
535
- "total_opening_cost",
536
- "total_closing_cost",
537
- "total_cost",
538
- "total_cost_per_unit",
539
- ],
540
- )
541
-
542
-
543
- class OrderChain(TastytradeData):
544
- """
545
- Dataclass containing information about an order chain: a group of orders
546
- for a specific underlying, such as total P/L, rolls, current P/L in a
547
- symbol, etc.
548
- """
549
-
550
- id: int
551
- account_number: str
552
- description: str
553
- underlying_symbol: str
554
- computed_data: ComputedData
555
- lite_nodes: list[OrderChainNode]
556
- lite_nodes_sizes: int | None = None
557
- updated_at: datetime | None = None
558
- 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,19 +364,11 @@ 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
  """
378
371
  Gets the customer dict from the API.
379
-
380
- :return: a Tastytrade 'Customer' object in JSON format.
381
372
  """
382
373
  data = await self._a_get("/customers/me")
383
374
  return Customer(**data)
@@ -385,8 +376,6 @@ class Session:
385
376
  def get_customer(self) -> Customer:
386
377
  """
387
378
  Gets the customer dict from the API.
388
-
389
- :return: a Tastytrade 'Customer' object in JSON format.
390
379
  """
391
380
  data = self._get("/customers/me")
392
381
  return Customer(**data)
@@ -394,8 +383,6 @@ class Session:
394
383
  async def a_validate(self) -> bool:
395
384
  """
396
385
  Validates the current session by sending a request to the API.
397
-
398
- :return: True if the session is valid and False otherwise.
399
386
  """
400
387
  response = await self.async_client.post("/sessions/validate")
401
388
  return response.status_code // 100 == 2
@@ -403,8 +390,6 @@ class Session:
403
390
  def validate(self) -> bool:
404
391
  """
405
392
  Validates the current session by sending a request to the API.
406
-
407
- :return: True if the session is valid and False otherwise.
408
393
  """
409
394
  response = self.sync_client.post("/sessions/validate")
410
395
  return response.status_code // 100 == 2
@@ -420,6 +405,7 @@ class Session:
420
405
  del attrs["sync_client"]
421
406
  attrs["session_expiration"] = self.session_expiration.strftime(_fmt)
422
407
  attrs["streamer_expiration"] = self.streamer_expiration.strftime(_fmt)
408
+ attrs["headers"] = dict(self.sync_client.headers.copy())
423
409
  return json.dumps(attrs)
424
410
 
425
411
  @classmethod
@@ -427,17 +413,11 @@ class Session:
427
413
  """
428
414
  Create a new Session object from a serialized string.
429
415
  """
430
- deserialized = json.loads(serialized)
416
+ deserialized: dict[str, Any] = json.loads(serialized)
417
+ headers = deserialized.pop("headers")
431
418
  self = cls.__new__(cls)
432
419
  self.__dict__ = deserialized
433
420
  base_url = CERT_URL if self.is_test else API_URL
434
- headers = {
435
- "Accept": "application/json",
436
- "Content-Type": "application/json",
437
- "Authorization": self.session_token
438
- if "user" in deserialized
439
- else f"Bearer {self.session_token}",
440
- }
441
421
  self.session_expiration = datetime.strptime(
442
422
  deserialized["session_expiration"], _fmt
443
423
  )
@@ -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]