How to set up this type of order?

I’m trying to achieve a particular behavior when placing an order. I would like to do the following:

if ( True )
BUY 100 shares and set a bracket order with these conditions:
take_profit.limit_price = average_entry_price + $1
stop_loss.stop_price = average_entry_price - $2
timeout for order = if ( elapsed time since purchase > 10 minutes)
sell all shares at market

How do I determine average entry price so I can place a bracket order like the above? And is there a way to specify a timeout to sell, and if not, how to achieve this behavior.

thanks

@Artic The basic issue (as you noticed) is one cannot really know the entry price of the original parent order. However, you can make an educated guess.

If the initial parent order is a limit order, it will typically fill at the limit price. Use the limit price as the entry price.

If the initial parent order is a market order, it will typically fill at the quote price (the ask price for buy orders and the bid price for sell orders). Use the quote price as the entry price.

There are really just two order ‘timeouts’ or Time-In-Force values you can use. Day or Good-Till-Cancelled (GTC). There is info in the docs here. You can also cancel the order at any time using the cancel_order_by_id method.

Here is an example of placing a limit and market order

!pip install -q alpaca-py

from alpaca.trading.client import TradingClient
from alpaca.trading.requests import MarketOrderRequest, LimitOrderRequest, StopLossRequest, TakeProfitRequest
from alpaca.trading.enums import OrderSide, OrderClass, TimeInForce

from alpaca.data import StockHistoricalDataClient
from alpaca.data.requests import StockLatestQuoteRequest

API_KEY = 'xxxxx'
SECRET_KEY = 'xxxxx'

client = TradingClient(api_key=API_KEY, secret_key=SECRET_KEY)

# Bracket order with limit order as initial 'parent' order
symbol = 'VXX'
limit_price = 11.00

limit_bracket_order = client.submit_order(LimitOrderRequest(
                  order_class = OrderClass.BRACKET,
                  time_in_force=TimeInForce.DAY,
                  symbol=symbol,
                  qty=1,
                  side=OrderSide.BUY,
                  limit_price=limit_price,
                  stop_loss=StopLossRequest(stop_price=limit_price-1.00),
                  take_profit=TakeProfitRequest(limit_price=limit_price+1.00)
                  ))

# Bracket order with market order as initial 'parent' order

symbol = 'VXX'
latest_quote = client.get_stock_latest_quote(StockLatestQuoteRequest(symbol_or_symbols=symbol))
expected_entry_price = latest_quote[symbol].ask_price

market_bracket_order = client.submit_order(MarketOrderRequest(
                  order_class = OrderClass.BRACKET,
                  time_in_force=TimeInForce.DAY,
                  symbol=symbol,
                  qty=1,
                  side=OrderSide.BUY,
                  stop_loss=StopLossRequest(stop_price=expected_entry_price-1.00),
                  take_profit=TakeProfitRequest(limit_price=expected_entry_price+1.00)
                  ))

One thing to note is you need to have access to real time full market SIP data to fetch actual quotes. If you are only using the free Basic Market Data, the latest_quote will only reflect quotes on the IEX exchange and will generally NOT be what your order will fill at.

If you want to be exact about the stop and take-profit limit prices, you can 1) fetch the actual filled price once the parent order fills and 2) replace the stop and take-profit limit prices based on that actual entry price.

1 Like

thanks, that’s very helpful. I am currently doing the last thing you mentioned “1) fetch the actual filled price once the parent order fills and 2) replace the stop and take-profit limit prices based on that actual entry price” by using:

position = trading_client.get_open_position(‘NVDA’) and then using

position.avg_entry_price to set up my orders. Problem is the get_open_position API call is very slow and choking up performance. I profiled my code and that function is a major bottleneck. My hope was to set up the order once from the beginning and not worry about it. But as you mentioned, the filled prices will be estimates if I use the other methods, I really need to be precise.

I have also tried
latest_quote = client.get_stock_latest_quote(StockLatestQuoteRequest())

and again, it’s a very expensive function call. My algo is running at a very very high frequency and these calls stymie the speed I need.

I’m not sure how to perform the second part of your response. How would I do this exactly? Seems like it would require updating the Bracket order, or perhaps even placing an OCO sell order. Could you provide some example python on how to do this?

thanks

Still need help updating the stop and take profit llimit prices. Anyone know how to update those parameters? I don’t see how to update anywhere in the documentation.

I’m still having trouble performing a simple bracket or OCO sell order. After successfully doing a regular market buy with:

    market_order_data = MarketOrderRequest(
        symbol="NVDA",
        qty=10,
        side=OrderSide.BUY,
        time_in_force=TimeInForce.DAY
        )

I then wait to make sure that I have all 10 shares in my position by checking my account position with trading_client.get_open_position(‘NVDA’) and checking that I hold a quantity = 10. Then later in my code I try to sell with:

            market_order_data = MarketOrderRequest(
                symbol="NVDA",
                qty=10,
                side=OrderSide.SELL,
                time_in_force=TimeInForce.DAY, 
                order_class=OrderClass.OCO,
                take_profit=TakeProfitRequest(limit_price=prof),
                stop_loss=StopLossRequest(stop_price=loss)
                )

but it doesn’t sell. I’ve tried the same command above but using a LimitOrderRequest and that also doesn’t work. Clearly, something is wrong with my syntax but I can’t find any documentation showing a sell order for bracket or OCO. Could anyone help?

@Artic Is your OCO order not being created or just not filling? One very un-obvious aspect of the alpaca-py SDK is OCO orders need to be submitted with a LimitOrderRequest (and not a MarketOrderRequest). This is because the initial ‘parent’ order in this case is the limit order.

So, try this. Notice you don’t really need the limit price for the main order. Simply include take_profit= {'limit_price': round(my_limit_price, 2)}. Also note that prices cannot be specified to more than 2 decimals or they will be rejected. That is the reason for the round function.

my_oco_order = client.submit_order(LimitOrderRequest(
  symbol='NVDA',
  qty=10,
  side=OrderSide.SELL,
  order_class= OrderClass.OCO,
  time_in_force=TimeInForce.DAY,
  stop_loss= {'stop_price': round(my_stop_price, 2)},
  take_profit= {'limit_price': round(my_limit_price, 2)},
)