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

I don’t use developer tools to dynamically insert the parameters into the call to the api with python. I only used it as a reference to see what the parameters should be when making a trade that included a TP/SL.

If you look at my first post, you will see the parameters I used for each call. Creating a trade that includes a TP/SL cannot be done with one call to the api - as we have already established. It needs three calls, right?

So, I used developer to see how Binance were doing it. I then copied each of the parameters and their values to make 3 (New Trade) calls with the api.

In your python code:

  • first call the api should be the trade and should contain the parameter values from my first post

  • second call should be the call to add your SL, using the parameters in my first post

  • third call should be to add your TP, using the parameters in my first post

There isn’t any python/developer tools interaction at all.

1 Like

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

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…

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:

3 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.

Sure, I’m unable to reply to you directly so hopefully you will see this in here. I can explain how to access the network tab in developer tools so that you can see the payload. I’m going to use Chrome as an example, but pretty much all modern browsers will have the same or similar for viewing stuff like this.

If you just search ‘Chrome Developer Tools’ in Google, there is a nice documentation site on using it.

For the sake of answering your question - (in Chrome) click ‘View’ → ‘Developer’ → ‘Developer Tools’. A new window should open, normally at the bottom of the page but if you click on the 3 dots at the top right of this window you can choose where it goes by selecting an arrangement in ‘Dock side’.

You will see along the top of the window ‘Elements’, ‘Console’… ‘Network’ is also there, this particular tab allows you to see the calls that are being made when you click on a link, etc.

The setup for viewing the call being made when you make an order with a TP/SL is to input all the values as if you were going to make a trade on the Binance site. Make sure you have TP/SL checked with your respective values in the fields.

With the ‘Network’ tab open, just before you click to buy or sell, there is a small circle with a diagonal line through it on the left of the Developer Tools window on the row below where it says ‘Network’. Click this to clear any previous network activity. It makes it easier for sorting through and finding the Binance request. Or in the filter box you can just search for it.

Now make your order and you will see under the ‘Name’ column the request called ‘place-order’ appear, this is your order. Click on it and to the right of it a window will open with ‘Headers’, ‘Payload’… Click ‘Payload’ and expand the ‘Request Payload’ JSON, then expand {strategyType: “OTOCO”,…} inside that. You will then see ‘subOrderList’. This contains the fields and their values for the calls needed to Binance, 1 for the trade, 1 for the TP and 1 for the SL.

There are extra values in there that won’t be required, but I’m sure you can work out what is and isn’t needed by comparing to the api documentation.

For the three calls to execute all of this, my logic is this (pseudo code):
make trade request:
if it returns a 200:
make a SL request:
if that returns a 200:
make a TP request
if that returns a 200:
return 200
otherwise:
cancel the original trade and SL
otherwise:
cancel the original trade (with its orderId)
otherwise:
return error

If you need any more clarification on any of this, let me know.

2 Likes

I don’t have Facebook, sorry. What is it you are actually trying to achieve here? Are you wanting to create an order in Binance via the api that includes a TP/SL?

I am calling the Binance api to do that, but in order to add the TP/SL it requires two additional calls. All of the required values are sent as url parameters in each of the calls.

So if you look in each of the JSON parts for the trade, take profit and stop loss that you get via the network tab in developer tools you will see all of the parameters and their values you will need to construct each url to make each call with.