Paper API¶
Although Tastytrade offers a sandbox environment, it is unfortunately not very useful for testing for a few reasons:
limited symbol support (only supports a few underlyings)
no ability to control order fills
environment gets reset every 24 hours
In order to get around these limitations, tastyware provides access to a proprietary paper trading API for paid subscribers. The paper trading endpoints are designed to emulate the actual API as closely as possible to allow you to test your code in the same way you’d write production code.
If you’ve made significant contributions to the SDK, please reach out to support@tastyware.dev and ask for a free API key. If you sponsor tastyware at a level of $30/month or above, you should be able to log in and create an API key already! Otherwise, if you’ve benefitted from this project and would make good use of the paper API, please consider subscribing–it’s a great way to support the project!
Warning
At this time, the paper API only supports equities/index options trades, and complex orders have not yet been implemented. I hope to add futures options and complex orders soon.
Note
The paper API is still in beta. If you run into any issues or questions, reach out via Matrix!
Creating a tastyware API key¶
To get started, log in with GitHub and subscribe to tastyware here. Once you’ve subscribed you’ll be able to generate a new API key. Save the API key, you’ll only be able to see it once!
Tip
You can manage your subscription, regenerate your API key, or view API docs at any time here!
Creating a session¶
Now that you have your API key, you can use it to create a PaperSession:
from tastytrade.paper import PaperSession
paper = PaperSession("MY-API-KEY")
These session objects can be used to make API requests to most account-related endpoints in the same way you’d use a normal session:
from tastytrade import Account
accounts = await Account.get(paper)
Info
Paper API endpoints have a default rate limit of 5 requests/second. If you need a higher limit or have a unique use case, please reach out to support.
PaperSession contains a few helper functions for managing paper accounts:
# create a new paper account
paper_account = await paper.create_account("Paper", margin_or_cash="Cash", initial_deposit=100_000)
# deposit some cash into the account (negative numbers are withdrawals)
await paper.deposit(paper_account, Decimal(40))
# delete the account and its transactions
await paper.delete_account(paper_account)
Writing paper trading applications¶
The paper session can only be used with account-related endpoints. For non-account endpoints, just use a normal Tastytrade session:
from tastytrade import Session
from tastytrade.paper import PaperSession
session = Session("MY-SECRET", "MY-REFRESH")
paper = PaperSession("MY-API-KEY")
Here’s an example of how one might combine a normal and a paper session to simulate production code:
# here, we use a normal session for the generic option chain endpoint
chain = (await get_option_chain(session, "SPX"))[date(2026, 4, 17)]
option = next(
c
for c in chain
if c.option_type == OptionType.CALL and round(c.strike_price) == 7000
)
# here, we use a paper session to simulate placing orders with the chosen option
acc = await Account.get(paper, "TW000014")
order = NewOrder(
time_in_force=OrderTimeInForce.DAY,
order_type=OrderType.LIMIT,
legs=[option.build_leg(1, OrderAction.BUY_TO_OPEN)],
price=-Decimal("3"),
)
print(await acc.place_order(paper, order, dry_run=True))
Using the paper account streamer¶
The AlertStreamer class is very important in most production apps for tracking whether orders have been filled or not. Fortunately the paper API provides a similar streamer that can be used with minimal code changes:
from tastytrade.paper import PaperAlertStreamer
async with PaperAlertStreamer(paper) as streamer:
await streamer.subscribe_accounts([acc])
async for msg in streamer.listen(PlacedOrder):
print(msg)
Note
PaperAlertStreamer only supports listening to order notifications at this time.
Controlling order fills¶
One of the most important features that distinguishes the paper API from the official Tastytrade sandbox is the ability to control order fill behavior. This is done through FillBehavior:
from tastytrade.order import (
FillBehavior,
NewOrder,
OrderAction,
OrderTimeInForce,
OrderType,
)
order = NewOrder(
time_in_force=OrderTimeInForce.DAY,
order_type=OrderType.LIMIT,
legs=[option.build_leg(1, OrderAction.BUY_TO_OPEN)],
price=-Decimal("3"),
behavior=FillBehavior.SCHEDULED,
schedule=datetime(...),
)
By default, orders are filled after a random delay between 0 and 10 seconds. However, via FillBehavior you can configure a custom delay, a specific fill time, immediate fill/rejection of an order, or even partial fills!
The paper API works just as well outside of normal market hours, so OrderTimeInForce is just a formality.
Writing unit tests¶
The paper API makes writing unit tests for your code straightforward:
pytestmark = pytest.mark.anyio
@pytest.fixture(scope="module")
async def paper():
yield PaperSession(...)
@pytest.fixture(scope="module")
async def account(paper: PaperSession):
async with paper.temporary_account() as acc:
yield acc
async def test_place_and_cancel_order(
account: Account, paper: PaperSession, session: Session
):
chain = (await get_option_chain(session, "SPX"))[date(...)]
option = next(
c
for c in chain
if c.option_type == OptionType.CALL and round(c.strike_price) == 7000
)
order = NewOrder(
time_in_force=OrderTimeInForce.DAY,
order_type=OrderType.LIMIT,
legs=[option.build_leg(1, OrderAction.BUY_TO_OPEN)],
price=-Decimal("3"),
behavior=FillBehavior.DELAYED,
delay=5,
)
res = await account.place_order(paper, order, dry_run=False)
await account.delete_order(paper, res.order.id)
Here, we use the temporary_account context manager to create an account, which can then be used to run tests and finally gets cleaned up–nifty!