tastyware / tastytrade

An unofficial Python SDK for Tastytrade!
MIT License
106 stars 38 forks source link

Not a bug - Question on usage - Submitting a multi-leg order #162

Open salamad opened 1 month ago

salamad commented 1 month ago

Hello, I've been trying to use your API to place options orders. However, as much as I follow the code and the examples, I'm having a really hard time placing a multi-leg order, such as the one in the tastytrade documentation. Below is the example. Could anyone please advise as to how this can be done with this API? Much appreciated.

{
  "source": "my-api-code",
  "order-type": "Limit",
  "time-in-force": "Day",
  "price": "1.51",
  "price-effect": "Credit",
  "legs": [
    {
      "instrument-type": "Equity Option",
      "symbol": "TSLA  230714P00210000",
      "action": "Buy to Open",
      "quantity": "1"
    },
    {
      "instrument-type": "Equity Option",
      "symbol": "TSLA  230714P00215000",
      "action": "Sell to Open",
      "quantity": "1"
    },
    {
      "instrument-type": "Equity Option",
      "symbol": "TSLA  230714C00282500",
      "action": "Sell to Open",
      "quantity": "1"
    },
    {
      "instrument-type": "Equity Option",
      "symbol": "TSLA  230714C00290000",
      "action": "Buy to Open",
      "quantity": "1"
    }
  ]
}

My attempt to do this is below. The problem here is that I see no place where I can specify the option contract identifier (e.g. TSLA 230714C00290000).

symbol = Equity.get_equity(session, 'TSLA')
leg1 = symbol.build_leg(Decimal(1), OrderAction.BUY_TO_CLOSE)
leg2 = symbol.build_leg(Decimal(1), OrderAction.SELL_TO_CLOSE)
leg3 = symbol.build_leg(Decimal(1), OrderAction.SELL_TO_CLOSE)
leg4 = symbol.build_leg(Decimal(1), OrderAction.BUY_TO_CLOSE)

order = NewOrder(
        time_in_force=OrderTimeInForce.DAY,
        order_type=OrderType.LIMIT,
        legs=[l1, l2, l3, l4],
        price=Decimal('1.51'),
        price_effect=PriceEffect.CREDIT
)
resp = account.place_complex_order(session, oco, dry_run=False)

The other approach was to do an OCO order but seems that does not solve the issue either. After further review of the source code, seems I should be using the Option class when "creating" the symbol such as in:

symbol = Option.get_option(session, 'TSLA  230714C00290000')

Having said that, would this be the more correct approach:

leg1 = Options.get_option(session, 'TSLA  230714P00210000').build_leg(Decimal(1), OrderAction.BUY_TO_CLOSE)
leg2 = Options.get_option(session, 'TSLA  230714P00215000').build_leg(Decimal(1), OrderAction.SELL_TO_CLOSE)
leg3 = Options.get_option(session, 'TSLA  230714C00282500').build_leg(Decimal(1), OrderAction.SELL_TO_CLOSE)
leg4 = Options.get_option(session, 'TSLA  230714C00290000').build_leg(Decimal(1), OrderAction.BUY_TO_CLOSE)

order = NewOrder(
        time_in_force=OrderTimeInForce.DAY,
        order_type=OrderType.LIMIT,
        legs=[l1, l2, l3, l4],
        price=Decimal('1.51'),
        price_effect=PriceEffect.CREDIT
)
resp = account.place_complex_order(session, oco, dry_run=False)

Your feedback is much appreciated.

Graeme22 commented 4 weeks ago

Hi, you're on the right track! The last bit of code you posted should work other than a couple typos (Option instead of Options). You can, if you'd like, combine all of the Option fetching into one line:

options = Option.get_options(session, ['TSLA  230714P00210000', 'TSLA  230714P00215000', 'TSLA  230714C00282500', 'TSLA  230714C00290000'])

However, the standard way to go about this is to use the option chain instead of individual symbols, as described here: https://tastyworks-api.readthedocs.io/en/latest/instruments.html#options-chains

salamad commented 4 weeks ago

