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

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)
4 Likes

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

Since a listen key is only valid for 24 hours, how would you keep listening to an order if it passes the 24 hour mark?

Another reason why ByBit is better

I copy a deal from one account to another in futures. First account creates a limit order, specifies stop loss and take profit immediately upon creation. Using api, I can only see the symbol entry price and other data, but I can’t get the stop loss and take profit prices. How can I get all the data at once?

second question: I copy the deal to the second account only the price of the limit order and other data without stop and take prices, after the price reaches the limit order, it goes into position and stop and take orders are created on the first account. I also copy them to the second account. But when the price approaches the stop, the take is also closed, and vice versa, but the position itself is not closed.
How can I link all three orders together?

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

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.

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.

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…

1 Like

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

For your first inquiry, as far as I can tell there is no way to set the take profit at the same time as making the order, because, even Binance itself, makes two different calls to their API when you use the UI.

Now concerning your second question, I proposed a solution in a different topic which isn’t officially provided in the Binance API doc, but it seems to work in my case. All that is needed for me is to use the GTE_GTC value for the timeInForce parameter when sending my TP/SL request.

I am not able to create a TP/SL order to a Short position, but I am able to do it in a LONG position.


Can you help me?

@MJW thanks for your answer.
I can see a different way to do it in implementing manually SL/TP in a program:

  1. subscribe to websocket data stream (candlestick)
  2. open an order,
  3. check all the close values received
  4. close your order when SL or TP is hit.

But I am wondering if this method is as accurate as your procedure. (In other words, are the SL/TP orders more accurate than an individual implementation of them using the websocket Kline/candlestick stream)
What do you think ?

I was running into the same issue and worked out that it can be done without the need for a socket connection to monitor whether or not one of the TP/SL has been filled, in order to cancel the other.

It does require three separate calls to the api to create each as the solved answer points out, but the parameters sent in the calls can be set as such that one will cancel the other two if it executes.

For example, the SL being hit will cancel the original order and the TP and the TP being hit will cancel the original order and the SL, etc. Here are the parameters I used in each call to achieve this (with example values):

Original order -

  • symbol=BNBUSDT

  • side=BUY

  • positionSide=BOTH

  • type=MARKET

  • quantity=1

  • reduceOnly=false

SL Order -

  • symbol=BNBUSDT

  • side=SELL

  • positionSide=BOTH

  • type=STOP_MARKET

  • timeInForce= GTE_GTC

  • quantity=1

  • reduceOnly=true

  • stopPrice=(your stop price)

  • workingType= MARK_PRICE

TP Order -

  • symbol=BNBUSDT

  • side=SELL

  • positionSide=BOTH

  • type=TAKE_PROFIT_MARKET

  • timeInForce= GTE_GTC

  • quantity=1

  • reduceOnly=true

  • stopPrice=(your take profit price)

  • workingType= MARK_PRICE

There isn’t two separate calls in the UI as pointed out in this thread, rather one call with a payload with three parts, one for each order entered in the UI (the trade, stop loss, take profit).

To see this, have a look in the network call in developer tools. In the call (place-order) under its payload tab, you’ll see it passes a list called ‘subOrderList’. Inside this are the three JSON blobs for each respective order. You’ll find the parameters needed in there.

Hopefully this helps anyone else running into this issue :slight_smile:

9 Likes

Can you post a link to where is the "network call in developer tools. In the call (place-order) under its payload tab, you’ll see it passes a list called ‘subOrderList’. " It would be verry helpful or an example of how you passed the three json files in an order.

You say you send 3 separate orders but how is the link between the TP and SL orders and their parent order specified?

This works for me, thanks a million for sharing.