Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs update #125

Merged
merged 2 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ The streamer is a websocket connection to dxfeed (the Tastytrade data provider)

>>> [Quote(eventSymbol='SPY', eventTime=0, sequence=0, timeNanoPart=0, bidTime=0, bidExchangeCode='Q', bidPrice=411.58, bidSize=400.0, askTime=0, askExchangeCode='Q', askPrice=411.6, askSize=1313.0), Quote(eventSymbol='SPX', eventTime=0, sequence=0, timeNanoPart=0, bidTime=0, bidExchangeCode='\x00', bidPrice=4122.49, bidSize='NaN', askTime=0, askExchangeCode='\x00', askPrice=4123.65, askSize='NaN')]

Note that this is asynchronous code, so you can't run it as is unless you're using a Jupyter notebook or something similar.

Getting current positions
-------------------------

Expand All @@ -63,7 +65,7 @@ Getting current positions
positions = account.get_positions(session)
print(positions[0])

>>> CurrentPosition(account_number='5WV69754', symbol='IAU', instrument_type=<InstrumentType.EQUITY: 'Equity'>, underlying_symbol='IAU', quantity=Decimal('20'), quantity_direction='Long', close_price=Decimal('37.09'), average_open_price=Decimal('37.51'), average_yearly_market_close_price=Decimal('37.51'), average_daily_market_close_price=Decimal('37.51'), multiplier=1, cost_effect='Credit', is_suppressed=False, is_frozen=False, realized_day_gain=Decimal('7.888'), realized_day_gain_effect='Credit', realized_day_gain_date=datetime.date(2023, 5, 19), realized_today=Decimal('0.512'), realized_today_effect='Debit', realized_today_date=datetime.date(2023, 5, 19), created_at=datetime.datetime(2023, 3, 31, 14, 38, 32, 58000, tzinfo=datetime.timezone.utc), updated_at=datetime.datetime(2023, 5, 19, 16, 56, 51, 920000, tzinfo=datetime.timezone.utc), mark=None, mark_price=None, restricted_quantity=Decimal('0'), expires_at=None, fixing_price=None, deliverable_type=None)
>>> CurrentPosition(account_number='5WX01234', symbol='IAU', instrument_type=<InstrumentType.EQUITY: 'Equity'>, underlying_symbol='IAU', quantity=Decimal('20'), quantity_direction='Long', close_price=Decimal('37.09'), average_open_price=Decimal('37.51'), average_yearly_market_close_price=Decimal('37.51'), average_daily_market_close_price=Decimal('37.51'), multiplier=1, cost_effect='Credit', is_suppressed=False, is_frozen=False, realized_day_gain=Decimal('7.888'), realized_day_gain_effect='Credit', realized_day_gain_date=datetime.date(2023, 5, 19), realized_today=Decimal('0.512'), realized_today_effect='Debit', realized_today_date=datetime.date(2023, 5, 19), created_at=datetime.datetime(2023, 3, 31, 14, 38, 32, 58000, tzinfo=datetime.timezone.utc), updated_at=datetime.datetime(2023, 5, 19, 16, 56, 51, 920000, tzinfo=datetime.timezone.utc), mark=None, mark_price=None, restricted_quantity=Decimal('0'), expires_at=None, fixing_price=None, deliverable_type=None)

Placing an order
----------------
Expand All @@ -75,15 +77,15 @@ Placing an order
from tastytrade.instruments import Equity
from tastytrade.order import NewOrder, OrderAction, OrderTimeInForce, OrderType, PriceEffect

account = Account.get_account(session, '5WV69754')
account = Account.get_account(session, '5WX01234')
symbol = Equity.get_equity(session, 'USO')
leg = symbol.build_leg(Decimal('5'), OrderAction.BUY_TO_OPEN) # buy to open 5 shares