@Graeme22 thank you so much for the info. I like the single-line Option fetching but am now understanding more thanks to that option chain link you provided. I certainly missed that in the docs.

If I may ask a separate question that I've been trying to figure out that is still somewhat related to this.

The question is about the price. When working on the tastytrade desktop/browser dashboard, it automatically sets the price at the MID. How can I compute that MID price through the API as I'm building the order? Even the #options-chains documentation link above, it just shows the price of 1.25 but not how it was derived.

I assume that I could setup a stream to watch for the metrics but I'm wondering if there is a more direct/real-time way to do it (just get a spot reading) as I'm building the order and not have to depend on an async process and hope that it comes back with an event.

Then again, I could be totally off on this too.

Thank you

Graeme22 commented 4 weeks ago

You will need to use the streamer and subscribe to quote events for the symbols you're interested in. The mid price is the average of bid and ask for single legged orders, while for multi legged you sum the bid price for short legs and the ask price for long legs and then average that.

salamad commented 4 weeks ago

@Graeme22, thank you again. One last question regarding the price, if you don't mind. After I do the operation, sometimes the price is negative. Seems that when I send a negative value as the price, I get a TastyTrade error that the request failed because price does not have a valid value. How can I specify a negative value in the price (particularly in a multi-leg transaction)? Thank you again

Graeme22 commented 4 weeks ago

The price_effect part of the order is what you're looking for. PriceEffect.DEBIT corresponds to negative values, CREDIT to positive ones.

salamad commented 4 weeks ago

@Graeme22 really appreciate your prompt feedback. I've been able to setup everything you have recommended and seems to be working perfectly.

I apologize in advance if I'm not using the correct channel for this type of questions, but I do have one other question :)

To keep matters simple, I'll skip writing the mechanical details of setting up the order legs and the order itself.

Suppose I place an order with 4 legs:

L1: BUY_TO_OPEN IWM 231215P00171000 @ price -$3.60
L2: SELL_TO_OPEN IWM 231215P00168000 @ price $2.55
L3 SELL_TO_OPEN IWM 231215P00167000 @ price $2.27
L4: BUY_TO_OPEN IWM 231215P00166000 @ price -$2.02

The above would have a price_effect of Debit and a price of $0.80

After I place the order (same day, a day later, or whenever), I want to close 2 of those legs and open 2 new legs such as:

L2: BUY_TO_CLOSE SPX 231215P00168000 @ price -$0.03
L4: SELL_TO_CLOSE SPX 231215P00166000 @ price $0.02
L5 SELL_TO_OPEN SPX 240216P00168000 @ price $1.10
L6: BUY_TO_OPEN SPX 240216P00166000 @ price -$0.93

The above would have a price_effect of Credit and a price of $0.16

The question is how to mechanically "close" the old L1 and L2 positions from the previous order and open the two new positions L5 and L6 for a total price of Credit $0.16?

If I modify the previous order, do I only modify L1 and L2 legs and then adjust, as needed, the price_effect and price of the entire original order and then place a brand new order for L5 and L6 with its applicable Credit of $2.03?

Not sure if there is a different mechanism within your API that I'm missing or not understanding? Would this convert the original "simple" order into a "complex" order, whatever that would mean?

Appreciate your help on this matter.

Graeme22 commented 4 weeks ago

I apologize in advance if I'm not using the correct channel for this type of questions, but I do have one other question :)

This is the right place! If other people in the future have similar questions hopefully they'll land here.

If I modify the previous order, do I only modify L1 and L2 legs and then adjust, as needed, the price_effect and price of the entire original order and then place a brand new order for L5 and L6 with its applicable Credit of $2.03?

Nope. You'll roll it by creating a single new order with the 4 legs you specified (2,4,5,6) and the total price you specified ($0.16 credit.) It works the same way as in the platform.

Not sure if there is a different mechanism within your API that I'm missing or not understanding? Would this convert the original "simple" order into a "complex" order, whatever that would mean?

Nope, complex orders involve stop losses and profit-taking orders.

salamad commented 3 weeks ago

@Graeme22 thank you again for the clarification. I will give this a shot.