I set a stop limit order on ACY with an 11.50 stop price and 11.40 limit price. Once it was triggered, it sold for 11.27. What happened?
I have the same issue; I’d like to know why as well.
@jakeyoon I can maybe help. What was the order number? With that I can check the logs and help troubleshoot.
I noticed slow order fill speed (even market orders get filled slow)
and I noticed positions going well over or below the profit and stop limit.
There were multiple orders that closed way below stop limit, even saw -20% loss and upward of 20% gain when I have +3% take profit and -3% stop limit.
SKYT was one of the example that lost 10%
ORDER ID - eea5cea1-9096-4dc4-88f7-d47876d8ced3
It’s also hard to see past performance and past trades via the website.
Thanks so much Dan,
@jakeyoon The order for SKYT above executed exactly as one would expect. I’ll walk through the execution which may help.
So, the order started as a bracket order
MARKET BUY SKYT
Take Profit LIMIT SELL $33.17
Stop loss LIMIT SELL $29.98 STOP PRICE $30.29
The market order filled immediately when the order was submitted
9:31:32 MARKET BUY SKYT filled at $32.51
In paper trading, BUY orders fill at the current
ask_price. In live trading there may be some price improvement but this is sort of a ‘worse case’ used to simulate paper orders. Let’s check the latest quote as of when the order was submitted at 9:31:32 to see if the order actually did fill at that current
# Set times to a bit before and after all the order times to ensure we get all the data start_time = pd.to_datetime('2021-06-21 13:31:00', utc=True) end_time = pd.to_datetime('2021-06-21 13:45:00', utc=True) symbol = 'SKYT' # First get quotes to verify initial market order fill quotes = api.get_quotes(symbol, start=start_time.isoformat(), end=end_time.isoformat()).df quotes.index = quotes.index.tz_convert('America/New_York') quotes.between_time('9:31:20', '9:31:38')
Here are the quotes. Notice the latest quote as of when the order was submitted was at 9:31:27. The
ask_price was $32.51 which matches the fill price.
After the market order filled at 9:31:32, the take profit and stop loss legs were submitted. A stop loss order will trigger when a ‘valid’ trade occurs at or below the stop loss price. There are certain conditions which exclude an order from being considered ‘valid’. There is a list of those conditions in the next post. So now let’s check the trades to see when the stop triggered. We are looking for the first ‘valid’ trade after 9:31:32 with a price less than the stop price of $30.29.
# The market buy filled at the current ask_price of $32.51 as of 9:31:32 # Now let's check why/when the stop limit leg triggered # That had a stop loss of $30.29 # Check the trades for that (ie stops are triggered from trades) # First get quotes to verify initial market order fill trades = api.get_trades(symbol, start=start_time.isoformat(), end=end_time.isoformat()).df trades.index = trades.index.tz_convert('America/New_York') trades.between_time('9:31:32', '9:45').query('price<=30.29')
Here are the trades. Notice the first ‘valid’ trade with a price below the stop price was at 9:42:15. There was also a trade at 9:42:08 but that was an excluded odd lot order (condition I). Therefore, the stop loss triggered at 9:42:15.
So, the stop loss triggered and a SELL limit order was submitted at 9:42:15.828. The limit price was $29.98 so it will fill at the
bid_price if it’s above that limit price. Let’s check the latest quote as of when the order was triggered at 9:42:15.828 to see if/when it filled at that current
Here are the quotes. Notice the latest quote as of when the order was submitted was at 9:42:15.828. The
bid_price was $30.21 which is above the limit price so the order filled immediately at that price.
One last check is to see if the take profit limit price was ever reached before the stop was triggered at 9:42:15.828. This would have filled if the
bid_price was ever above the take profit limit of $33.17. So doing a quick query, it looks like there are no quotes returned. Therefore the take profit never would have filled before the stop was triggered.
Hope that helps explain the steps as the order executed. It all looks correct. I included the python code for checking trades and quotes so one can check their own orders in a similar way.
Here is the list of trades which are excluded when looking for ‘valid’ trades to trigger a stop.
|CTS||AB||B||Average Price Trade|
|UTDF||C||W||Average Price Trade|
|CTS/UTDF||ABC||7||Qualified Contingent Trade (“QCT”)|
|CTS/UTDF||ABC||9||Corrected Consolidated Close (per listing market)|
|CTS/UTDF||ABC||G||Bunched Sold Trade|
|CTS/UTDF||ABC||H||Price Variation Trade|
|CTS/UTDF||ABC||I||Odd Lot Trade|
|CTS/UTDF||ABC||M||Market Center Official Close|
|CTS/UTDF||ABC||P||Prior Reference Price|
|CTS/UTDF||ABC||Q||Market Center Official Open|
|CTS/UTDF||ABC||U||Extended Trading Hours (Sold Out of Sequence)|
|CTS/UTDF||ABC||Z||Sold (out of Sequence)|
I appreciate it
Thank you so much for your detailed reply.