order = NewOrder(
time_in_force=OrderTimeInForce.DAY,
order_type=OrderType.LIMIT,
legs=[leg], # you can have multiple legs in an order
price=Decimal('50'), # limit price, here $50 for 5 shares = $10/share
price=Decimal('10'), # limit price, $10/share for a total value of $50
price_effect=PriceEffect.DEBIT
)
response = account.place_order(session, order, dry_run=True) # a test order
Expand All @@ -96,15 +98,18 @@ Options chain/streaming greeks

.. code-block:: python

from tastytrade import DXLinkStreamer
from tastytrade.instruments import get_option_chain
from datetime import date
from tastytrade.utils import get_tasty_monthly

chain = get_option_chain(session, 'SPLG')
subs_list = [chain[date(2023, 6, 16)][0].streamer_symbol]
exp = get_tasty_monthly() # 45 DTE expiration!
subs_list = [chain[exp][0].streamer_symbol]

await streamer.subscribe(EventType.GREEKS, subs_list)
greeks = await streamer.get_event(EventType.GREEKS)
print(greeks)
async with DXLinkStreamer(session) as streamer:
await streamer.subscribe(EventType.GREEKS, subs_list)
greeks = await streamer.get_event(EventType.GREEKS)
print(greeks)

>>> [Greeks(eventSymbol='.SPLG230616C23', eventTime=0, eventFlags=0, index=7235129486797176832, time=1684559855338, sequence=0, price=26.3380972233688, volatility=0.396983376650804, delta=0.999999999996191, gamma=4.81989763184255e-12, theta=-2.5212017514875e-12, rho=0.01834504287973133, vega=3.7003015672215e-12)]

Expand Down
15 changes: 15 additions & 0 deletions docs/account-streamer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,18 @@ Here's an example of setting up an account streamer to continuously wait for eve

async for data in streamer.listen():
print(data)

Probably the most important information the account streamer handles is order fills. We can listen just for orders like so:

.. code-block:: python

from tastytrade.order import PlacedOrder

async def listen_for_orders(session):
async with AccountStreamer(session) as streamer:
accounts = Account.get_accounts(session)
await streamer.subscribe_accounts(accounts)

async for data in streamer.listen():
if isinstance(data, PlacedOrder):
yield return data
29 changes: 23 additions & 6 deletions docs/accounts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,28 @@ To obtain information about current positions:

>>> CurrentPosition(account_number='5WX01234', symbol='BRK/B', instrument_type=<InstrumentType.EQUITY: 'Equity'>, underlying_symbol='BRK/B', quantity=Decimal('10'), quantity_direction='Long', close_price=Decimal('361.34'), average_open_price=Decimal('339.63'), multiplier=1, cost_effect='Credit', is_suppressed=False, is_frozen=False, realized_day_gain=Decimal('18.5'), realized_today=Decimal('279.15'), created_at=datetime.datetime(2023, 3, 31, 14, 35, 40, 138000, tzinfo=datetime.timezone.utc), updated_at=datetime.datetime(2023, 8, 10, 15, 42, 7, 482000, tzinfo=datetime.timezone.utc), mark=None, mark_price=None, restricted_quantity=Decimal('0'), expires_at=None, fixing_price=None, deliverable_type=None, average_yearly_market_close_price=Decimal('339.63'), average_daily_market_close_price=Decimal('361.34'), realized_day_gain_effect=<PriceEffect.CREDIT: 'Credit'>, realized_day_gain_date=datetime.date(2023, 8, 10), realized_today_effect=<PriceEffect.CREDIT: 'Credit'>, realized_today_date=datetime.date(2023, 8, 10))

TODO:
get_history
get_net_liquidating_value_history
get_live_orders
delete_order(and place and replace)
get_order_history
To fetch a list of past transactions:

.. code-block:: python

history = account.get_history(session, start_date=date(2024, 1, 1))
print(history[-1])

