An unofficial, reverse-engineered Python API for Tastyworks.

Overview

Tastyworks API (Unofficial)

A simple, async-based, reverse-engineered API for tastyworks. This will allow you to create trading algorithms for whatever strategies you may have.

Please note that this is in the very early stages of development so any and all contributions are welcome. Please submit an issue and/or a pull request.

This is a fork with modified and added features. You can find the original (unmaintained) GitHub repo at: https://github.com/boyan-soubachov/tastyworks_api

Installation

$ pip install tastyworks-api

Usage

Here's a simple example which showcases many different aspects of the API:

import asyncio
from datetime import date
from decimal import Decimal as D

from tastyworks.models import option_chain, underlying
from tastyworks.models.greeks import Greeks
from tastyworks.models.option import Option, OptionType
from tastyworks.models.order import (Order, OrderDetails, OrderPriceEffect,
                                     OrderType)
from tastyworks.models.session import TastyAPISession
from tastyworks.models.trading_account import TradingAccount
from tastyworks.models.underlying import UnderlyingType
from tastyworks.streamer import DataStreamer
from tastyworks.tastyworks_api import tasty_session
from tastyworks.utils import get_third_friday


async def main_loop(session: TastyAPISession, streamer: DataStreamer):
    accounts = await TradingAccount.get_remote_accounts(session)
    acct = accounts[0]
    print(f'Accounts available: {accounts}')

    orders = await Order.get_remote_orders(session, acct)
    print(f'Number of active orders: {len(orders)}')

    # Execute an order
    details = OrderDetails(
        type=OrderType.LIMIT,
        price=D(400),
        price_effect=OrderPriceEffect.CREDIT)
    new_order = Order(details)

    opt = Option(
        ticker='AKS',
        quantity=1,
        expiry=get_third_friday(date.today()),
        strike=D(3),
        option_type=OptionType.CALL,
        underlying_type=UnderlyingType.EQUITY
    )
    new_order.add_leg(opt)

    res = await acct.execute_order(new_order, session, dry_run=True)
    print(f'Order executed successfully: {res}')

    # Get an options chain
    undl = underlying.Underlying('AKS')

    chain = await option_chain.get_option_chain(session, undl)
    print(f'Chain strikes: {chain.get_all_strikes()}')

    # Get all expirations for the options for the above equity symbol
    exp = chain.get_all_expirations()

    # Choose the next expiration as an example & fetch the entire options chain for that expiration (all strikes)
    next_exp = exp[0]
    chain_next_exp = await option_chain.get_option_chain(session, undl, next_exp)
    options = []
    for option in chain_next_exp.options:
        options.append(option)

    # Get the greeks data for all option symbols via the streamer by subscribing
    options_symbols = [options[x].symbol_dxf for x in range(len(options))]
    greeks_data = await streamer.stream('Greeks', options_symbols)

    for data in greeks_data:
        gd = Greeks().from_streamer_dict(data)
        # gd = Greeks(kwargs=data)
        idx_match = [options[x].symbol_dxf for x in range(len(options))].index(gd.symbol)
        options[idx_match].greeks = gd
        print('> Symbol: {}\tPrice: {}\tDelta {}'.format(gd.symbol, gd.price, gd.delta))


	quote = await streamer.stream('Quote', sub_values)
    print(f'Received item: {quote}')

	await streamer.close()


if __name__ == '__main__':
    tasty_client = tasty_session.create_new_session('foo', 'bar')
    streamer = DataStreamer(tasty_client)
    loop = asyncio.get_event_loop()

    try:
        loop.run_until_complete(main_loop(tasty_client, streamer))
    except Exception:
        print('Exception in main loop')
    finally:
        # find all futures/tasks still running and wait for them to finish
        pending_tasks = [
            task for task in asyncio.Task.all_tasks() if not task.done()
        ]
        loop.run_until_complete(asyncio.gather(*pending_tasks))
        loop.close()

Guidelines and caveats

There are a few useful things to know which will help you get the most out of this API and use it in the way it was intended.

  1. All objects are designed to be independent of each other in their steady-state. That is, unless executing an action, all objects are not bound to each other and have no knowledge of each other's awareness.
  2. One can have multiple sessions and, due to the inter-object independence, can execute identical actions on identical objects in different sessions.
  3. Given the above points, this API does not implement state management and synchronization (i.e. are my local object representations identical to the remote [Tastyworks] ones?). This is not an indefinitely closed matter and may be re-evaluated if the need arises.

Disclaimer

