Updating Profit and Stoploss


I am trying to develop a program with the Alpaca API, which has both a stop loss and a profit target which will be updated daily based on certain conditions. I tried implementing this with a bracket order, but I found I can’t cancel the existing stop loss and profit target orders to replace them. Is there any way that I can keep updating my sell orders every day to have both a stop loss and profit target order?

@Rishab_Maheshwari Definitely the approach should be to use bracket orders. This will keep the three parent, stop loss, and take profit orders all linked together making it easier to follow the order flow. Definitely also use the ‘replace’ order endpoint/method to update the stop loss and limit prices. Don’t cancel the orders. That will break the ‘linkage’ between the orders.

The key to understanding the ‘replace’ process is that orders are never really ‘updated’. As the name implies, they are replaced. The original order is cancelled and a new order, with a new order ID, is created with any updated parameters (eg stop or limit price). This can become confusing keeping track of the latest up to date order IDs. Fortunately, and the reason bracket orders are helpful, the parent order has an attribute called legs which contains the most recent stop loss and take profit limit orders. Behind the scenes the parent order is constantly being updated to reflect any replaced ‘leg’ orders. legs[0] is always the take profit limit order and leg[1] the stop loss. All one needs to do then is store, or somehow get, the original parent order and one can get the leg order IDs. Once one has those order IDs one can replace the stop loss and limit prices as desired.

So to update the take profit order one could do something like this (using the alpaca-py SDK). First, store or somehow fetch the parent order ID. Perhaps when first placing the bracket order.

# place the original bracket order
my_bracket_parent_order = trading_client.submit_order(order_data) 

# the take profit order is always the first (ie 0) leg 
take_profit_leg = my_bracket_parent_order.legs[0] 

# create a ReplaceOrderRequest object with any changed data (the limit price in this case) 
updates_to_take_profit = ReplaceOrderRequest(limit_price=100.50) 

# replace the order 
trading_client.replace_order_by_id(take_profit_leg.id, updates_to_take_profit)

# Similarly for the stop loss leg
# the stop loss order is always the second (ie 1) leg
stop_loss_leg = my_bracket_parent_order.legs[1] 

# create a ReplaceOrderRequest object with any changed data (the stop price in this case) 
updates_to_stop_loss = ReplaceOrderRequest(stop_price=90) 

# replace the order
trading_client.replace_order_by_id(stop_loss_leg.id, updates_to_stop_loss)

The new replaced order IDs will then be updated and reflected in the original parent order. One can then use the same code to replace the orders again (presumably though with different prices).

One subtle note about the replace endpoint/method. It will cause an error if one tries to replace an order without making any changes (for example the current limit price is $10 and one replaces it with $10). It’s more of an ‘information only’ error but it may crash your program so either 1) only replace with different values or 2) capture the error and handle it gracefully.

One ‘trick’ I use to close a position which currently has an outstanding bracket order is to replace the limit price of the take profit leg. Replace it with a ‘marketable’ price (ie better than the current quote) and it should fill immediately. One could cancel the bracket order and then submit a new order to close the position, but by simply replacing the limit price it keeps the orders all ‘linked’ which is often helpful.

Hope that helps.

1 Like


Thank you for your response. For the above line, how do you import the ReplaceOrderRequest.


@Rishab_Maheshwari Good question “how do you import the ReplaceOrderRequest”. I personally find the new alpaca-py SDK to be much more cumbersome than the original SDK. One reason is all the manual imports one must do. This may not be so bad if there were better documentation, but that’s a different issue.

Anyway, here is the complete code (with imports) and assuming one previously has set the take_profit_leg order. Good luck!

!pip install alpaca-py

from alpaca.trading.client import TradingClient
from alpaca.trading.requests import ReplaceOrderRequest


trading_client = TradingClient(ALPACA_API_KEY_ID_PAPER, ALPACA_API_SECRET_KEY_PAPER, paper=True)

updates_to_take_profit = ReplaceOrderRequest(limit_price=100.50)
trading_client.replace_order_by_id(take_profit_leg.id, updates_to_take_profit)