>>> Transaction(id=280070508, account_number='5WX01234', transaction_type='Trade', transaction_sub_type='Sell to Close', description='Sold 10 BRK/B @ 384.04', executed_at=datetime.datetime(2024, 1, 26, 15, 51, 53, 685000, tzinfo=datetime.timezone.utc), transaction_date=datetime.date(2024, 1, 26), value=Decimal('3840.4'), value_effect=<PriceEffect.CREDIT: 'Credit'>, net_value=Decimal('3840.35'), net_value_effect=<PriceEffect.CREDIT: 'Credit'>, is_estimated_fee=True, symbol='BRK/B', instrument_type=<InstrumentType.EQUITY: 'Equity'>, underlying_symbol='BRK/B', action='Sell to Close', quantity=Decimal('10.0'), price=Decimal('384.04'), regulatory_fees=Decimal('0.042'), regulatory_fees_effect=<PriceEffect.DEBIT: 'Debit'>, clearing_fees=Decimal('0.008'), clearing_fees_effect=<PriceEffect.DEBIT: 'Debit'>, commission=Decimal('0.0'), commission_effect=<PriceEffect.NONE: 'None'>, proprietary_index_option_fees=Decimal('0.0'), proprietary_index_option_fees_effect=<PriceEffect.NONE: 'None'>, ext_exchange_order_number='12271026815307', ext_global_order_number=2857, ext_group_id='0', ext_group_fill_id='0', ext_exec_id='0', exec_id='123_40126000126350300000', exchange='JNS', order_id=305250635, exchange_affiliation_identifier='', leg_count=1, destination_venue='JANE_STREET_EQUITIES_A', other_charge=None, other_charge_effect=None, other_charge_description=None, reverses_id=None, cost_basis_reconciliation_date=None, lots=None, agency_price=None, principal_price=None)

We can also view portfolio P/L over time (and even plot it!):

.. code-block:: python

import matplotlib.pyplot as plt
nl = account.get_net_liquidating_value_history(session, time_back='1m') # past 1 month
plt.plot([n.time for n in nl], [n.close for n in nl])
plt.show()

.. image:: img/netliq.png
:width: 640
:alt: P/L graph

Accounts are needed to place, replace, and delete orders. See more in :doc:`Orders <orders>`.

There are many more things you can do with an ``Account`` object--check out the SDK Reference section!
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
project = 'tastytrade'
copyright = '2023, Graeme Holliday'
author = 'Graeme Holliday'
release = '6.5'
release = '6.6'

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
Expand Down
18 changes: 10 additions & 8 deletions docs/data-streamer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,27 @@ Once you've created the streamer, you can subscribe/unsubscribe to events, like

async with DXFeedStreamer(session) as streamer:
await streamer.subscribe(EventType.QUOTE, subs_list)
quotes = []
quotes = {}
async for quote in streamer.listen(EventType.QUOTE):
quotes.append(quote)
quotes[quote.eventSymbol] = quote
if len(quotes) >= len(subs_list):
break
print(quotes)

>>> [Quote(eventSymbol='SPY', eventTime=0, sequence=0, timeNanoPart=0, bidTime=0, bidExchangeCode='Q', bidPrice=411.58, bidSize=400.0, askTime=0, askExchangeCode='Q', askPrice=411.6, askSize=1313.0), Quote(eventSymbol='SPX', eventTime=0, sequence=0, timeNanoPart=0, bidTime=0, bidExchangeCode='\x00', bidPrice=4122.49, bidSize='NaN', askTime=0, askExchangeCode='\x00', askPrice=4123.65, askSize='NaN')]
>>> {'SPY': Quote(eventSymbol='SPY', eventTime=0, sequence=0, timeNanoPart=0, bidTime=0, bidExchangeCode='Q', bidPrice=411.58, bidSize=400.0, askTime=0, askExchangeCode='Q', askPrice=411.6, askSize=1313.0), 'SPX': Quote(eventSymbol='SPX', eventTime=0, sequence=0, timeNanoPart=0, bidTime=0, bidExchangeCode='\x00', bidPrice=4122.49, bidSize='NaN', askTime=0, askExchangeCode='\x00', askPrice=4123.65, askSize='NaN')}

