Skip to content

Commit

Permalink
bump version, annotate tests, fix #194
Browse files Browse the repository at this point in the history
  • Loading branch information
Graeme22 committed Jan 17, 2025
1 parent 0b3cdf8 commit 7928e9b
Show file tree
Hide file tree
Showing 14 changed files with 160 additions and 134 deletions.
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
project = "tastytrade"
copyright = "2024, Graeme Holliday"
author = "Graeme Holliday"
release = "9.7"
release = "9.8"

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
Expand Down
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ A simple, reverse-engineered, sync/async SDK for Tastytrade built on their (now
.. tip::
Want to see the SDK in action? Check out `tastytrade-cli <https://github.com/tastyware/tastytrade-cli>`_, a CLI for Tastytrade that showcases many of the SDK's features.

.. tip::
.. note::
Do you use TradeStation? We're building a `brand-new SDK <https://github.com/tastyware/tradestation>`_ for TS users, with many of the same features!

.. toctree::
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "tastytrade"
version = "9.7"
version = "9.8"
description = "An unofficial, sync/async SDK for Tastytrade!"
readme = "README.md"
requires-python = ">=3.9"
Expand Down
2 changes: 1 addition & 1 deletion tastytrade/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.7"
VERSION = "9.8"

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
Expand Down
2 changes: 2 additions & 0 deletions tastytrade/streamer.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ async def close(self) -> None:
self._reconnect_task.cancel()
tasks.append(self._reconnect_task)
await asyncio.gather(*tasks)
await self._websocket.wait_closed() # type: ignore

async def _connect(self) -> None:
"""
Expand Down Expand Up @@ -443,6 +444,7 @@ async def close(self) -> None:
self._reconnect_task.cancel()
tasks.append(self._reconnect_task)
await asyncio.gather(*tasks)
await self._websocket.wait_closed()

async def _connect(self) -> None:
"""
Expand Down
9 changes: 6 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
from typing import AsyncGenerator

from pytest import fixture

Expand All @@ -7,12 +8,12 @@

# Run all tests with asyncio only
@fixture(scope="session")
def aiolib():
def aiolib() -> str:
return "asyncio"


@fixture(scope="session")
def credentials():
def credentials() -> tuple[str, str]:
username = os.getenv("TT_USERNAME")
password = os.getenv("TT_PASSWORD")
assert username is not None
Expand All @@ -21,7 +22,9 @@ def credentials():


@fixture(scope="session")
async def session(credentials, aiolib):
async def session(
credentials: tuple[str, str], aiolib: str
) -> AsyncGenerator[Session, None]:
session = Session(*credentials)
yield session
session.destroy()
114 changes: 65 additions & 49 deletions tests/test_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,157 +5,162 @@

from pytest import fixture

from tastytrade import Account
from tastytrade import Account, Session
from tastytrade.instruments import Equity
from tastytrade.order import (
NewComplexOrder,
NewOrder,
OrderAction,
OrderTimeInForce,
OrderType,
PlacedOrder,
)


@fixture(scope="module")
def account_number():
def account_number() -> str:
account_number = os.getenv("TT_ACCOUNT")
assert account_number is not None
return account_number


@fixture(scope="module")
async def account(session, account_number, aiolib):
async def account(session: Session, account_number: str, aiolib: str) -> Account:
return Account.get_account(session, account_number)


def test_get_account(account):
def test_get_account(account: Account):
pass


def test_get_accounts(session):
def test_get_accounts(session: Session):
assert Account.get_accounts(session) != []


def test_get_trading_status(session, account):
def test_get_trading_status(session: Session, account: Account):
account.get_trading_status(session)


def test_get_balances(session, account):
def test_get_balances(session: Session, account: Account):
account.get_balances(session)


def test_get_balance_snapshots(session, account):
def test_get_balance_snapshots(session: Session, account: Account):
account.get_balance_snapshots(session)


def test_get_positions(session, account):
def test_get_positions(session: Session, account: Account):
account.get_positions(session)


def test_get_history(session, account):
def test_get_history(session: Session, account: Account):
account.get_history(session, page_offset=0)


def test_get_total_fees(session, account):
def test_get_total_fees(session: Session, account: Account):
account.get_total_fees(session)


def test_get_position_limit(session, account):
def test_get_position_limit(session: Session, account: Account):
account.get_position_limit(session)


def test_get_margin_requirements(session, account):
def test_get_margin_requirements(session: Session, account: Account):
account.get_margin_requirements(session)


def test_get_net_liquidating_value_history(session, account):
def test_get_net_liquidating_value_history(session: Session, account: Account):
account.get_net_liquidating_value_history(session, time_back="1y")


def test_get_effective_margin_requirements(session, account):
def test_get_effective_margin_requirements(session: Session, account: Account):
account.get_effective_margin_requirements(session, "SPY")


def test_get_order_history(session, account):
def test_get_order_history(session: Session, account: Account):
account.get_order_history(session, page_offset=0)


def test_get_complex_order_history(session, account):
def test_get_complex_order_history(session: Session, account: Account):
account.get_complex_order_history(session, page_offset=0)


def test_get_live_orders(session, account):
def test_get_live_orders(session: Session, account: Account):
account.get_live_orders(session)


async def test_get_account_async(session, account_number):
async def test_get_account_async(session: Session, account_number: str):
await Account.a_get_account(session, account_number)


async def test_get_accounts_async(session):
async def test_get_accounts_async(session: Session):
accounts = await Account.a_get_accounts(session)
assert accounts != []


async def test_get_trading_status_async(session, account):
async def test_get_trading_status_async(session: Session, account: Account):
await account.a_get_trading_status(session)


async def test_get_balances_async(session, account):
async def test_get_balances_async(session: Session, account: Account):
await account.a_get_balances(session)


async def test_get_balance_snapshots_async(session, account):
async def test_get_balance_snapshots_async(session: Session, account: Account):
await account.a_get_balance_snapshots(session)


async def test_get_positions_async(session, account):
async def test_get_positions_async(session: Session, account: Account):
await account.a_get_positions(session)


async def test_get_history_async(session, account):
async def test_get_history_async(session: Session, account: Account):
await account.a_get_history(session, page_offset=0)


async def test_get_total_fees_async(session, account):
async def test_get_total_fees_async(session: Session, account: Account):
await account.a_get_total_fees(session)


async def test_get_position_limit_async(session, account):
async def test_get_position_limit_async(session: Session, account: Account):
await account.a_get_position_limit(session)


async def test_get_margin_requirements_async(session, account):
async def test_get_margin_requirements_async(session: Session, account: Account):
await account.a_get_margin_requirements(session)


async def test_get_net_liquidating_value_history_async(session, account):
async def test_get_net_liquidating_value_history_async(
session: Session, account: Account
):
await account.a_get_net_liquidating_value_history(session, time_back="1y")


async def test_get_effective_margin_requirements_async(session, account):
async def test_get_effective_margin_requirements_async(
session: Session, account: Account
):
await account.a_get_effective_margin_requirements(session, "SPY")


async def test_get_order_history_async(session, account):
async def test_get_order_history_async(session: Session, account: Account):
await account.a_get_order_history(session, page_offset=0)


async def test_get_complex_order_history_async(session, account):
async def test_get_complex_order_history_async(session: Session, account: Account):
await account.a_get_complex_order_history(session, page_offset=0)


async def test_get_live_orders_async(session, account):
async def test_get_live_orders_async(session: Session, account: Account):
await account.a_get_live_orders(session)


def test_get_order_chains(session, account):
def test_get_order_chains(session: Session, account: Account):
start_time = datetime(2024, 1, 1, 0, 0, 0)
end_time = datetime.now()
account.get_order_chains(session, "F", start_time=start_time, end_time=end_time)


async def test_get_order_chains_async(session, account):
async def test_get_order_chains_async(session: Session, account: Account):
start_time = datetime(2024, 1, 1, 0, 0, 0)
end_time = datetime.now()
await account.a_get_order_chains(
Expand All @@ -164,7 +169,7 @@ async def test_get_order_chains_async(session, account):


@fixture(scope="module")
def new_order(session):
def new_order(session: Session) -> NewOrder:
symbol = Equity.get_equity(session, "F")
leg = symbol.build_leg(Decimal(1), OrderAction.BUY_TO_OPEN)
return NewOrder(
Expand All @@ -176,28 +181,32 @@ def new_order(session):


@fixture(scope="module")
def placed_order(session, account, new_order):
def placed_order(
session: Session, account: Account, new_order: NewOrder
) -> PlacedOrder:
return account.place_order(session, new_order, dry_run=False).order


def test_place_order(placed_order):
def test_place_order(placed_order: PlacedOrder):
pass


def test_get_order(session, account, placed_order):
def test_get_order(session: Session, account: Account, placed_order: PlacedOrder):
sleep(3)
assert account.get_order(session, placed_order.id).id == placed_order.id


def test_replace_and_delete_order(session, account, new_order, placed_order):
def test_replace_and_delete_order(
session: Session, account: Account, new_order: NewOrder, placed_order: PlacedOrder
):
modified_order = new_order.model_copy()
modified_order.price = Decimal("-2.01")
replaced = account.replace_order(session, placed_order.id, modified_order)
sleep(3)
account.delete_order(session, replaced.id)


def test_place_oco_order(session, account):
def test_place_oco_order(session: Session, account: Account):
# account must have a share of F for this to work
symbol = Equity.get_equity(session, "F")
closing = symbol.build_leg(Decimal(1), OrderAction.SELL_TO_CLOSE)
Expand All @@ -224,7 +233,7 @@ def test_place_oco_order(session, account):
account.delete_complex_order(session, resp2.complex_order.id)


def test_place_otoco_order(session, account):
def test_place_otoco_order(session: Session, account: Account):
symbol = Equity.get_equity(session, "AAPL")
opening = symbol.build_leg(Decimal(1), OrderAction.BUY_TO_OPEN)
closing = symbol.build_leg(Decimal(1), OrderAction.SELL_TO_CLOSE)
Expand Down Expand Up @@ -255,29 +264,36 @@ def test_place_otoco_order(session, account):
account.delete_complex_order(session, resp.complex_order.id)


def test_get_live_complex_orders(session, account):
def test_get_live_complex_orders(session: Session, account: Account):
orders = account.get_live_complex_orders(session)
assert orders != []


@fixture(scope="module")
async def placed_order_async(session, account, new_order):
async def placed_order_async(
session: Session, account: Account, new_order: NewOrder
) -> PlacedOrder:
res = await account.a_place_order(session, new_order, dry_run=False)
return res.order


async def test_place_order_async(placed_order_async):
async def test_place_order_async(placed_order_async: PlacedOrder):
pass


async def test_get_order_async(session, account, placed_order_async):
async def test_get_order_async(
session: Session, account: Account, placed_order_async: PlacedOrder
):
sleep(3)
placed = await account.a_get_order(session, placed_order_async.id)
assert placed.id == placed_order_async.id


async def test_replace_and_delete_order_async(
session, account, new_order, placed_order_async
session: Session,
account: Account,
new_order: NewOrder,
placed_order_async: PlacedOrder,
):
modified_order = new_order.model_copy()
modified_order.price = Decimal("-2.01")
Expand All @@ -288,7 +304,7 @@ async def test_replace_and_delete_order_async(
await account.a_delete_order(session, replaced.id)


async def test_place_complex_order_async(session, account):
async def test_place_complex_order_async(session: Session, account: Account):
sleep(3)
symbol = Equity.get_equity(session, "AAPL")
opening = symbol.build_leg(Decimal(1), OrderAction.BUY_TO_OPEN)
Expand Down Expand Up @@ -320,6 +336,6 @@ async def test_place_complex_order_async(session, account):
await account.a_delete_complex_order(session, resp.complex_order.id)


async def test_get_live_complex_orders_async(session, account):
async def test_get_live_complex_orders_async(session: Session, account: Account):
orders = await account.a_get_live_complex_orders(session)
assert orders != []
Loading

0 comments on commit 7928e9b

Please sign in to comment.