Bracket Order Exit Trades Not Executing

I’m utilizing bracket orders and am currently testing my code in the paper trading. I have had a bracket order’s entry fill; however, both exit trades still have their status marked as “held” as opposed to “new”. Additionally, the stop loss exit order has been reached yet has never been executed. Is this something wrong with the paper trading, or am I missing something?

Paper trading generally mimics how orders are processed in live trading. So, no there probably isn’t anything ‘wrong’ with paper trading.

A bracket order is a form of “One-Triggers-Other” (OTO) order. When the initial parent order is filled, it triggers the other two orders. Technically the other two ‘leg’ orders are “One-Cancels-Other” (OCO) when one is started it cancels the other. So, until the initial parent order is filled, the other two will have a state of held. This would be a normal state if a bracket order is submitted after market hours for example.

Once the parent order completely fills (ie not a partial fill) the typical state would be the take profit limit order leg is set to new and the stop loss leg remains held. The orders will stay in this state until either the limit price is exceeded or the stop loss is exceeded. At that point, the limit order would move to partial fill or fill and the stop loss order would be canceled, OR the stop loss would be triggered and move to partial fill or fill and the limit order canceled.

Where are you seeing both legs as held and the parent order filled. Is it on the web portal ‘orders’ page or via the API? If you submitted multiple orders, ensure you are looking at the three orders from associated bracket order and not from another order. All three orders will have the same ‘submitted at’ time which is one way to group them.

Perhaps include a screen shot of what you are seeing?

Hey Dan,

Below is a screenshot where the parent order has been filled but both legs are being seen as held. If I were to also get the orders via API the same thing is seen. Not sure why this is happening, but it’s causing additional losses (outside of things I need to change in my algorithm) as for example the BAX stop order should have already been reached (buy stop at 78.94 but market price is at the time of writing 79.37). This has happened with a couple other bracket orders, but I can only post one screenshot.

1 Like

Bump on this. Problem still existing.

I have the exact same issue on paper trading OCO orders. The stop price is reached and very significantly crossed, but the stop order is not triggered. I made some replacements on the take-profit limit price, would that be related?
Please help!

1 Like

same issue, and got no help. Two legs both held, 100% sure the market price has passed both legs multiple times, and orders never get triggered.

1 Like

Same issue here. Opened at 11:39 am a FSLY buy bracket order (gtc) at $52.76 with Stop Loss at $46.02 and Take Profit at $53.84. The market closed at $56.24 and the Take Profit should’ve been triggered yet neither orders were activated. Take a look at the screenshot

Appreciate any recommendations on how to solve this.

@Camilo_Cabrera paper trading is a simulation of live trading. The fill prices are simulated exclusively by looking at the bid and ask quotes. In live trading there is usually (but not always) price improvement but paper trading rather assumes a ‘worst case’. Buy orders fill at the current ‘ask’, and sell orders fill at the current ‘bid’.

Let’s walk through the history of this order.

11:39:38 bracket order is created
11:39:38 parent market BUY order is filled at $52.76
11:39:38 take profit SELL limit order is submitted at $53.84

First, let’s see why the parent market order filled at $52.76. In paper trading, BUY orders fill at the current ask price. So, start with listing the quotes at the time the order filled.

start_time = pd.to_datetime('2021-06-08 15:39:38', utc=True)
end_time = pd.to_datetime('2021-06-08 21:00', utc=True)

symbol = 'FSLY'

quotes = api.get_quotes(symbol, start=start_time.isoformat(), end=end_time.isoformat()).df
quotes.index = quotes.index.tz_convert('America/New_York')

Sure enough. The ask price at the time the market BUY was submitted and filled was $52.76.

Now, the limit sell order should fill when the bid is greater than the limit price (ie someone is willing to pay the asking limit price). Let’s check if the bid is ever greater than $53.84.

It appears the bid price matched the limit price at 12:42. The order would have normally filled at that time.

Why didn’t it? The order was blocked because of Pattern Day Trading (PDT) protection. Unless one has over $25,000 in their account, the PDT protections will prevent orders from being submitted which could result in going over the 3 day trades in 5 days rule. Buying and then selling FSLY in the same day constitutes a day trade. It isn’t indicated in the screenshots above, but if you look at the status of the limit and stop-loss orders they will be ‘Held’. This means they haven’t actually been submitted for execution. That is the clue to what is going on. The Alpaca day trade protections will keep these orders in a ‘Held’ state until the following day, and then try to re-submit them as ‘New’.

The way around this is to ensure one has more than $25,000 in equity in the account. This can be done in paper trading by ‘resetting’ the account and entering a starting balance over $25,000. However, if the goal is to simulate what would happen in live trading, and one’s live account is less than $25,000, then the above behavior is correct. In live trading, one would be blocked from selling a stock which would result in a day trade and if there is already 3 actual or ‘potential’ day trades in a 5 day rolling window.

Make sense?

1 Like

What a wonderful answer. Appreciate you took the time to go through my numbers and had the patience to explain the situation. I’m glad as well your answer is going to help someone else! I’ll do my homework based on what you said. Thanks again @Dan_Whitnable_Alpaca

1 Like

This should be user configurable. I think it would better to block an entry that could possibly lead to an intraday exit that violates the PDT rule. Other brokers have have warnings on the 4th entry potentially leading to a PDT violation. It could be disastrously to allow an entry with defined risk parameters and being unable to execute it because the exit was held for the next day.