This is an unofficial, reverse-engineered API for Tastyworks. There is no implied warranty for any actions and results which arise from using it. The only guarantee I make is that you will lose all your money if you use this API.

Comments
  • asyncio compatibility issue?

    asyncio compatibility issue?

    I'm trying to run the demo/example provided with the tasyworks-api package

    'Main' keeps throwing an exception:

    task for task in asyncio.Task.all_tasks() if not task.done() AttributeError: type object '_asyncio.Task' has no attribute 'all_tasks'

    opened by ReneJ22 5
  • Feature/fix python

    Feature/fix python

    • depends on https://github.com/tastyware/tastyworks-api/pull/6
    • fixes the asyncio loop
    • I don't know much about asyncio
    • I changed the ticker so that the loop progresses further

    I get the following output now which is much better than before:

    Accounts available: [TradingAccount(account_number=xxxxx]
    Number of active orders: 1
    Order executed successfully: {'order': {'account-number': 'xxx', 'time-in-force': 'Day', 'order-type': 'Limit', 'size': 1, 'underlying-symbol': 'SPY', 'underlying-instrument-type': 'Equity', 'price': '400.0', 'price-effect': 'Credit', 'status': 'Received', 'cancellable': True, 'editable': True, 'edited': False, 'updated-at': 0, 'legs': [{'instrument-type': 'Equity Option', 'symbol': 'SPY   221216C00400000', 'quantity': 1, 'remaining-quantity': 1, 'action': 'Sell to Open', 'fills': []}]}, 'warnings': [{'code': 'tif_next_valid_sesssion', 'message': 'Your order will begin working during next valid session.'}], 'buying-power-effect': {'change-in-margin-requirement': '8294.6', 'change-in-margin-requirement-effect': 'Debit', 'change-in-buying-power': '31704.25782', 'change-in-buying-power-effect': 'Credit', 'current-buying-power': '4701.027', 'current-buying-power-effect': 'Credit', 'new-buying-power': '36405.28482', 'new-buying-power-effect': 'Credit', 'isolated-order-margin-requirement': '8294.6', 'isolated-order-margin-requirement-effect': 'Debit', 'is-spread': False, 'impact': '31704.25782', 'effect': 'Credit'}, 'fee-calculation': {'regulatory-fees': '0.04218', 'regulatory-fees-effect': 'Debit', 'clearing-fees': '0.1', 'clearing-fees-effect': 'Debit', 'commission': '1.0', 'commission-effect': 'Debit', 'proprietary-index-option-fees': '0.0', 'proprietary-index-option-fees-effect': 'None', 'total-fees': '1.14218', 'total-fees-effect': 'Debit'}}
    Chain strikes: [Decimal('85.0'), Decimal('90.0'), Decimal('95.0'), Decimal('100.0'), Decimal('105.0'), Decimal('110.0'), Decimal('115.0'), Decimal('120.0'), Decimal('125.0'), Decimal('130.0'), Decimal('135.0'), Decimal('140.0'), Decimal('145.0'), Decimal('150.0'), Decimal('155.0'), Decimal('160.0'), Decimal('165.0'), Decimal('170.0'), Decimal('175.0'), Decimal('180.0'), Decimal('185.0'), Decimal('190.0'), Decimal('195.0'), Decimal('200.0'), Decimal('205.0'), Decimal('210.0'), Decimal('215.0'), Decimal('220.0'), Decimal('225.0'), Decimal('230.0'), Decimal('235.0'), Decimal('240.0'), Decimal('245.0'), Decimal('250.0'), Decimal('255.0'), Decimal('260.0'), Decimal('265.0'), Decimal('270.0'), Decimal('275.0'), Decimal('280.0'), Decimal('285.0'), Decimal('290.0'), Decimal('295.0'), Decimal('300.0'), Decimal('301.0'), Decimal('302.0'), Decimal('303.0'), Decimal('304.0'), Decimal('305.0'), Decimal('306.0'), Decimal('307.0'), Decimal('308.0'), Decimal('309.0'), Decimal('310.0'), Decimal('311.0'), Decimal('312.0'), Decimal('313.0'), Decimal('314.0'), Decimal('315.0'), Decimal('316.0'), Decimal('317.0'), Decimal('318.0'), Decimal('319.0'), Decimal('320.0'), Decimal('321.0'), Decimal('322.0'), Decimal('323.0'), Decimal('324.0'), Decimal('325.0'), Decimal('326.0'), Decimal('327.0'), Decimal('328.0'), Decimal('329.0'), Decimal('330.0'), Decimal('331.0'), Decimal('332.0'), Decimal('333.0'), Decimal('334.0'), Decimal('335.0'), Decimal('336.0'), Decimal('337.0'), Decimal('338.0'), Decimal('339.0'), Decimal('340.0'), Decimal('341.0'), Decimal('342.0'), Decimal('343.0'), Decimal('344.0'), Decimal('345.0'), Decimal('346.0'), Decimal('347.0'), Decimal('347.5'), Decimal('348.0'), Decimal('349.0'), Decimal('350.0'), Decimal('351.0'), Decimal('352.0'), Decimal('352.5'), Decimal('353.0'), Decimal('354.0'), Decimal('355.0'), Decimal('356.0'), Decimal('357.0'), Decimal('357.5'), Decimal('358.0'), Decimal('359.0'), Decimal('360.0'), Decimal('361.0'), Decimal('362.0'), Decimal('362.5'), Decimal('363.0'), Decimal('364.0'), Decimal('365.0'), Decimal('366.0'), Decimal('367.0'), Decimal('367.5'), Decimal('368.0'), Decimal('369.0'), Decimal('370.0'), Decimal('371.0'), Decimal('372.0'), Decimal('372.5'), Decimal('373.0'), Decimal('374.0'), Decimal('375.0'), Decimal('376.0'), Decimal('377.0'), Decimal('377.5'), Decimal('378.0'), Decimal('379.0'), Decimal('380.0'), Decimal('381.0'), Decimal('382.0'), Decimal('382.5'), Decimal('383.0'), Decimal('384.0'), Decimal('385.0'), Decimal('386.0'), Decimal('387.0'), Decimal('387.5'), Decimal('388.0'), Decimal('389.0'), Decimal('390.0'), Decimal('391.0'), Decimal('392.0'), Decimal('392.5'), Decimal('393.0'), Decimal('394.0'), Decimal('395.0'), Decimal('396.0'), Decimal('397.0'), Decimal('397.5'), Decimal('398.0'), Decimal('399.0'), Decimal('400.0'), Decimal('401.0'), Decimal('402.0'), Decimal('403.0'), Decimal('404.0'), Decimal('405.0'), Decimal('406.0'), Decimal('407.0'), Decimal('408.0'), Decimal('409.0'), Decimal('410.0'), Decimal('411.0'), Decimal('412.0'), Decimal('413.0'), Decimal('414.0'), Decimal('415.0'), Decimal('416.0'), Decimal('417.0'), Decimal('418.0'), Decimal('419.0'), Decimal('420.0'), Decimal('421.0'), Decimal('422.0'), Decimal('423.0'), Decimal('424.0'), Decimal('425.0'), Decimal('426.0'), Decimal('427.0'), Decimal('428.0'), Decimal('429.0'), Decimal('430.0'), Decimal('431.0'), Decimal('432.0'), Decimal('433.0'), Decimal('434.0'), Decimal('435.0'), Decimal('436.0'), Decimal('437.0'), Decimal('438.0'), Decimal('439.0'), Decimal('440.0'), Decimal('441.0'), Decimal('442.0'), Decimal('443.0'), Decimal('444.0'), Decimal('445.0'), Decimal('446.0'), Decimal('447.0'), Decimal('448.0'), Decimal('449.0'), Decimal('450.0'), Decimal('451.0'), Decimal('452.0'), Decimal('453.0'), Decimal('454.0'), Decimal('455.0'), Decimal('456.0'), Decimal('457.0'), Decimal('458.0'), Decimal('459.0'), Decimal('460.0'), Decimal('461.0'), Decimal('462.0'), Decimal('463.0'), Decimal('464.0'), Decimal('465.0'), Decimal('466.0'), Decimal('467.0'), Decimal('468.0'), Decimal('469.0'), Decimal('470.0'), Decimal('471.0'), Decimal('472.0'), Decimal('473.0'), Decimal('474.0'), Decimal('475.0'), Decimal('476.0'), Decimal('477.0'), Decimal('478.0'), Decimal('479.0'), Decimal('480.0'), Decimal('481.0'), Decimal('482.0'), Decimal('483.0'), Decimal('484.0'), Decimal('485.0'), Decimal('486.0'), Decimal('487.0'), Decimal('488.0'), Decimal('489.0'), Decimal('490.0'), Decimal('491.0'), Decimal('492.0'), Decimal('493.0'), Decimal('494.0'), Decimal('495.0'), Decimal('496.0'), Decimal('497.0'), Decimal('498.0'), Decimal('499.0'), Decimal('500.0'), Decimal('501.0'), Decimal('502.0'), Decimal('503.0'), Decimal('504.0'), Decimal('505.0'), Decimal('506.0'), Decimal('507.0'), Decimal('508.0'), Decimal('509.0'), Decimal('510.0'), Decimal('511.0'), Decimal('512.0'), Decimal('513.0'), Decimal('514.0'), Decimal('515.0'), Decimal('516.0'), Decimal('517.0'), Decimal('518.0'), Decimal('519.0'), Decimal('520.0'), Decimal('521.0'), Decimal('522.0'), Decimal('523.0'), Decimal('525.0'), Decimal('530.0'), Decimal('535.0'), Decimal('540.0'), Decimal('545.0'), Decimal('550.0'), Decimal('555.0'), Decimal('560.0'), Decimal('565.0'), Decimal('570.0'), Decimal('575.0'), Decimal('580.0'), Decimal('585.0'), Decimal('590.0'), Decimal('595.0'), Decimal('600.0'), Decimal('605.0'), Decimal('610.0'), Decimal('615.0'), Decimal('620.0'), Decimal('625.0'), Decimal('630.0'), Decimal('635.0'), Decimal('640.0'), Decimal('645.0'), Decimal('650.0'), Decimal('655.0'), Decimal('660.0'), Decimal('665.0'), Decimal('670.0'), Decimal('675.0'), Decimal('680.0'), Decimal('685.0'), Decimal('690.0'), Decimal('695.0'), Decimal('700.0'), Decimal('705.0'), Decimal('710.0'), Decimal('715.0'), Decimal('720.0')]
    Traceback (most recent call last):
      File "/Users/avion/Documents/programming/trading/tasty/tastyworks/docs/example.py", line 87, in <module>
        asyncio.run(main_loop(tasty_client, streamer))
      File "/opt/homebrew/Cellar/[email protected]/3.10.8/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/runners.py", line 44, in run
        return loop.run_until_complete(main)
      File "/opt/homebrew/Cellar/[email protected]/3.10.8/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
        return future.result()
      File "/Users/avion/Documents/programming/trading/tasty/tastyworks/docs/example.py", line 65, in main_loop
        greeks_data = await streamer.stream('Greeks', options_symbols)
      File "/opt/homebrew/lib/python3.10/site-packages/tastyworks/streamer.py", line 107, in stream
        await self.add_data_sub(streamer_dict)
      File "/opt/homebrew/lib/python3.10/site-packages/tastyworks/streamer.py", line 32, in add_data_sub
        await self._send_msg(dxfeed.SUBSCRIPTION_CHANNEL, {'add': values})
      File "/opt/homebrew/lib/python3.10/site-packages/tastyworks/streamer.py", line 43, in _send_msg
        if not self.logged_in:
    AttributeError: 'DataStreamer' object has no attribute 'logged_in'
    
    opened by avion23 1
  • Preparation for future commits

    Preparation for future commits

    The example in the readme is broken. I'm trying to fix it. To be able to execute and debug the program it needs to be in a *.py version.

    This PR just copy-pastes the example and injects username and password through the environment. It's useful to do so to avoid accidentally leaking your credentials (meaning mine ;) when committing

    opened by avion23 1
  • update to use fixed aiocometd

    update to use fixed aiocometd

    Problem addressed

    aiocometd doesn't handle certain errors, making it impossible to close a DataStreamer without throwing an unhandleable error. Updating to my fork of aiocometd fixes this, as the library is no longer maintained.

    Solution

    Version of aiocometd is now a tag of my fork.

    Checklist

    • PR commits have been squashed
    • All tests pass
    opened by Graeme22 0
  • Readme.md example code is not executable

    Readme.md example code is not executable

    Readme.md code is not executed with several errors

    • First problem: indentation is incorrect: TabError: inconsistent use of tabs and spaces in indentation
    • You can even see this in github, adding print(f'Received item: {quote}') broke the code
    • Second problem: the code is a mix of old and new, caused by a refactoring in c7d2d3b6
    • In the current version sub_values does not exist, but it is used
    • A comparison with c7d2d3b6 shows me that there is a lot of refactoring.

    Is it possible to either revert this change or insert a working version?

    opened by avion23 4
Releases(4.3.0)
Owner
tastyware
A collection of open-source tools for Tastyworks traders!
tastyware
A Python API-Wrapper for the unofficial wikifolio API

wikifolio-api A Python API-Wrapper for the unofficial wikifolio API Usage Clone this repo Create a new file inside it with the following content (the

henrydatei 6 Dec 29, 2022
Build API with ASGI in AWS Lambda with API Gateway HTTP API or REST API, or with Function URL ✨

asgi-aws Build API with ASGI in AWS Lambda with API Gateway HTTP API or REST API, or with Function URL ✨ Installation pip install asgi_aws Example Cre

Yasser Tahiri 20 Sep 23, 2022
An unofficial MyKU API wrapper and utilities for python.

PyMyKU An unofficial MyKU API wrapper and utilities for python. Table of Contents PyMyKU Table of Contents Notices Documentation Examples Installation

Phusit Somboonyingsuk 5 Dec 17, 2022
An Unofficial Python API Library to Download Manga for FREE...

KissManngaApi An Unofficial Python API Library to Download Manga for FREE... Installation $ pip install kissmanga Important Usage 1. Importing the Lib

Meta Void 3 Dec 15, 2022
Unofficial Python SDK for Chapa API. https://developer.chapa.co/docs

Chapa Unofficial Python SDK for Chapa API. Instructions This is a Python SDK for Chapa API. It is not official and is not supported by Chapa. It is pr

Temkin Mengsitu 7 Dec 28, 2022
instagrapi - Unofficial Instagram API 2022 for Python

Fast and effective Instagram Private API wrapper (public+private requests and challenge resolver) without selenium

Mikhail Andreev 1.7k Dec 31, 2022
WeTransfer unofficial API wrapper in python for anonymous upload and download of files and folders.

WeTransferTool Anonymously upload and download files/folders from wetransfer.com You can upload a file/folder of max 2GB. example usage: from WE impor

bunny 8 Oct 18, 2022
An Unofficial Python version of Mdisk API wrapper. Used to convert and rename Mdisk Files.

Mdisky An Unofficial Python version of Mdisk API wrapper · Report Bug · Usage · Reference Mdisky An Unofficial Python version of Mdisk API wrapper. Us

Kevin 10 Oct 26, 2022
Unofficial API service for pepper.pl, based on FastAPI, BeautifulSoup and Selenium Python libraries

Pepper.pl unofficial API service This is a simple API service for the Pepper.pl website, based on page data scraping. It is not affiliated with the we

Cyprian 3 Sep 19, 2022
The unofficial Python client for communicating with the Kite Connect API.

kiteconnect Unofficial Python client for communicating with the Kite Connect API. Kite Connect is a set of REST-like APIs that expose many capabilitie

Nasir 5 Nov 16, 2022
unofficial linkedin api for finding jobs and searching for/connecting with users

LinkedIn REST Unofficial API (python) A simple unofficial LinkedIn python api providing basic LinkedIn functionality. Programmatically find jobs and s

alex 2 Aug 1, 2022
Snapchat Friend parser / (future) Unofficial API

A Snapchat Friend parser / (future) API Prerequisites Access to https://web.snapchat.com/ Python3 Download prerequisites: Python3 requests pillow OR P

Quintin Dunn 1 Dec 11, 2022
An unofficial api client for pulling player Stats, Fixtures, Tables and Results data from the Premier League

Premier League API An unofficial api client for pulling Player Stats, Fixtures, Results, Tables from the Premier League website Setup Details Follow t

Tarun 48 Dec 15, 2022
Unofficial Fastapi implementation Stable-Diffusion API

Unofficial Fastapi implementation Stable-Diffusion API UNOFFICIAL, Stable-Diffusion api using FastAPI Samples TODO Requirements API /stable-diffusion

yslee 28 Dec 25, 2022
An unofficial API client to search players using the player's reference image and get the player Stats data from the Premier League

Premier-League-Face-Recognition-API An unofficial API client to search players using the player reference image and get the player Stats data from the

Tarun 1 Oct 9, 2022
udemyAPI is an unofficial API for Udemy, developed for udemy teachers. It helps you to monitor your course. You can monitor the questions in your course with this library.

Important Note This API is unofficial, fully open source and in Beta version. If you have any questions/troubles, you can use Issues to let me know. A

Aziz Kaplan 1 Oct 16, 2022
Unofficial API for hordes.io

Hordes.io-API Unofficial API for hordes.io Player Stats import hordesAPI player = hordesAPI.Player("danziel") # initialize the player player.Name()

Daniel 1 Oct 23, 2022
Simple reverse shell written in Python.

reverse-shell This is a reverse shell written in Python used to to target a machine and gain remote shell access. It consists of two distinct scripts:

Paulo Medeiros 3 Jun 21, 2022
My first project in python is a ReverseShellGenerator which can url encode the reverse shell and setup a port listener.

ReverseShellGenerator Description My first project in python is a simple program which can generate Reverse shell, url encode the reverse shell and se

Memoriz 1 Apr 28, 2022