Note that these are ``asyncio`` calls, so you'll need to run this code asynchronously. Here's an example:

.. code-block:: python

async def main():
import asyncio
async def main(session):
async with DXLinkStreamer(session) as streamer:
await streamer.subscribe(EventType.QUOTE, subs_list)
quote = await streamer.get_event(EventType.QUOTE)
print(quote)
asyncio.run(main())

asyncio.run(main(session))

>>> [Quote(eventSymbol='SPY', eventTime=0, sequence=0, timeNanoPart=0, bidTime=0, bidExchangeCode='Q', bidPrice=411.58, bidSize=400.0, askTime=0, askExchangeCode='Q', askPrice=411.6, askSize=1313.0), Quote(eventSymbol='SPX', eventTime=0, sequence=0, timeNanoPart=0, bidTime=0, bidExchangeCode='\x00', bidPrice=4122.49, bidSize='NaN', askTime=0, askExchangeCode='\x00', askPrice=4123.65, askSize='NaN')]

Expand All @@ -60,10 +61,11 @@ We can also use the streamer to stream greeks for options symbols:
.. code-block:: python

from tastytrade.instruments import get_option_chain
from datetime import date
from tastytrade.utils import get_tasty_monthly

chain = get_option_chain(session, 'SPLG')
subs_list = [chain[date(2023, 6, 16)][0].streamer_symbol]
exp = get_tasty_monthly() # 45 DTE expiration!
subs_list = [chain[exp][0].streamer_symbol]

async with DXFeedStreamer(session) as streamer:
await streamer.subscribe(EventType.GREEKS, subs_list)
Expand Down
Binary file added docs/img/netliq.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
115 changes: 104 additions & 11 deletions docs/instruments.rst
Original file line number Diff line number Diff line change
@@ -1,14 +1,107 @@
Instruments
===========

TODO:
Cryptocurrency
Equity
Option
NestedOptionChain
get_option_chain
Future
FutureOption
NestedFutureOptionChain
Warrant
get_future_option_chain
In the Tastytrade API, an instrument is a tradeable object, such as a cryptocurrency, an option, an equity/ETF, futures, futures options, and warrants.
The SDK makes it easy to fetch, trade, and perform various other operations for these instruments.

Initialization
--------------

Instruments follow a basic schema for initialization. To create an instrument(s), use the classmethods for the desired type of instrument:

- ``Cryptocurrency.get_cryptocurrency()``, ``Cryptocurrency.get_cryptocurrencies()``
- ``Equity.get_equity()``, ``Equity.get_equities()``
- ``Future.get_future()``, ``Future.get_futures()``
- ``Option.get_option()``, ``Option.get_options()``
- ``FutureOption.get_future_option()``, ``FutureOption.get_future_options()``

These functions take the session object as the first parameter, and the symbol (or list of symbols) as the second.
Note that ETFs and indices are treated as equities for the purposes of the API.

.. code-block:: python

from tastytrade.instruments import Equity, FutureOption

equities = Equity.get_equities(session, ['SPY', 'AAPL'])
print(equities[0].is_etf, equities[0].description)
future_option = FutureOption.get_future_option(session, './GCJ4 OG4G4 240223P1915')
print(future_option.exchange)

>>> (False, 'APPLE INC')
>>> 'CME'

The different instruments have a host of properties that will be automatically populated with their associated values upon initialization. You can explore these properties in depth in the "SDK Reference" section.

Options chains
--------------

The symbol structure for options and futures options is somewhat complex, so you can use ``get_option_chain()`` and ``get_future_option_chain()`` to get the instruments for a specific underlying as explained below.

.. code-block:: python

from tastytrade.instruments import get_option_chain, get_future_option_chain
from tastytrade.utils import get_tasty_monthly

