Skip to content

Commit

Permalink
docs-update
Browse files Browse the repository at this point in the history
  • Loading branch information
Graeme22 committed Feb 7, 2024
1 parent 768c40a commit 737096f
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 40 deletions.
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!
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

0 comments on commit 737096f

Please sign in to comment.