diff --git a/docs/conf.py b/docs/conf.py index 222bc48..5ee4348 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -9,7 +9,7 @@ project = 'tastytrade' copyright = '2023, Graeme Holliday' author = 'Graeme Holliday' -release = '6.7' +release = '7.0' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/docs/data-streamer.rst b/docs/data-streamer.rst index 92c4877..2924e50 100644 --- a/docs/data-streamer.rst +++ b/docs/data-streamer.rst @@ -88,6 +88,7 @@ For example, we can use the streamer to create an option chain that will continu from tastytrade import DXFeedStreamer from tastytrade.instruments import get_option_chain from tastytrade.dxfeed import Greeks, Quote + from tastytrade.utils import today_in_new_york @dataclass class LivePrices: @@ -102,7 +103,7 @@ For example, we can use the streamer to create an option chain that will continu cls, session: ProductionSession, symbol: str = 'SPY', - expiration: date = date.today() + expiration: date = today_in_new_york() ): chain = get_option_chain(session, symbol) options = [o for o in chain[expiration]] diff --git a/requirements.txt b/requirements.txt index ba2398e..35eccbf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,9 +3,10 @@ mypy==1.4.1 flake8==5.0.4 isort==5.11.5 types-requests==2.31.0.1 +types-pytz==2024.1.0.20240203 websockets==11.0.3 pandas_market_calendars==4.3.3 -pydantic==1.10.11 +pydantic==2.6.3 pytest==7.4.0 pytest_cov==4.1.0 pytest-asyncio==0.21.1 diff --git a/setup.py b/setup.py index f98fa96..582a4ef 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ setup( name='tastytrade', - version='6.7', + version='7.0', description='An unofficial SDK for Tastytrade!', long_description=LONG_DESCRIPTION, long_description_content_type='text/x-rst', @@ -18,7 +18,7 @@ install_requires=[ 'requests<3', 'websockets>=11.0.3', - 'pydantic<2', + 'pydantic>=2.6.3', 'pandas_market_calendars>=4.3.3' ], packages=find_packages(exclude=['ez_setup', 'tests*']), diff --git a/tastytrade/__init__.py b/tastytrade/__init__.py index 1bccd8a..6f6fcb5 100644 --- a/tastytrade/__init__.py +++ b/tastytrade/__init__.py @@ -2,7 +2,7 @@ API_URL = 'https://api.tastyworks.com' CERT_URL = 'https://api.cert.tastyworks.com' -VERSION = '6.7' +VERSION = '7.0' logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) diff --git a/tastytrade/account.py b/tastytrade/account.py index db904b6..156916b 100644 --- a/tastytrade/account.py +++ b/tastytrade/account.py @@ -11,7 +11,7 @@ PlacedOrderResponse, PriceEffect) from tastytrade.session import ProductionSession, Session from tastytrade.utils import (TastytradeError, TastytradeJsonDataclass, - validate_response) + today_in_new_york, validate_response) class EmptyDict(BaseModel): @@ -254,7 +254,7 @@ class NetLiqOhlc(TastytradeJsonDataclass): total_high: Decimal total_low: Decimal total_close: Decimal - time: datetime + time: str class PositionLimit(TastytradeJsonDataclass): @@ -379,7 +379,7 @@ class Account(TastytradeJsonDataclass): nickname: str account_type_name: str is_closed: bool - day_trader_status: str + day_trader_status: Union[str, bool] is_firm_error: bool is_firm_proprietary: bool is_futures_approved: bool @@ -587,7 +587,7 @@ def get_history( types: Optional[List[str]] = None, sub_types: Optional[List[str]] = None, start_date: Optional[date] = None, - end_date: date = date.today(), + end_date: date = today_in_new_york(), instrument_type: Optional[InstrumentType] = None, symbol: Optional[str] = None, underlying_symbol: Optional[str] = None, @@ -700,7 +700,7 @@ def get_transaction( def get_total_fees( self, session: Session, - date: date = date.today() + date: date = today_in_new_york() ) -> Dict[str, Any]: """ Get the total fees for a given date. diff --git a/tastytrade/dxfeed/event.py b/tastytrade/dxfeed/event.py index f355834..e4758dc 100644 --- a/tastytrade/dxfeed/event.py +++ b/tastytrade/dxfeed/event.py @@ -45,7 +45,7 @@ def from_stream(cls, data: list) -> List['Event']: # pragma: no cover :return: list of event objects from data """ objs = [] - size = len(cls.__fields__) + size = len(cls.model_fields) multiples = len(data) / size if not multiples.is_integer(): msg = 'Mapper data input values are not a multiple of the key size' diff --git a/tastytrade/instruments.py b/tastytrade/instruments.py index a679027..9d40354 100644 --- a/tastytrade/instruments.py +++ b/tastytrade/instruments.py @@ -1090,7 +1090,7 @@ def get_option_chain( In the case that there are two expiries on the same day (e.g. SPXW and SPX AM options), both will be returned in the same list. If you just want one expiry, you'll need to filter the list yourself, or use - ~:class:`NestedOptionChain` instead. + :class:`NestedOptionChain` instead. :param session: the session to use for the request. :param symbol: the symbol to get the option chain for. diff --git a/tastytrade/order.py b/tastytrade/order.py index 10600ec..abcc671 100644 --- a/tastytrade/order.py +++ b/tastytrade/order.py @@ -261,8 +261,8 @@ class PlacedOrder(TastytradeJsonDataclass): edited: bool updated_at: datetime legs: List[Leg] - size: Optional[str] = None - id: Optional[str] = None + size: Optional[int] = None + id: Optional[int] = None price: Optional[Decimal] = None price_effect: Optional[PriceEffect] = None gtc_date: Optional[date] = None diff --git a/tastytrade/utils.py b/tastytrade/utils.py index 3db009d..79932bb 100644 --- a/tastytrade/utils.py +++ b/tastytrade/utils.py @@ -1,13 +1,33 @@ -from datetime import date, timedelta +from datetime import date, datetime, timedelta import pandas_market_calendars as mcal # type: ignore +import pytz from pydantic import BaseModel from requests import Response NYSE = mcal.get_calendar('NYSE') +TZ = pytz.timezone('US/Eastern') -def get_third_friday(day: date = date.today()) -> date: +def now_in_new_york() -> datetime: + """ + Gets the current time in the New York timezone. + + :return: current time as datetime + """ + return datetime.now(TZ) + + +def today_in_new_york() -> date: + """ + Gets the current date in the New York timezone. + + :return: current date + """ + return now_in_new_york().date() + + +def get_third_friday(day: date = today_in_new_york()) -> date: """ Gets the monthly expiration associated with the month of the given date, or the monthly expiration associated with today's month. @@ -29,11 +49,11 @@ def get_tasty_monthly() -> date: :return: the closest to 45 DTE monthly expiration """ - day = date.today() + day = today_in_new_york() exp1 = get_third_friday(day + timedelta(weeks=4)) exp2 = get_third_friday(day + timedelta(weeks=8)) day45 = day + timedelta(days=45) - return exp1 if day45 - exp2 < exp2 - day45 else exp2 + return exp1 if day45 - exp1 < exp2 - day45 else exp2 def _get_last_day_of_month(day: date) -> date: @@ -44,7 +64,7 @@ def _get_last_day_of_month(day: date) -> date: return last - timedelta(days=1) -def get_future_fx_monthly(day: date = date.today()) -> date: +def get_future_fx_monthly(day: date = today_in_new_york()) -> date: """ Gets the monthly expiration associated with the FX futures: /6E, /6A, etc. As far as I can tell, these expire on the first Friday prior to the second @@ -63,7 +83,7 @@ def get_future_fx_monthly(day: date = date.today()) -> date: return day -def get_future_treasury_monthly(day: date = date.today()) -> date: +def get_future_treasury_monthly(day: date = today_in_new_york()) -> date: """ Gets the monthly expiration associated with the treasury futures: /ZN, /ZB, etc. According to CME, these expire the Friday before the 2nd last @@ -85,7 +105,7 @@ def get_future_treasury_monthly(day: date = date.today()) -> date: return itr - timedelta(days=1) -def get_future_metal_monthly(day: date = date.today()) -> date: +def get_future_metal_monthly(day: date = today_in_new_york()) -> date: """ Gets the monthly expiration associated with the metals futures: /GC, /SI, etc. According to CME, these expire on the 4th last business day of the @@ -106,7 +126,7 @@ def get_future_metal_monthly(day: date = date.today()) -> date: return itr -def get_future_grain_monthly(day: date = date.today()) -> date: +def get_future_grain_monthly(day: date = today_in_new_york()) -> date: """ Gets the monthly expiration associated with the grain futures: /ZC, /ZW, etc. According to CME, these expire on the Friday which precedes, by at @@ -125,7 +145,7 @@ def get_future_grain_monthly(day: date = date.today()) -> date: return itr -def get_future_oil_monthly(day: date = date.today()) -> date: +def get_future_oil_monthly(day: date = today_in_new_york()) -> date: """ Gets the monthly expiration associated with the WTI oil futures: /CL and /MCL. According to CME, these expire 6 business days before the 25th day @@ -142,7 +162,7 @@ def get_future_oil_monthly(day: date = date.today()) -> date: return valid_range[-7] -def get_future_index_monthly(day: date = date.today()) -> date: +def get_future_index_monthly(day: date = today_in_new_york()) -> date: """ Gets the monthly expiration associated with the index futures: /ES, /RTY, /NQ, etc. According to CME, these expire on the last business day of the @@ -183,7 +203,7 @@ class TastytradeJsonDataclass(BaseModel): """ class Config: alias_generator = _dasherize - allow_population_by_field_name = True + populate_by_name = True def validate_response(response: Response) -> None: diff --git a/tests/test_account.py b/tests/test_account.py index 02019d3..e191ede 100644 --- a/tests/test_account.py +++ b/tests/test_account.py @@ -103,7 +103,7 @@ def test_get_order(session, account, placed_order): def test_replace_and_delete_order(session, account, new_order, placed_order): - modified_order = new_order.copy() + modified_order = new_order.model_copy() modified_order.price = Decimal(40) replaced = account.replace_order(session, placed_order.id, modified_order) sleep(3) diff --git a/tests/test_utils.py b/tests/test_utils.py index 97f5139..2aeeab8 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -4,7 +4,7 @@ get_future_index_monthly, get_future_metal_monthly, get_future_oil_monthly, get_future_treasury_monthly, get_tasty_monthly, - get_third_friday) + get_third_friday, today_in_new_york) def test_get_third_friday(): @@ -12,7 +12,7 @@ def test_get_third_friday(): def test_get_tasty_monthly(): - delta = (get_tasty_monthly() - date.today()).days + delta = (get_tasty_monthly() - today_in_new_york()).days assert abs(45 - delta) <= 17