Handling rapidly filled orders for OCO placement using websocket

Here’s a breakdown of the issue:

  • My script listens for ‘fill’ events through the Alpaca WebSocket to get real-time updates on order status.

  • Upon receiving a ‘fill’ event for a buy order, it is supposed to place an OCO order for that particular fill, based on the filled price and quantity.

  • However, when the next buy order gets filled almost instantly after the first one, the script places the OCO order for the second fill using the details (price and quantity) of the first fill.

For example:

  • Order 1 filled: 50 shares at $2.
    
  • Order 2 filled: 100 shares at $1 (shortly after order 1).
    
  • OCO for order 2: Incorrectly placed using details from order 1 (50 shares at $2).
    

example screenshot of the order history. As you can see, the quantity and price of the stop or limit sell order do not match with its corresponding buy order.

Here is the part of the script that handles this operation:

    if data['event'] == 'fill':
        order_id = data['order']['id']
        if data['order']['side'] == 'buy':
            ticker = data['order']['symbol']
            filled_price = float(data['order']['filled_avg_price'])
            order_qty = int(data['order']['filled_qty'])

            def place_oco_order(ticker, filled_price, order_qty, order_id):
                try:
                    stop_loss_price = rounding.round_risk(filled_price * 0.95)
                    take_profit_price = rounding.round_risk(filled_price * 1.05)
                    print(f"Placing OCO for {ticker} based on Order ID {order_id}: SL at {stop_loss_price}, TP at {take_profit_price}")
                    api.submit_order(
                        symbol=ticker,
                        qty=order_qty,
                        side='sell',
                        type='limit',
                        time_in_force='gtc',
                        order_class='oco',
                        stop_loss={'stop_price': str(stop_loss_price)},
                        take_profit={'limit_price': str(take_profit_price)}
                    )
                except tradeapi.rest.APIError as e:
                    print(f"API Error: {e}")

            Timer(125, place_oco_order, args=(ticker, filled_price, order_qty, order_id)).start()

Would greatly appreciate any advice or solutions, thanks!