chain = get_option_chain(session, 'SPLG')
exp = get_tasty_monthly() # 45 DTE expiration!
print(chain[exp][0])
future_chain = get_future_option_chain(session, '/MCL')
print(future_chain.keys()) # print all expirations

>>> instrument_type=<InstrumentType.EQUITY_OPTION: 'Equity Option'> symbol='SPLG 240315C00024000' active=True strike_price=Decimal('24.0') root_symbol='SPLG' underlying_symbol='SPLG' expiration_date=datetime.date(2024, 3, 15) exercise_style='American' shares_per_contract=100 option_type=<OptionType.CALL: 'C'> option_chain_type='Standard' expiration_type='Regular' settlement_type='PM' stops_trading_at=datetime.datetime(2024, 3, 15, 20, 0, tzinfo=datetime.timezone.utc) market_time_instrument_collection='Equity Option' days_to_expiration=38 expires_at=datetime.datetime(2024, 3, 15, 20, 0, tzinfo=datetime.timezone.utc) is_closing_only=False listed_market=None halted_at=None old_security_number=None streamer_symbol='.SPLG240315C24'
>>> dict_keys([datetime.date(2024, 7, 17), datetime.date(2024, 6, 14), datetime.date(2024, 9, 17), datetime.date(2024, 11, 15), datetime.date(2024, 12, 16), datetime.date(2024, 2, 9), datetime.date(2024, 5, 16), datetime.date(2025, 1, 15), datetime.date(2024, 8, 15), datetime.date(2024, 2, 16), datetime.date(2024, 2, 14), datetime.date(2024, 10, 17), datetime.date(2024, 4, 17), datetime.date(2024, 3, 15)])

Alternatively, ``NestedOptionChain`` and ``NestedFutureOptionChain`` provide a structured way to fetch chain expirations and available strikes.

.. code-block:: python

from tastytrade.instruments import NestedOptionChain

chain = NestedOptionChain.get_chain(session, 'SPY')
print(chain.expirations[0].strikes[0])

>>> Strike(strike_price=Decimal('415.0'), call='SPY 240207C00415000', put='SPY 240207P00415000')

Each expiration contains a list of these strikes, which have the associated put and call symbols that can then be used to fetch option objects via ``Option.get_options()`` or converted to dxfeed symbols for use with the streamer via ``Option.occ_to_streamer_symbol()``.

Placing trades
--------------

Probably the most powerful tool available for instruments is the ``build_leg()`` function. This allows an instrument to be quickly converted into a tradeable 'leg', which by itself or together with other legs forms the basis for a trade.
This makes placing new trades across a wide variety of instruments surprisingly simple:

.. code-block:: python

from tastytrade.instruments import get_future_option_chain
from tastytrade.order import *
from datetime import date

chain = get_future_option_chain(session, '/MCL')
put = chain[date(2024, 3, 15)][286]
call = chain[date(2024, 3, 15)][187]

order = NewOrder(
time_in_force=OrderTimeInForce.DAY,
order_type=OrderType.LIMIT,
legs=[
# two parameters: quantity and order action
put.build_leg(Decimal(1), OrderAction.SELL_TO_OPEN),
call.build_leg(Decimal(1), OrderAction.SELL_TO_OPEN)
],
price=Decimal('1.25'), # price is always per quantity, not total
price_effect=PriceEffect.CREDIT
)
# assuming an initialized account
account.place_order(session, order, dry_run=False)

That's it! We just sold a micro crude oil futures strangle in a few lines of code.
Note that price is per quantity, not the price for the entire order! So if the legs looked like this:

.. code-block:: python

legs=[
put.build_leg(Decimal(2), OrderAction.SELL_TO_OPEN),
call.build_leg(Decimal(2), OrderAction.SELL_TO_OPEN)
]

the price would still be ``Decimal('1.25')``, and the total credit collected would be $2.50. This holds true for ratio spreads, so a 4:2 ratio spread should be priced as a 2:1 ratio spread.
Loading
Loading