diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 7bc850c..a52dc0d 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -11,10 +11,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up Python 3.8 + - name: Set up Python 3.9 uses: actions/setup-python@v4 with: - python-version: '3.8' + python-version: '3.9' - uses: yezz123/setup-uv@v4 - name: Setup uv venv run: | diff --git a/.python-version b/.python-version index cc1923a..bd28b9c 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.8 +3.9 diff --git a/Makefile b/Makefile index 650b945..0feea42 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ install: uv sync + uv pip install -e . lint: uv run ruff format tastytrade/ tests/ diff --git a/docs/conf.py b/docs/conf.py index 6721a6e..27ea038 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -13,7 +13,7 @@ project = "tastytrade" copyright = "2024, Graeme Holliday" author = "Graeme Holliday" -release = "9.1" +release = "9.2" # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration @@ -23,7 +23,7 @@ "sphinx.ext.doctest", "sphinx.ext.autodoc", "sphinx.ext.autosummary", - "sphinx.ext.intersphinx", + # "sphinx.ext.intersphinx", "sphinx_toolbox.more_autodoc.autotypeddict", "enum_tools.autoenum", "sphinxcontrib.autodoc_pydantic", diff --git a/pyproject.toml b/pyproject.toml index 22bd4ae..b1a68c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "tastytrade" -version = "9.1" +version = "9.2" description = "An unofficial, sync/async SDK for Tastytrade!" readme = "README.md" requires-python = ">=3.8" @@ -29,7 +29,6 @@ dev-dependencies = [ "pytest-aio>=1.5.0", "pytest-cov>=5.0.0", "ruff>=0.6.9", - "types-pytz>=2024.2.0.20241003", "pyright>=1.1.384", ] diff --git a/tastytrade/__init__.py b/tastytrade/__init__.py index b7abb19..61045d0 100644 --- a/tastytrade/__init__.py +++ b/tastytrade/__init__.py @@ -4,7 +4,7 @@ BACKTEST_URL = "https://backtester.vast.tastyworks.com" CERT_URL = "https://api.cert.tastyworks.com" VAST_URL = "https://vast.tastyworks.com" -VERSION = "9.1" +VERSION = "9.2" logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) diff --git a/tastytrade/account.py b/tastytrade/account.py index b665948..34a8d3b 100644 --- a/tastytrade/account.py +++ b/tastytrade/account.py @@ -1,6 +1,6 @@ from datetime import date, datetime from decimal import Decimal -from typing import Any, Dict, List, Literal, Optional, Union +from typing import Any, Literal, Optional, Union import httpx from pydantic import BaseModel, model_validator @@ -229,7 +229,7 @@ class MarginReportEntry(TastytradeJsonDataclass): margin_requirement: Decimal expected_price_range_up_percent: Optional[Decimal] = None expected_price_range_down_percent: Optional[Decimal] = None - groups: Optional[List[Dict[str, Any]]] = None + groups: Optional[list[dict[str, Any]]] = None initial_requirement: Optional[Decimal] = None maintenance_requirement: Optional[Decimal] = None point_of_no_return_percent: Optional[Decimal] = None @@ -269,7 +269,7 @@ class MarginReport(TastytradeJsonDataclass): reg_t_option_buying_power: Decimal maintenance_excess: Decimal last_state_timestamp: int - groups: List[Union[MarginReportEntry, EmptyDict]] + groups: list[Union[MarginReportEntry, EmptyDict]] initial_requirement: Optional[Decimal] = None @model_validator(mode="before") @@ -430,7 +430,7 @@ class Transaction(TastytradeJsonDataclass): other_charge_description: Optional[str] = None reverses_id: Optional[int] = None cost_basis_reconciliation_date: Optional[date] = None - lots: Optional[List[Lot]] = None + lots: Optional[list[Lot]] = None agency_price: Optional[Decimal] = None principal_price: Optional[Decimal] = None @@ -484,7 +484,7 @@ class Account(TastytradeJsonDataclass): submitting_user_id: Optional[str] = None @classmethod - async def a_get_accounts(cls, session: Session, include_closed=False) -> List[Self]: + async def a_get_accounts(cls, session: Session, include_closed=False) -> list[Self]: """ Gets all trading accounts associated with the Tastytrade user. @@ -500,7 +500,7 @@ async def a_get_accounts(cls, session: Session, include_closed=False) -> List[Se ] @classmethod - def get_accounts(cls, session: Session, include_closed=False) -> List[Self]: + def get_accounts(cls, session: Session, include_closed=False) -> list[Self]: """ Gets all trading accounts associated with the Tastytrade user. @@ -583,7 +583,7 @@ async def a_get_balance_snapshots( start_date: Optional[date] = None, snapshot_date: Optional[date] = None, time_of_day: Literal["BOD", "EOD"] = "EOD", - ) -> List[AccountBalanceSnapshot]: + ) -> list[AccountBalanceSnapshot]: """ Returns a list of balance snapshots. This list will just have a few snapshots if you don't pass a start @@ -646,7 +646,7 @@ def get_balance_snapshots( start_date: Optional[date] = None, snapshot_date: Optional[date] = None, time_of_day: Literal["BOD", "EOD"] = "EOD", - ) -> List[AccountBalanceSnapshot]: + ) -> list[AccountBalanceSnapshot]: """ Returns a list of balance snapshots. This list will just have a few snapshots if you don't pass a start @@ -702,15 +702,15 @@ def get_balance_snapshots( async def a_get_positions( self, session: Session, - underlying_symbols: Optional[List[str]] = None, + underlying_symbols: Optional[list[str]] = None, symbol: Optional[str] = None, instrument_type: Optional[InstrumentType] = None, include_closed: Optional[bool] = None, underlying_product_code: Optional[str] = None, - partition_keys: Optional[List[str]] = None, + partition_keys: Optional[list[str]] = None, net_positions: Optional[bool] = None, include_marks: Optional[bool] = None, - ) -> List[CurrentPosition]: + ) -> list[CurrentPosition]: """ Get the current positions of the account. @@ -747,15 +747,15 @@ async def a_get_positions( def get_positions( self, session: Session, - underlying_symbols: Optional[List[str]] = None, + underlying_symbols: Optional[list[str]] = None, symbol: Optional[str] = None, instrument_type: Optional[InstrumentType] = None, include_closed: Optional[bool] = None, underlying_product_code: Optional[str] = None, - partition_keys: Optional[List[str]] = None, + partition_keys: Optional[list[str]] = None, net_positions: Optional[bool] = None, include_marks: Optional[bool] = None, - ) -> List[CurrentPosition]: + ) -> list[CurrentPosition]: """ Get the current positions of the account. @@ -796,8 +796,8 @@ async def a_get_history( page_offset: Optional[int] = None, sort: str = "Desc", type: Optional[str] = None, - types: Optional[List[str]] = None, - sub_types: Optional[List[str]] = None, + types: Optional[list[str]] = None, + sub_types: Optional[list[str]] = None, start_date: Optional[date] = None, end_date: Optional[date] = None, instrument_type: Optional[InstrumentType] = None, @@ -808,7 +808,7 @@ async def a_get_history( futures_symbol: Optional[str] = None, start_at: Optional[datetime] = None, end_at: Optional[datetime] = None, - ) -> List[Transaction]: + ) -> list[Transaction]: """ Get transaction history of the account. @@ -891,8 +891,8 @@ def get_history( page_offset: Optional[int] = None, sort: str = "Desc", type: Optional[str] = None, - types: Optional[List[str]] = None, - sub_types: Optional[List[str]] = None, + types: Optional[list[str]] = None, + sub_types: Optional[list[str]] = None, start_date: Optional[date] = None, end_date: Optional[date] = None, instrument_type: Optional[InstrumentType] = None, @@ -903,7 +903,7 @@ def get_history( futures_symbol: Optional[str] = None, start_at: Optional[datetime] = None, end_at: Optional[datetime] = None, - ) -> List[Transaction]: + ) -> list[Transaction]: """ Get transaction history of the account. @@ -1036,7 +1036,7 @@ async def a_get_net_liquidating_value_history( session: Session, time_back: Optional[str] = None, start_time: Optional[datetime] = None, - ) -> List[NetLiqOhlc]: + ) -> list[NetLiqOhlc]: """ Returns a list of account net liquidating value snapshots over the specified time period. @@ -1070,7 +1070,7 @@ def get_net_liquidating_value_history( session: Session, time_back: Optional[str] = None, start_time: Optional[datetime] = None, - ) -> List[NetLiqOhlc]: + ) -> list[NetLiqOhlc]: """ Returns a list of account net liquidating value snapshots over the specified time period. @@ -1175,7 +1175,7 @@ def get_margin_requirements(self, session: Session) -> MarginReport: data = session._get(f"/margin/accounts/{self.account_number}/requirements") return MarginReport(**data) - async def a_get_live_orders(self, session: Session) -> List[PlacedOrder]: + async def a_get_live_orders(self, session: Session) -> list[PlacedOrder]: """ Get orders placed today for the account. @@ -1184,7 +1184,7 @@ async def a_get_live_orders(self, session: Session) -> List[PlacedOrder]: data = await session._a_get(f"/accounts/{self.account_number}/orders/live") return [PlacedOrder(**i) for i in data["items"]] - def get_live_orders(self, session: Session) -> List[PlacedOrder]: + def get_live_orders(self, session: Session) -> list[PlacedOrder]: """ Get orders placed today for the account. @@ -1195,7 +1195,7 @@ def get_live_orders(self, session: Session) -> List[PlacedOrder]: async def a_get_live_complex_orders( self, session: Session - ) -> List[PlacedComplexOrder]: + ) -> list[PlacedComplexOrder]: """ Get complex orders placed today for the account. @@ -1206,7 +1206,7 @@ async def a_get_live_complex_orders( ) return [PlacedComplexOrder(**i) for i in data["items"]] - def get_live_complex_orders(self, session: Session) -> List[PlacedComplexOrder]: + def get_live_complex_orders(self, session: Session) -> list[PlacedComplexOrder]: """ Get complex orders placed today for the account. @@ -1309,13 +1309,13 @@ async def a_get_order_history( start_date: Optional[date] = None, end_date: Optional[date] = None, underlying_symbol: Optional[str] = None, - statuses: Optional[List[OrderStatus]] = None, + statuses: Optional[list[OrderStatus]] = None, futures_symbol: Optional[str] = None, underlying_instrument_type: Optional[InstrumentType] = None, sort: Optional[str] = None, start_at: Optional[datetime] = None, end_at: Optional[datetime] = None, - ) -> List[PlacedOrder]: + ) -> list[PlacedOrder]: """ Get order history of the account. @@ -1390,13 +1390,13 @@ def get_order_history( start_date: Optional[date] = None, end_date: Optional[date] = None, underlying_symbol: Optional[str] = None, - statuses: Optional[List[OrderStatus]] = None, + statuses: Optional[list[OrderStatus]] = None, futures_symbol: Optional[str] = None, underlying_instrument_type: Optional[InstrumentType] = None, sort: Optional[str] = None, start_at: Optional[datetime] = None, end_at: Optional[datetime] = None, - ) -> List[PlacedOrder]: + ) -> list[PlacedOrder]: """ Get order history of the account. @@ -1465,7 +1465,7 @@ def get_order_history( async def a_get_complex_order_history( self, session: Session, per_page: int = 50, page_offset: Optional[int] = None - ) -> List[PlacedComplexOrder]: + ) -> list[PlacedComplexOrder]: """ Get order history of the account. @@ -1504,7 +1504,7 @@ async def a_get_complex_order_history( def get_complex_order_history( self, session: Session, per_page: int = 50, page_offset: Optional[int] = None - ) -> List[PlacedComplexOrder]: + ) -> list[PlacedComplexOrder]: """ Get order history of the account. @@ -1653,7 +1653,7 @@ async def a_get_order_chains( symbol: str, start_time: datetime, end_time: datetime, - ) -> List[OrderChain]: + ) -> list[OrderChain]: """ Get a list of order chains (open + rolls + close) for given symbol over the given time frame, with total P/L, commissions, etc. @@ -1692,7 +1692,7 @@ def get_order_chains( symbol: str, start_time: datetime, end_time: datetime, - ) -> List[OrderChain]: + ) -> list[OrderChain]: """ Get a list of order chains (open + rolls + close) for given symbol over the given time frame, with total P/L, commissions, etc. diff --git a/tastytrade/backtest.py b/tastytrade/backtest.py index 028670b..db936e6 100644 --- a/tastytrade/backtest.py +++ b/tastytrade/backtest.py @@ -1,7 +1,7 @@ import asyncio from datetime import date, datetime from decimal import Decimal -from typing import AsyncGenerator, List, Literal, Optional +from typing import AsyncGenerator, Literal, Optional import httpx from pydantic import BaseModel, Field @@ -71,7 +71,7 @@ class Backtest(BacktestJsonDataclass): symbol: str entry_conditions: BacktestEntry exit_conditions: BacktestExit - legs: List[BacktestLeg] + legs: list[BacktestLeg] start_date: date end_date: date = date(2024, 7, 31) status: str = "pending" @@ -138,9 +138,9 @@ class BacktestResults(BacktestJsonDataclass): Dataclass containing partial or finished results of a backtest. """ - snapshots: Optional[List[BacktestSnapshot]] + snapshots: Optional[list[BacktestSnapshot]] statistics: Optional[BacktestStatistics] - trials: Optional[List[BacktestTrial]] + trials: Optional[list[BacktestTrial]] class BacktestResponse(Backtest): diff --git a/tastytrade/instruments.py b/tastytrade/instruments.py index 6fe8246..bc0a5d0 100644 --- a/tastytrade/instruments.py +++ b/tastytrade/instruments.py @@ -3,7 +3,7 @@ from datetime import date, datetime from decimal import Decimal from enum import Enum -from typing import Dict, List, Optional +from typing import Optional from pydantic import model_validator from typing_extensions import Self @@ -118,7 +118,7 @@ class NestedOptionChainExpiration(TastytradeJsonDataclass): expiration_date: date days_to_expiration: int settlement_type: str - strikes: List[Strike] + strikes: list[Strike] class NestedFutureOptionChainExpiration(TastytradeJsonDataclass): @@ -140,8 +140,8 @@ class NestedFutureOptionChainExpiration(TastytradeJsonDataclass): option_contract_symbol: str stops_trading_at: datetime settlement_type: str - strikes: List[Strike] - tick_sizes: List[TickSize] + strikes: list[Strike] + tick_sizes: list[TickSize] class NestedFutureOptionFuture(TastytradeJsonDataclass): @@ -196,13 +196,13 @@ class Cryptocurrency(TradeableTastytradeJsonDataclass): is_closing_only: bool active: bool tick_size: Decimal - destination_venue_symbols: List[DestinationVenueSymbol] + destination_venue_symbols: list[DestinationVenueSymbol] streamer_symbol: Optional[str] = None @classmethod async def a_get_cryptocurrencies( - cls, session: Session, symbols: List[str] = [] - ) -> List[Self]: + cls, session: Session, symbols: list[str] = [] + ) -> list[Self]: """ Returns a list of cryptocurrency objects from the given symbols. @@ -215,8 +215,8 @@ async def a_get_cryptocurrencies( @classmethod def get_cryptocurrencies( - cls, session: Session, symbols: List[str] = [] - ) -> List[Self]: + cls, session: Session, symbols: list[str] = [] + ) -> list[Self]: """ Returns a list of cryptocurrency objects from the given symbols. @@ -260,7 +260,6 @@ class Equity(TradeableTastytradeJsonDataclass): id: int is_index: bool - listed_market: str description: str lendability: str market_time_instrument_collection: str @@ -276,8 +275,9 @@ class Equity(TradeableTastytradeJsonDataclass): halted_at: Optional[datetime] = None stops_trading_at: Optional[datetime] = None is_fractional_quantity_eligible: Optional[bool] = None - tick_sizes: Optional[List[TickSize]] = None - option_tick_sizes: Optional[List[TickSize]] = None + tick_sizes: Optional[list[TickSize]] = None + listed_market: Optional[str] = None + option_tick_sizes: Optional[list[TickSize]] = None @classmethod async def a_get_active_equities( @@ -286,7 +286,7 @@ async def a_get_active_equities( per_page: int = 1000, page_offset: Optional[int] = None, lendability: Optional[str] = None, - ) -> List[Self]: + ) -> list[Self]: """ Returns a list of actively traded Equity objects. @@ -337,7 +337,7 @@ def get_active_equities( per_page: int = 1000, page_offset: Optional[int] = None, lendability: Optional[str] = None, - ) -> List[Self]: + ) -> list[Self]: """ Returns a list of actively traded Equity objects. @@ -385,11 +385,11 @@ def get_active_equities( async def a_get_equities( cls, session: Session, - symbols: Optional[List[str]] = None, + symbols: Optional[list[str]] = None, lendability: Optional[str] = None, is_index: Optional[bool] = None, is_etf: Optional[bool] = None, - ) -> List[Self]: + ) -> list[Self]: """ Returns a list of Equity objects from the given symbols. @@ -417,11 +417,11 @@ async def a_get_equities( def get_equities( cls, session: Session, - symbols: Optional[List[str]] = None, + symbols: Optional[list[str]] = None, lendability: Optional[str] = None, is_index: Optional[bool] = None, is_etf: Optional[bool] = None, - ) -> List[Self]: + ) -> list[Self]: """ Returns a list of Equity objects from the given symbols. @@ -507,10 +507,10 @@ def set_streamer_symbol(self) -> Self: async def a_get_options( cls, session: Session, - symbols: Optional[List[str]] = None, + symbols: Optional[list[str]] = None, active: Optional[bool] = None, with_expired: Optional[bool] = None, - ) -> List[Self]: + ) -> list[Self]: """ Returns a list of Option objects from the given symbols. @@ -530,10 +530,10 @@ async def a_get_options( def get_options( cls, session: Session, - symbols: Optional[List[str]] = None, + symbols: Optional[list[str]] = None, active: Optional[bool] = None, with_expired: Optional[bool] = None, - ) -> List[Self]: + ) -> list[Self]: """ Returns a list of Option objects from the given symbols. @@ -655,9 +655,9 @@ class NestedOptionChain(TastytradeJsonDataclass): root_symbol: str option_chain_type: str shares_per_contract: int - tick_sizes: List[TickSize] - deliverables: List[Deliverable] - expirations: List[NestedOptionChainExpiration] + tick_sizes: list[TickSize] + deliverables: list[Deliverable] + expirations: list[NestedOptionChainExpiration] @classmethod async def a_get_chain(cls, session: Session, symbol: str) -> Self: @@ -699,8 +699,8 @@ class FutureProduct(TastytradeJsonDataclass): description: str exchange: str product_type: str - listed_months: List[FutureMonthCode] - active_months: List[FutureMonthCode] + listed_months: list[FutureMonthCode] + active_months: list[FutureMonthCode] notional_multiplier: Decimal tick_size: Decimal display_factor: Decimal @@ -722,10 +722,10 @@ class FutureProduct(TastytradeJsonDataclass): clearport_code: Optional[str] = None legacy_code: Optional[str] = None legacy_exchange_code: Optional[str] = None - option_products: Optional[List["FutureOptionProduct"]] = None + option_products: Optional[list["FutureOptionProduct"]] = None @classmethod - async def a_get_future_products(cls, session: Session) -> List[Self]: + async def a_get_future_products(cls, session: Session) -> list[Self]: """ Returns a list of FutureProduct objects available. @@ -735,7 +735,7 @@ async def a_get_future_products(cls, session: Session) -> List[Self]: return [cls(**i) for i in data["items"]] @classmethod - def get_future_products(cls, session: Session) -> List[Self]: + def get_future_products(cls, session: Session) -> list[Self]: """ Returns a list of FutureProduct objects available. @@ -811,17 +811,17 @@ class Future(TradeableTastytradeJsonDataclass): roll_target_symbol: Optional[str] = None true_underlying_symbol: Optional[str] = None future_etf_equivalent: Optional[FutureEtfEquivalent] = None - tick_sizes: Optional[List[TickSize]] = None - option_tick_sizes: Optional[List[TickSize]] = None - spread_tick_sizes: Optional[List[TickSize]] = None + tick_sizes: Optional[list[TickSize]] = None + option_tick_sizes: Optional[list[TickSize]] = None + spread_tick_sizes: Optional[list[TickSize]] = None @classmethod async def a_get_futures( cls, session: Session, - symbols: Optional[List[str]] = None, - product_codes: Optional[List[str]] = None, - ) -> List[Self]: + symbols: Optional[list[str]] = None, + product_codes: Optional[list[str]] = None, + ) -> list[Self]: """ Returns a list of Future objects from the given symbols or product codes. @@ -844,9 +844,9 @@ async def a_get_futures( def get_futures( cls, session: Session, - symbols: Optional[List[str]] = None, - product_codes: Optional[List[str]] = None, - ) -> List[Self]: + symbols: Optional[list[str]] = None, + product_codes: Optional[list[str]] = None, + ) -> list[Self]: """ Returns a list of Future objects from the given symbols or product codes. @@ -916,7 +916,7 @@ class FutureOptionProduct(TastytradeJsonDataclass): clearport_code: Optional[str] = None @classmethod - async def a_get_future_option_products(cls, session: Session) -> List[Self]: + async def a_get_future_option_products(cls, session: Session) -> list[Self]: """ Returns a list of FutureOptionProduct objects available. @@ -926,7 +926,7 @@ async def a_get_future_option_products(cls, session: Session) -> List[Self]: return [cls(**i) for i in data["items"]] @classmethod - def get_future_option_products(cls, session: Session) -> List[Self]: + def get_future_option_products(cls, session: Session) -> list[Self]: """ Returns a list of FutureOptionProduct objects available. @@ -1014,12 +1014,12 @@ class FutureOption(TradeableTastytradeJsonDataclass): async def a_get_future_options( cls, session: Session, - symbols: Optional[List[str]] = None, + symbols: Optional[list[str]] = None, root_symbol: Optional[str] = None, expiration_date: Optional[date] = None, option_type: Optional[OptionType] = None, strike_price: Optional[Decimal] = None, - ) -> List[Self]: + ) -> list[Self]: """ Returns a list of FutureOption objects from the given symbols. @@ -1051,12 +1051,12 @@ async def a_get_future_options( def get_future_options( cls, session: Session, - symbols: Optional[List[str]] = None, + symbols: Optional[list[str]] = None, root_symbol: Optional[str] = None, expiration_date: Optional[date] = None, option_type: Optional[OptionType] = None, strike_price: Optional[Decimal] = None, - ) -> List[Self]: + ) -> list[Self]: """ Returns a list of FutureOption objects from the given symbols. @@ -1118,7 +1118,7 @@ class NestedFutureOptionSubchain(TastytradeJsonDataclass): underlying_symbol: str root_symbol: str exercise_style: str - expirations: List[NestedFutureOptionChainExpiration] + expirations: list[NestedFutureOptionChainExpiration] class NestedFutureOptionChain(TastytradeJsonDataclass): @@ -1131,8 +1131,8 @@ class NestedFutureOptionChain(TastytradeJsonDataclass): extra API request or two. """ - futures: List[NestedFutureOptionFuture] - option_chains: List[NestedFutureOptionSubchain] + futures: list[NestedFutureOptionFuture] + option_chains: list[NestedFutureOptionSubchain] @classmethod async def a_get_chain(cls, session: Session, symbol: str) -> Self: @@ -1175,8 +1175,8 @@ class Warrant(TastytradeJsonDataclass): @classmethod async def a_get_warrants( - cls, session: Session, symbols: Optional[List[str]] = None - ) -> List[Self]: + cls, session: Session, symbols: Optional[list[str]] = None + ) -> list[Self]: """ Returns a list of Warrant objects from the given symbols. @@ -1189,8 +1189,8 @@ async def a_get_warrants( @classmethod def get_warrants( - cls, session: Session, symbols: Optional[List[str]] = None - ) -> List[Self]: + cls, session: Session, symbols: Optional[list[str]] = None + ) -> list[Self]: """ Returns a list of Warrant objects from the given symbols. @@ -1230,7 +1230,7 @@ def get_warrant(cls, session: Session, symbol: str) -> Self: async def a_get_quantity_decimal_precisions( session: Session, -) -> List[QuantityDecimalPrecision]: +) -> list[QuantityDecimalPrecision]: """ Returns a list of QuantityDecimalPrecision objects for different types of instruments. @@ -1241,7 +1241,7 @@ async def a_get_quantity_decimal_precisions( return [QuantityDecimalPrecision(**i) for i in data["items"]] -def get_quantity_decimal_precisions(session: Session) -> List[QuantityDecimalPrecision]: +def get_quantity_decimal_precisions(session: Session) -> list[QuantityDecimalPrecision]: """ Returns a list of QuantityDecimalPrecision objects for different types of instruments. @@ -1252,7 +1252,7 @@ def get_quantity_decimal_precisions(session: Session) -> List[QuantityDecimalPre return [QuantityDecimalPrecision(**i) for i in data["items"]] -async def a_get_option_chain(session: Session, symbol: str) -> Dict[date, List[Option]]: +async def a_get_option_chain(session: Session, symbol: str) -> dict[date, list[Option]]: """ Returns a mapping of expiration date to a list of option objects representing the options chain for the given symbol. @@ -1275,7 +1275,7 @@ async def a_get_option_chain(session: Session, symbol: str) -> Dict[date, List[O return chain -def get_option_chain(session: Session, symbol: str) -> Dict[date, List[Option]]: +def get_option_chain(session: Session, symbol: str) -> dict[date, list[Option]]: """ Returns a mapping of expiration date to a list of option objects representing the options chain for the given symbol. @@ -1300,7 +1300,7 @@ def get_option_chain(session: Session, symbol: str) -> Dict[date, List[Option]]: async def a_get_future_option_chain( session: Session, symbol: str -) -> Dict[date, List[FutureOption]]: +) -> dict[date, list[FutureOption]]: """ Returns a mapping of expiration date to a list of futures options objects representing the options chain for the given symbol. @@ -1325,7 +1325,7 @@ async def a_get_future_option_chain( def get_future_option_chain( session: Session, symbol: str -) -> Dict[date, List[FutureOption]]: +) -> dict[date, list[FutureOption]]: """ Returns a mapping of expiration date to a list of futures options objects representing the options chain for the given symbol. diff --git a/tastytrade/metrics.py b/tastytrade/metrics.py index 1d873bc..670e863 100644 --- a/tastytrade/metrics.py +++ b/tastytrade/metrics.py @@ -1,6 +1,6 @@ from datetime import date, datetime from decimal import Decimal -from typing import List, Optional +from typing import Optional from tastytrade.session import Session from tastytrade.utils import TastytradeJsonDataclass @@ -84,7 +84,7 @@ class MarketMetricInfo(TastytradeJsonDataclass): liquidity_rating: Optional[int] = None updated_at: datetime option_expiration_implied_volatilities: Optional[ - List[OptionExpirationImpliedVolatility] + list[OptionExpirationImpliedVolatility] ] = None # noqa: E501 beta: Optional[Decimal] = None corr_spy_3month: Optional[Decimal] = None @@ -114,8 +114,8 @@ class MarketMetricInfo(TastytradeJsonDataclass): async def a_get_market_metrics( - session: Session, symbols: List[str] -) -> List[MarketMetricInfo]: + session: Session, symbols: list[str] +) -> list[MarketMetricInfo]: """ Retrieves market metrics for the given symbols. @@ -128,7 +128,7 @@ async def a_get_market_metrics( return [MarketMetricInfo(**i) for i in data["items"]] -def get_market_metrics(session: Session, symbols: List[str]) -> List[MarketMetricInfo]: +def get_market_metrics(session: Session, symbols: list[str]) -> list[MarketMetricInfo]: """ Retrieves market metrics for the given symbols. @@ -139,7 +139,7 @@ def get_market_metrics(session: Session, symbols: List[str]) -> List[MarketMetri return [MarketMetricInfo(**i) for i in data["items"]] -async def a_get_dividends(session: Session, symbol: str) -> List[DividendInfo]: +async def a_get_dividends(session: Session, symbol: str) -> list[DividendInfo]: """ Retrieves dividend information for the given symbol. @@ -153,7 +153,7 @@ async def a_get_dividends(session: Session, symbol: str) -> List[DividendInfo]: return [DividendInfo(**i) for i in data["items"]] -def get_dividends(session: Session, symbol: str) -> List[DividendInfo]: +def get_dividends(session: Session, symbol: str) -> list[DividendInfo]: """ Retrieves dividend information for the given symbol. @@ -169,7 +169,7 @@ def get_dividends(session: Session, symbol: str) -> List[DividendInfo]: async def a_get_earnings( session: Session, symbol: str, start_date: date -) -> List[EarningsInfo]: +) -> list[EarningsInfo]: """ Retrieves earnings information for the given symbol. @@ -186,7 +186,7 @@ async def a_get_earnings( return [EarningsInfo(**i) for i in data["items"]] -def get_earnings(session: Session, symbol: str, start_date: date) -> List[EarningsInfo]: +def get_earnings(session: Session, symbol: str, start_date: date) -> list[EarningsInfo]: """ Retrieves earnings information for the given symbol. diff --git a/tastytrade/order.py b/tastytrade/order.py index f7fc304..2442c27 100644 --- a/tastytrade/order.py +++ b/tastytrade/order.py @@ -1,7 +1,7 @@ from datetime import date, datetime from decimal import Decimal from enum import Enum -from typing import Any, Dict, List, Optional, Union +from typing import Any, Optional, Union from pydantic import computed_field, field_serializer, model_validator @@ -135,7 +135,7 @@ class Leg(TastytradeJsonDataclass): action: OrderAction quantity: Optional[Decimal] = None remaining_quantity: Optional[Decimal] = None - fills: Optional[List[FillInfo]] = None + fills: Optional[list[FillInfo]] = None class TradeableTastytradeJsonDataclass(TastytradeJsonDataclass): @@ -206,7 +206,7 @@ class OrderCondition(TastytradeJsonDataclass): is_threshold_based_on_notional: bool triggered_at: datetime triggered_value: Decimal - price_components: List[OrderConditionPriceComponent] + price_components: list[OrderConditionPriceComponent] class OrderRule(TastytradeJsonDataclass): @@ -218,7 +218,7 @@ class OrderRule(TastytradeJsonDataclass): routed_at: datetime cancel_at: datetime cancelled_at: datetime - order_conditions: List[OrderCondition] + order_conditions: list[OrderCondition] class NewOrder(TastytradeJsonDataclass): @@ -230,7 +230,7 @@ class NewOrder(TastytradeJsonDataclass): time_in_force: OrderTimeInForce order_type: OrderType source: str = f"tastyware/tastytrade:v{VERSION}" - legs: List[Leg] + legs: list[Leg] gtc_date: Optional[date] = None #: For a stop/stop limit order. If the latter, use price for the limit price stop_trigger: Optional[Decimal] = None @@ -263,7 +263,7 @@ class NewComplexOrder(TastytradeJsonDataclass): Also used for modifying existing orders. """ - orders: List[NewOrder] + orders: list[NewOrder] source: str = f"tastyware/tastytrade:v{VERSION}" trigger_order: Optional[NewOrder] = None type: ComplexOrderType = ComplexOrderType.OCO @@ -290,7 +290,7 @@ class PlacedOrder(TastytradeJsonDataclass): editable: bool edited: bool updated_at: datetime - legs: List[Leg] + legs: list[Leg] #: the ID of the order; test orders placed with dry_run don't have an ID id: int = -1 size: Optional[Decimal] = None @@ -330,7 +330,7 @@ class PlacedComplexOrder(TastytradeJsonDataclass): account_number: str type: str - orders: List[PlacedOrder] + orders: list[PlacedOrder] #: the ID of the order; test orders placed with dry_run don't have an ID id: int = -1 trigger_order: Optional[PlacedOrder] = None @@ -338,7 +338,7 @@ class PlacedComplexOrder(TastytradeJsonDataclass): ratio_price_threshold: Optional[Decimal] = None ratio_price_comparator: Optional[str] = None ratio_price_is_threshold_based_on_notional: Optional[bool] = None - related_orders: Optional[List[Dict[str, str]]] = None + related_orders: Optional[list[dict[str, str]]] = None class BuyingPowerEffect(TastytradeJsonDataclass): @@ -405,8 +405,8 @@ class PlacedComplexOrderResponse(TastytradeJsonDataclass): buying_power_effect: BuyingPowerEffect complex_order: PlacedComplexOrder fee_calculation: Optional[FeeCalculation] = None - warnings: Optional[List[Message]] = None - errors: Optional[List[Message]] = None + warnings: Optional[list[Message]] = None + errors: Optional[list[Message]] = None class PlacedOrderResponse(TastytradeJsonDataclass): @@ -417,8 +417,8 @@ class PlacedOrderResponse(TastytradeJsonDataclass): buying_power_effect: BuyingPowerEffect order: PlacedOrder fee_calculation: Optional[FeeCalculation] = None - warnings: Optional[List[Message]] = None - errors: Optional[List[Message]] = None + warnings: Optional[list[Message]] = None + errors: Optional[list[Message]] = None class OrderChainEntry(TastytradeJsonDataclass): @@ -461,8 +461,8 @@ class OrderChainNode(TastytradeJsonDataclass): fill_cost_per_quantity: Optional[Decimal] = None order_fill_count: Optional[int] = None roll: Optional[bool] = None - legs: Optional[List[OrderChainLeg]] = None - entries: Optional[List[OrderChainEntry]] = None + legs: Optional[list[OrderChainLeg]] = None + entries: Optional[list[OrderChainEntry]] = None @model_validator(mode="before") @classmethod @@ -501,7 +501,7 @@ class ComputedData(TastytradeJsonDataclass): total_cost: Decimal gcd_open_quantity: Decimal fees_missing: bool - open_entries: List[OrderChainEntry] + open_entries: list[OrderChainEntry] total_cost_per_unit: Optional[Decimal] = None @model_validator(mode="before") @@ -534,7 +534,7 @@ class OrderChain(TastytradeJsonDataclass): description: str underlying_symbol: str computed_data: ComputedData - lite_nodes: List[OrderChainNode] + lite_nodes: list[OrderChainNode] lite_nodes_sizes: Optional[int] = None updated_at: Optional[datetime] = None created_at: Optional[datetime] = None diff --git a/tastytrade/search.py b/tastytrade/search.py index 5fd5678..618da92 100644 --- a/tastytrade/search.py +++ b/tastytrade/search.py @@ -1,5 +1,3 @@ -from typing import List - from tastytrade.session import Session from tastytrade.utils import TastytradeJsonDataclass @@ -13,7 +11,7 @@ class SymbolData(TastytradeJsonDataclass): description: str -async def a_symbol_search(session: Session, symbol: str) -> List[SymbolData]: +async def a_symbol_search(session: Session, symbol: str) -> list[SymbolData]: """ Performs a symbol search using the Tastytrade API and returns a list of symbols that are similar to the given search phrase. @@ -31,7 +29,7 @@ async def a_symbol_search(session: Session, symbol: str) -> List[SymbolData]: return [SymbolData(**i) for i in data["items"]] -def symbol_search(session: Session, symbol: str) -> List[SymbolData]: +def symbol_search(session: Session, symbol: str) -> list[SymbolData]: """ Performs a symbol search using the Tastytrade API and returns a list of symbols that are similar to the given search phrase. diff --git a/tastytrade/session.py b/tastytrade/session.py index 8d13279..58a7df8 100644 --- a/tastytrade/session.py +++ b/tastytrade/session.py @@ -1,5 +1,5 @@ from datetime import date, datetime -from typing import Any, Dict, List, Optional, Union +from typing import Any, Optional, Union import httpx @@ -95,7 +95,7 @@ class CustomerAccountType(TastytradeJsonDataclass): is_tax_advantaged: bool is_publicly_available: bool has_multiple_owners: bool - margin_types: List[CustomerAccountMarginType] + margin_types: list[CustomerAccountMarginType] class CustomerEntity(TastytradeJsonDataclass): @@ -107,7 +107,7 @@ class CustomerEntity(TastytradeJsonDataclass): address: Address business_nature: str email: str - entity_officers: List[EntityOfficer] + entity_officers: list[EntityOfficer] entity_suitability: EntitySuitability entity_type: str foreign_institution: str @@ -207,7 +207,7 @@ class Customer(TastytradeJsonDataclass): has_delayed_quotes: bool has_pending_or_approved_application: bool is_professional: bool - permitted_account_types: List[CustomerAccountType] + permitted_account_types: list[CustomerAccountType] created_at: datetime identifiable_type: str person: CustomerPerson @@ -341,11 +341,11 @@ def __init__( #: URL for dxfeed websocket self.dxlink_url = data["dxlink-url"] - async def _a_get(self, url, **kwargs) -> Dict[str, Any]: + async def _a_get(self, url, **kwargs) -> dict[str, Any]: response = await self.async_client.get(url, timeout=30, **kwargs) return self._validate_and_parse(response) - def _get(self, url, **kwargs) -> Dict[str, Any]: + def _get(self, url, **kwargs) -> dict[str, Any]: response = self.sync_client.get(url, timeout=30, **kwargs) return self._validate_and_parse(response) @@ -357,23 +357,23 @@ def _delete(self, url, **kwargs) -> None: response = self.sync_client.delete(url, **kwargs) validate_response(response) - async def _a_post(self, url, **kwargs) -> Dict[str, Any]: + async def _a_post(self, url, **kwargs) -> dict[str, Any]: response = await self.async_client.post(url, **kwargs) return self._validate_and_parse(response) - def _post(self, url, **kwargs) -> Dict[str, Any]: + def _post(self, url, **kwargs) -> dict[str, Any]: response = self.sync_client.post(url, **kwargs) return self._validate_and_parse(response) - async def _a_put(self, url, **kwargs) -> Dict[str, Any]: + async def _a_put(self, url, **kwargs) -> dict[str, Any]: response = await self.async_client.put(url, **kwargs) return self._validate_and_parse(response) - def _put(self, url, **kwargs) -> Dict[str, Any]: + def _put(self, url, **kwargs) -> dict[str, Any]: response = self.sync_client.put(url, **kwargs) return self._validate_and_parse(response) - def _validate_and_parse(self, response: httpx._models.Response) -> Dict[str, Any]: + def _validate_and_parse(self, response: httpx._models.Response) -> dict[str, Any]: validate_response(response) return response.json()["data"] diff --git a/tastytrade/streamer.py b/tastytrade/streamer.py index ffe6a68..0158d30 100644 --- a/tastytrade/streamer.py +++ b/tastytrade/streamer.py @@ -6,7 +6,7 @@ from decimal import Decimal from enum import Enum from ssl import SSLContext, create_default_context -from typing import Any, AsyncIterator, Dict, List, Optional, Type, TypeVar, Union +from typing import Any, AsyncIterator, Optional, Type, TypeVar, Union import websockets from pydantic import model_validator @@ -39,7 +39,6 @@ STREAMER_URL = "wss://streamer.tastyworks.com" DXLINK_VERSION = "0.1-js/1.0.0-beta.4" -T = TypeVar("T") class QuoteAlert(TastytradeJsonDataclass): @@ -126,6 +125,7 @@ class SubscriptionType(str, Enum): UnderlyingYearGainSummary, Watchlist, ] +T = TypeVar("T", bound=AlertType) MAP_EVENTS = { "Candle": Candle, @@ -151,6 +151,7 @@ class SubscriptionType(str, Enum): Trade, Underlying, ] +U = TypeVar("U", bound=EventType) class AlertStreamer: @@ -186,7 +187,7 @@ def __init__(self, session: Session): #: The base url for the streamer websocket self.base_url: str = CERT_STREAMER_URL if session.is_test else STREAMER_URL - self._queues: Dict[str, Queue] = defaultdict(Queue) + self._queues: dict[str, Queue] = defaultdict(Queue) self._websocket: Optional[WebSocketClientProtocol] = None self._connect_task = asyncio.create_task(self._connect()) @@ -259,7 +260,7 @@ async def _map_message(self, type_str: str, data: dict): ) await self._queues[type_str].put(MAP_ALERTS[type_str](**data)) - async def subscribe_accounts(self, accounts: List[Account]) -> None: + async def subscribe_accounts(self, accounts: list[Account]) -> None: """ Subscribes to account-level updates (balances, orders, positions). @@ -302,13 +303,13 @@ async def _heartbeat(self) -> None: async def _subscribe( self, subscription: SubscriptionType, - value: Union[Optional[str], List[str]] = "", + value: Union[Optional[str], list[str]] = "", ) -> None: """ Subscribes to a :class:`SubscriptionType`. Depending on the kind of subscription, the value parameter may be required. """ - message: Dict[str, Any] = {"auth-token": self.token, "action": subscription} + message: dict[str, Any] = {"auth-token": self.token, "action": subscription} if value: message["value"] = value logger.debug("sending alert subscription: %s", message) @@ -341,8 +342,8 @@ def __init__( ): self._counter = 0 self._lock: Lock = Lock() - self._queues: Dict[str, Queue] = defaultdict(Queue) - self._channels: Dict[str, int] = { + self._queues: dict[str, Queue] = defaultdict(Queue) + self._channels: dict[str, int] = { "Candle": 1, "Greeks": 3, "Profile": 5, @@ -353,7 +354,7 @@ def __init__( "Trade": 15, "Underlying": 17, } - self._subscription_state: Dict[str, str] = defaultdict(lambda: "CHANNEL_CLOSED") + self._subscription_state: dict[str, str] = defaultdict(lambda: "CHANNEL_CLOSED") #: The unique client identifier received from the server self._session = session @@ -454,7 +455,7 @@ async def _authenticate_connection(self): } await self._websocket.send(json.dumps(message)) - async def listen(self, event_class: Type[T]) -> AsyncIterator[T]: + async def listen(self, event_class: Type[U]) -> AsyncIterator[U]: """ Using the existing subscriptions, pulls events of the given type and yield returns them. Never exits unless there's an error or the channel @@ -469,7 +470,7 @@ async def listen(self, event_class: Type[T]) -> AsyncIterator[T]: while True: yield await self._queues[cls_str].get() - def get_event_nowait(self, event_class: Type[T]) -> Optional[T]: + def get_event_nowait(self, event_class: Type[U]) -> Optional[U]: """ Using the existing subscriptions, pulls an event of the given type and returns it. If the queue is empty None is returned. @@ -485,7 +486,7 @@ def get_event_nowait(self, event_class: Type[T]) -> Optional[T]: except QueueEmpty: return None - async def get_event(self, event_class: Type[T]) -> T: + async def get_event(self, event_class: Type[U]) -> U: """ Using the existing subscription, pulls an event of the given type and returns it. @@ -511,14 +512,13 @@ async def _heartbeat(self) -> None: # send the heartbeat every 30 seconds await asyncio.sleep(30) - async def subscribe(self, event_class: Type[EventType], symbols: List[str]) -> None: + async def subscribe(self, event_class: Type[EventType], symbols: list[str]) -> None: """ Subscribes to quotes for given list of symbols. Used for recurring data feeds. For candles, use :meth:`subscribe_candle` instead. - :param event_class: type of subscription to add, should be of - `~tastytrade.streamer.EventType` + :param event_class: type of subscription to add, should be of :any:`EventType` :param symbols: list of symbols to subscribe for """ cls_str = MAP_EVENTS_REVERSE[event_class] @@ -585,7 +585,7 @@ def dict_from_schema(event_class: Any): await self._websocket.send(json.dumps(message)) async def unsubscribe( - self, event_class: Type[EventType], symbols: List[str] + self, event_class: Type[EventType], symbols: list[str] ) -> None: """ Removes existing subscription for given list of symbols. @@ -607,7 +607,7 @@ async def unsubscribe( async def subscribe_candle( self, - symbols: List[str], + symbols: list[str], interval: str, start_time: datetime, end_time: Optional[datetime] = None, diff --git a/tastytrade/utils.py b/tastytrade/utils.py index 02286b5..965a59f 100644 --- a/tastytrade/utils.py +++ b/tastytrade/utils.py @@ -1,15 +1,15 @@ from datetime import date, datetime, timedelta from decimal import Decimal from enum import Enum -from typing import Any, List, Optional +from typing import Any, Optional +from zoneinfo import ZoneInfo import pandas_market_calendars as mcal # type: ignore -import pytz from httpx._models import Response from pydantic import BaseModel NYSE = mcal.get_calendar("NYSE") -TZ = pytz.timezone("US/Eastern") +TZ = ZoneInfo("US/Eastern") class PriceEffect(str, Enum): @@ -51,7 +51,7 @@ def is_market_open_on(day: date = today_in_new_york()) -> bool: :return: whether the market opens on given day """ date_range = NYSE.valid_days(day, day) - return len(date_range) != 0 + return not date_range.empty def get_third_friday(day: date = today_in_new_york()) -> date: @@ -261,7 +261,7 @@ def _get_sign(value: Optional[Decimal]) -> Optional[PriceEffect]: return PriceEffect.DEBIT if value < 0 else PriceEffect.CREDIT -def _set_sign_for(data: Any, properties: List[str]) -> Any: +def _set_sign_for(data: Any, properties: list[str]) -> Any: """ Handles setting the sign of a number using the associated "-effect" field. diff --git a/tastytrade/watchlists.py b/tastytrade/watchlists.py index 54626c4..96417a6 100644 --- a/tastytrade/watchlists.py +++ b/tastytrade/watchlists.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional +from typing import Any, Optional from typing_extensions import Self @@ -27,10 +27,10 @@ class PairsWatchlist(TastytradeJsonDataclass): name: str order_index: int - pairs_equations: List[Pair] + pairs_equations: list[Pair] @classmethod - async def a_get_pairs_watchlists(cls, session: Session) -> List[Self]: + async def a_get_pairs_watchlists(cls, session: Session) -> list[Self]: """ Fetches a list of all Tastytrade public pairs watchlists. @@ -40,7 +40,7 @@ async def a_get_pairs_watchlists(cls, session: Session) -> List[Self]: return [cls(**i) for i in data["items"]] @classmethod - def get_pairs_watchlists(cls, session: Session) -> List[Self]: + def get_pairs_watchlists(cls, session: Session) -> list[Self]: """ Fetches a list of all Tastytrade public pairs watchlists. @@ -79,14 +79,14 @@ class Watchlist(TastytradeJsonDataclass): """ name: str - watchlist_entries: Optional[List[Dict[str, Any]]] = None + watchlist_entries: Optional[list[dict[str, Any]]] = None group_name: str = "default" order_index: int = 9999 @classmethod async def a_get_public_watchlists( cls, session: Session, counts_only: bool = False - ) -> List[Self]: + ) -> list[Self]: """ Fetches a list of all Tastytrade public watchlists. @@ -101,7 +101,7 @@ async def a_get_public_watchlists( @classmethod def get_public_watchlists( cls, session: Session, counts_only: bool = False - ) -> List[Self]: + ) -> list[Self]: """ Fetches a list of all Tastytrade public watchlists. @@ -134,7 +134,7 @@ def get_public_watchlist(cls, session: Session, name: str) -> Self: return cls(**data) @classmethod - async def a_get_private_watchlists(cls, session: Session) -> List[Self]: + async def a_get_private_watchlists(cls, session: Session) -> list[Self]: """ Fetches a the user's private watchlists. @@ -144,7 +144,7 @@ async def a_get_private_watchlists(cls, session: Session) -> List[Self]: return [cls(**i) for i in data["items"]] @classmethod - def get_private_watchlists(cls, session: Session) -> List[Self]: + def get_private_watchlists(cls, session: Session) -> list[Self]: """ Fetches a the user's private watchlists. diff --git a/uv.lock b/uv.lock index 8e9b5c4..b405720 100644 --- a/uv.lock +++ b/uv.lock @@ -586,7 +586,7 @@ wheels = [ [[package]] name = "tastytrade" -version = "9.1" +version = "9.2" source = { editable = "." } dependencies = [ { name = "httpx" }, @@ -602,7 +602,6 @@ dev = [ { name = "pytest-aio" }, { name = "pytest-cov" }, { name = "ruff" }, - { name = "types-pytz" }, ] [package.metadata] @@ -620,7 +619,6 @@ dev = [ { name = "pytest-aio", specifier = ">=1.5.0" }, { name = "pytest-cov", specifier = ">=5.0.0" }, { name = "ruff", specifier = ">=0.6.9" }, - { name = "types-pytz", specifier = ">=2024.2.0.20241003" }, ] [[package]] @@ -641,15 +639,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/03/98/eb27cc78ad3af8e302c9d8ff4977f5026676e130d28dd7578132a457170c/toolz-1.0.0-py3-none-any.whl", hash = "sha256:292c8f1c4e7516bf9086f8850935c799a874039c8bcf959d47b600e4c44a6236", size = 56383 }, ] -[[package]] -name = "types-pytz" -version = "2024.2.0.20241003" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/66/d0/73aa3063a9ef9881bd7103cb4ae379bfd8fafda0e86b01b694d676313a4b/types-pytz-2024.2.0.20241003.tar.gz", hash = "sha256:575dc38f385a922a212bac00a7d6d2e16e141132a3c955078f4a4fd13ed6cb44", size = 5474 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/60/2a2977ce0f91255bbb668350b127a801a06ad37c326a2e5bfd52f03e0784/types_pytz-2024.2.0.20241003-py3-none-any.whl", hash = "sha256:3e22df1336c0c6ad1d29163c8fda82736909eb977281cb823c57f8bae07118b7", size = 5245 }, -] - [[package]] name = "typing-extensions" version = "4.12.2"