boyan-soubachov / tastyworks_api

An unofficial, reverse-engineered Python API for tastyworks.
Apache License 2.0
208 stars 79 forks source link

New module that includes all the low level TW API features to unify into one location #77

Open perigvennetier opened 3 years ago

perigvennetier commented 3 years ago

@boyan-soubachov, @bandwiches, I hope you guys like this.

Going through the current base code I noticed that the calls to the TW API seems to be spread out throughout the entire code base making it a bit hard to find out what features were already available and which were not, what URLs were used, what methods to communicate to the API were used, etc. and would make it really hard to update if there was changes made in the server side API and if/when they finally release their API officially.

So I decided to create a new base layer module that includes all the low level TW API URLs/features/methods currently available via the server API. This low level module does not depend on any other created module and so it is self sustaining. In order to achieve that I decided to only pass the session token to the included methods and not an entire session object. I don't believe there is a real downside to doing this and it avoids having to pass username and password around.

I also created a new example_api file that runs through all the API calls. The example file does not use any of the classes currently implemented to keep it as low level as possible with no application specific import requirements.

WARNING

The example will route a real order, modify it and then cancel it. It will not be filled as I picked a price that would not fill but it will show in your account.

Requirement

This new example relies on an additional system environment variable TW_ACCOUNT in order to be able to run without having to fetch and pick the right account and modify the example code. As long as the 'TW_ACCOUNT' variable match a valid account number, the example will run. The call [environ.get('TW_ACCOUNT', "")] can also be manually be replaced with hardcoded account number as a string in the example_api script to run.

The new module includes

Missing

Features implemented

  1. Start a session [session_start()]
  2. Validate a session [session_validate()]
  3. Get the steamer information [get_streamer_info()]
  4. Get all the accounts [get_accounts()]
  5. Get the balances for an account [get_balances()]
  6. Get all the positions for an account [get_positions()]
  7. Get the status info for an account [get_status()]
  8. Get the capital requirement for an account, including lots of details on open positions [get_capital_req()]
  9. Get all the transactions with pagination support [get_transactions()]
  10. Get all the orders with pagination support [get_orders()]
  11. Get all the 'live' order [get_orders_live()]
  12. Order (new and update) routing dry-run and live [route_order()]
  13. Order cancel [cancel_order()]
  14. Order statistical analysis probability of profit [fifty_percent_pop()]
  15. Symbol search for equities, futures, small exchange, support symbols lists for bulk requests [symbol_search()]
  16. Get market metrics, i.e. detailed info about symbols, supports symbols lists for bulk requests [get_market_metrics()]
  17. Get for option chains for equities and futures [get_options_chain()]
  18. Get all the account alerts [get_quote_alert()]
  19. Create a new quote alert [create_quote_alert()]
  20. Delete a quote alert [delete_quote_alert()]
  21. Get user defined watchlists [get_watchlists()]
  22. Create new watchlist [create_watchlist()]
  23. Update watchlist [update_watchlist()]
  24. Delete watchlist [delete_watchlist()]
  25. Get the public watchlists [get_watchlists_public()]
  26. Get user account journal entries [get_journal_entries()]
  27. Add a journal entry [add_journal_entry()]
  28. Update a journal entry [update_journal_entry()]
  29. Delete a journal entry [delete_journal_entry()]
  30. Get all available futures products [get_instruments_futures()]
  31. Get instruments data precisions [get_instruments_precisions()]

Problem addressed

The calls to the TW API seems to be spread out throughout the entire code base making it a bit hard to find out what features were already available and which were not and would make it hard to update there was changes made in the server side API and if/when they finally release their API officially.

Solution

New low level module.

Checklist

perigvennetier commented 3 years ago

I was missing the listing/creation/deletion of alerts so I added that as well.

perigvennetier commented 3 years ago

I also just added the missing capital/margin requirement API call and example

perigvennetier commented 3 years ago

Thanks for all the comments. I hear all your comments about things being already in the classes we have.

I think there may be some confusion about what my intent for this was.

My idea was actually the other way around where the modules would be updated to pull data from this, even the session module/class because this would be the ground truth for all low level API calls so api low level interface is not spread around in all the classes. Easier for maintenance and future proof.

bandwiches commented 3 years ago

That's fair. I think the argument could also be made that the models themselves are API endpoints.

perigvennetier commented 3 years ago

That's fair. I think the argument could also be made that the models themselves are API endpoints.

I absolutely agree and I think we're seeing two overall implementation architectures.

I still feel have all the API requests being all spread out and left to be discovered in the various classes make it a bit hard to use. It definitely took me quite a bit to figure how all the piece are fitting together as it is.

I still strongly feel that having one module working as an abstraction layer between the TW API and the classes that handles all the API interaction is a lot cleaner and easier to pick up to add new classes and methods. If this is in place, to create a new "Stock" or "Journal" or "Alert" class would be super simple because all the API calls are already handled and raw data is returned.

This layer, providing (almost) no filtering from the data returned by the API leaves all the flexibility to the classes to have more diverse and simple methods to apply to the returned raw data. All the formatting and filtering of the data and more useful features of classes and method would still belong to the classes themselves.

PS: It seems what I am proposing here is quite similar to how the streamer is handled where all the interfaces and calls to the streamer are in the DataStreamer class.