How to implement OTOCO(TP/SL) orders using API

Q: In Futures web page/App, you can check “TP/SL” box to place a limit/market order followed by a TP order and/or an SL order. Is there an API interface to do that?

A:
There’s no direct way to do it but you can leverage on WSS and code logic to get similar effect. Below is the procedure:

  1. Listen to your futures user data stream - https://binance-docs.github.io/apidocs/futures/en/#user-data-streams
  2. Place the limit/market order as you planned and specify a unique client order id to identify it later
  3. If you receive an event from your user data stream indicating that particular order has been filled, place the TP/SL order(s) with unique client order ID. The orders both should be reduce-only and same quantity as your order placed in #2
  4. Keep tracking your TP/SL orders(s) until either one of your TP/SL orders gets filled (it would go through triggering stage and then get filled)
  5. Cancel the other order (might have been triggered and turned to a limit order)
1 Like

Hey, I am unable to set up a scenario like you described, for my take profit/stoploss orders I keep receiving “-1106: Parameter ‘reduceOnly’ sent when not required.”

Settings I used for testing:
symbol = btcusdt
signal = Signal.BUY; (Buy market order with sell for stoploss/take profit)
quantity = 0.01
takeprofit = 59000
stoploss = 58000

I fetch their unique client order IDs from the result, instead of generating my own as it seemed easier.

I first create a market order, which works fine (the order appears on the testnet platform)
And then create the 2 stoploss/take profit orders (but both of them respond with the same error)
like this:

 Order marketOrder = syncRequestClient.postOrder(
     symbol,
     side == Signal.SELL ? OrderSide.SELL : OrderSide.BUY,
     side == Signal.SELL ? PositionSide.SHORT : PositionSide.LONG,
     OrderType.MARKET,
     null, //TimeInForce
     quantity.toString(), //Quantity
     null, //Price
     null, //Reduce only
     null, //NewClientOrderId
     null, //Stop price
     null, //workingType
     NewOrderRespType.RESULT //NewOrderRespType
 );

 Order stopOrder = syncRequestClient.postOrder(
      symbol,
      side == Signal.SELL ? OrderSide.BUY : OrderSide.SELL,
      side == Signal.SELL ? PositionSide.SHORT : PositionSide.LONG, 
      OrderType.LIMIT,
      TimeInForce.GTC,
      quantity.toString(),
      stoploss.toString(),
      "true", //Reduce only
      null, //NewClientOrderId
      null, //Stop price
      null, //workingType
      NewOrderRespType.RESULT //NewOrderRespType
  );

 Order profitOrder = syncRequestClient.postOrder(
      symbol,
      side == Signal.SELL ? OrderSide.BUY : OrderSide.SELL,
      side == Signal.SELL ? PositionSide.SHORT : PositionSide.LONG, 
      OrderType.LIMIT,
      TimeInForce.GTC,
      quantity.toString(),
      null, //takeprofit.toString(), //Price
      "true", //Reduce only
      null, //NewClientOrderId
      takeprofit.toString(), //Stop price
      null, //workingType
      NewOrderRespType.RESULT //NewOrderRespType
  );

I’ve tried using the stop price instead of price, and also in one-way mode but it I can not get it to work

This is a knowledge sharing topic. Please open your problem with a new one

Hi for step 4, how do you keep track of the TP/SL orders? inside of our code

Let’s keep the discussion in the “Automatically cancel take profit and stop loss orders when closing position” post.

It’s a reasonable workaround but it makes me a little nervous because what if stop loss order fails after position has been opened?

I would prefer that all orders are processed atomically. i.e. The position only becomes active once the stops have been added. From my understanding of the (private) OCO API this is what happens. Can anyone confirm?

That sounds really nice. Does the position has to be in the new state or it can be also on the open state to place TP/SL positions as you described?

I had similiar case: I opened a position, placed a stop-loss order (order type: STOP_MARKET, closePosition: ture) attached to the open position, and placed a limit order (this was NOT attached to the opened position) to take profit, both stop-loss and limit orders were placed via API. Then when I closed the position from the Binance UI manually, the limit order got deleted (why???), while the stop-loss order remained there…

I mean, when you have closePosition=true specified to the order, which means the order is attached to the open position, so when the position is closed, there is no point leaving the attached order there.

They even deleted the limit order, which is not attached to the position…yet they left the attached stop-loss order open there, which is no logic at all.

well, if these orders (limit and stop loss) are placed manually from the Binance UI, they will be automatically deleted when you close the position from the Binance UI as well, that’s the correct logic, and that logic should apply to the orders placed via API (by us), the logic has nothing to do with whether they are placed via API or manually from the UI. The logic is the position is closed, there is no point leaving those orders there.

It’s pretty easy for them to just do one extra step, that is when the position is closed, whether it is closed by users or the system due to execution of stop-loss or take profit orders, as long as the position is closed, they just need to cancel all open orders for the pair.

I’m able to do it with the API by placing 2 orders, one for the TP and one for the SL. Types are TAKE_PROFIT_MARKET and STOP_MARKET. Both orders are in the Orders tab and also attached to the position (TP/SL for position). They both work well except that if one is executed, the position is closed but there is a remaining order opened. It does the same if I try to close the position by myself. It seams there is a bug that doesn’t cancel the remaining orders attached to the closed position.

Has anyone figured how to take profit on same order in Binance Api for futures yet in here?

I know 2 line orders but it’s not right…

Or better yet how to auto close the second order so it doesn’t effect the bot