Skip to main content

Command Palette

Search for a command to run...

Create and Cancel Orders via WebSocket on Binance

How to access the Binance API via WebSocket in Python

Published
โ€ข9 min read
Create and Cancel Orders via WebSocket on Binance

Until now it was only possible to receive data from Binance via WebSocket. To send requests to the Binance API, for example to create or cancel orders, you always had to use the slower REST API. This has changed now! ๐Ÿš€

Recently, the Binance Spot Testnet and Binance Spot have added the ability to place orders, cancel orders, and handle other API requests via a WebSocket connection:

Binance WebSocket API Documentation

In Python, UNICORN Binance WebSocket API already supports the new features to send API requests to Binance via WebSocket. To do this, we will go through the following steps:

  1. Binance

    โ€” Account

    โ€” API key and API secret

  2. Installation of requirements

    โ€” PIP

    โ€” Conda

  3. Create a Python script and establish a WebSocket API connection to Binance

  4. Send requests and handle the responses

    โ€” Global async function

    โ€” Global callback function

    โ€” Stream specific async function

    โ€” Stream specific callback function

    โ€” Request specific callback function

    โ€” Save answer in variable

    โ€” Using the stream_buffer

    โ€” Multiple API Streams

  5. Available methods for API requests

    โ€” ubwa.api.spot.cancel_order()

    โ€” ubwa.api.spot.create_order()

  6. Further information


1. Binance

Account

To be able to trade on Binance you need a user account. If you don't have one yet, you can sign up via my referral link. With this we both get 100 USDT cashback vouchers for a 50 USD deposit.

API key and API secret

How to create an API Key/Secret pair you can read in detail here.


2. Installation of requirements

Minimum requirement is a working Python 3.8+ installation. To use the UNICORN Binance WebSocket API in your Python script, you need to install it on the command line using one of the two commands:

With PIP:

This is the default way and should always work. It contains the plain source code and optimized Cython and PyPy Wheels:

pip install unicorn-binance-websocket-api

Or with Conda:

This is only possible within an Anaconda environment:

conda install -c conda-forge unicorn-binance-websocket-api

3. Create a Python script and establish a WebSocket API connection to Binance

First we import unicorn_binance_websocket_api, define the callback function handle_socket_message() to print the received data and then store an instance of BinanceWebSocketApiManager() in the variable ubwa.

If you want to connect BinanceWebSocketApiManager() to the testnet, you need to pass the string 'binance.com-testnet' to the exchange parameter.

With process_stream_data=handle_socket_message we pass BinanceWebSocketApiManager() a global callback function, to this function all received responses of the API are passed by default.

By default, all API requests return a string with a JSON structure. To automatically convert the JSON structure to a Python dictionary, we configure output_default="dict".

https://gist.github.com/oliver-zehentleitner/5a5d739710400c8fbd9f04833c4bf1dc

Next we create the API stream, for this it is important to set the parameter api to True, otherwise a connection to another WebSocket endpoint would be established, where the API requests would not work. Please make sure that you use a valid API key/secret pair and consider the IP whitelist restrictions when testing!

https://gist.github.com/oliver-zehentleitner/b31adc37450a7a45752adb459c6b71d6

Now all the prerequisites are met for us to send and process requests to the Binance API.


4. Send requests and handle the responses

Note: According to this scheme all methods of ubwa.api can be called and executed.

Using the ubwa.api object we can now send requests to the Binance API, e.g. a connection test with the ubwa.api.spot.ping() method.

There are several ways to handle the API requests:

  • Global async function
  • Global callback function
  • Stream specific async function
  • Stream specific callback function
  • Request specific callback function
  • Save answer in variable
  • Using the stream_buffer
  • Multiple API Streams

Global async function

When receiving the response from Binance, the function handle_socket_message() is executed which we passed when initiating BinanceWebSocketApiManager(), and this is where you can hook your code.

In our example, we simply output the received data:

https://gist.github.com/oliver-zehentleitner/576ab4518ecbca583c3eec209533def1

Output:

Received data:
{"id":"1cc8812a0c6b-08aa-6098-2742-ac0cedc8","status":200,"result":{},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200,"count":2}]}

I will use the example with ubwa.api.spot.ping() to introduce the other methods how you can receive and process the received data.

Global callback function

When receiving the response from Binance, the function handle_socket_message() is executed which we passed when initiating BinanceWebSocketApiManager(), and this is where you can hook your code.

In our example, we simply output the received data:

https://gist.github.com/oliver-zehentleitner/576ab4518ecbca583c3eec209533def1

Output:

Received data:
{"id":"1cc8812a0c6b-08aa-6098-2742-ac0cedc8","status":200,"result":{},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200,"count":2}]}

Stream specific callback function

It is possible to pass a callback function to ubwa.create_stream() as well. Then, for receiving responses from this stream, the stream specific callback function is used instead of the global callback function we passed to BinanceWebSocketApiManager().

https://gist.github.com/oliver-zehentleitner/461a06fef1d809dd104e241599ab0f20

Now we can run ping again and this time we would use the callback function handle_stream_message() instead of handle_socket_message().

https://gist.github.com/oliver-zehentleitner/576ab4518ecbca583c3eec209533def1

Output:

Received stream data:
{"id":"1cc8812a0c6b-08aa-6098-2742-ac0cedc8","status":200,"result":{},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200,"count":2}]}

Request specific callback function

Since not all types of data are treated the same way, there is also the possibility to process responses of a specific request with a specific callback function.

https://gist.github.com/oliver-zehentleitner/863f346969bbbe315ab808ca6e658013

Output:

Received ping response:
{"id":"1cc8812a0c6b-08aa-6098-2742-ac0cedc8","status":200,"result":{},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200,"count":2}]}

Save answer in variable

There is also the possibility to let the called function wait until the response to the request has arrived and then store it directly into a variable. The disadvantage of this method is that the function becomes blocking, whereby several requests can only be processed sequentially.

https://gist.github.com/oliver-zehentleitner/57e1a32959b2019c62e0254f2ad19726

Output:

Awaited ping response:
{"id":"1cc8812a0c6b-08aa-6098-2742-ac0cedc8","status":200,"result":{},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200,"count":2}]}

Using the stream_buffer

A completely different approach than the callback functions is the stream_buffer. This is the standard way of BinanceWebSocketApiManager() to process received data, here all received data is stored in a deque() stack and can be used as FIFO as well as LIFO stack to read the received data in a loop.

The main advantage of this solution is the desynchronization between receiving and processing the new data.

What does this mean? UBWA receives the new data in an AsyncIO event loop and processes them asynchronously. This means that the data is not received and processed sequentially, but that the callback functions within the event loop are started in parallel for each data record received, which can easily push your system to its limits during peak load times, e.g. if you receive more data faster than you can store in your database over a longer period for speed reasons.

Because each received record is simply stored in the stream_buffer, the processing is immediately finished for UBWA. You can easily access the new data in the stream_buffer in another thread or better another process and adjust the processing time to your circumstances. Additionally the stream_buffer can be monitored by you and offers further options.

As already mentioned the stream_buffer is the standard method of UBWA and is simply overwritten by setting the callback functions. So if you want to use it, do not pass a callback function in the class initialization of BinanceWebSocketApiManager() or anywhere else.

One way to use callback functions everywhere and still use the stream_buffer in a special situation is to explicitly pass ubwa.add_to_stream_buffer as callback function.

Of course it is also possible to use different stream_buffer, how to do that is described in the wiki.

I now choose a simple example with only one global stream_buffer. If you have now simply omitted passing the callback functions, the global stream_buffer is activated by default and your stream will write its data there.

Hereby you get access to the oldest record in the stack:

https://gist.github.com/oliver-zehentleitner/96d5789f65761f08069b25229ec83af9

Note: If the stream_buffer is empty, ubwa.pop_stream_data_from_stream_buffer() returns the boolean value False!

For example, if you have problems with your database, you can catch the exception of the database connection in a try block and use ubwa.add_to_stream_buffer() in the except block to save the record back into the stream_buffer and simply retrieve it later. Unfortunately, this messes up the chronological sorting! ๐Ÿคจ

https://gist.github.com/oliver-zehentleitner/241061d809c76ac1b08013a9ee40386c

Multiple API streams

If there is more than one API stream (with api=True), the methods must be told which stream to use. The stream_id is used to uniquely identify the streams and is returned by ubwa.create_stream(), alternatively you can also work with a stream_label. For this to work, a stream_label must also be defined when creating the stream with ubwa.create_stream().

Example with stream_id:

https://gist.github.com/oliver-zehentleitner/7fc0c1ed65fa96bd9e65f3173143c17d

Example with stream_label:

https://gist.github.com/oliver-zehentleitner/b2634fd6c45574b460c3fde8414f278c


5. Available methods for API requests

Note: Actions executed via WebSocket are subject to the same filters and rate restrictions as when executed via the REST API, and are also functionally equivalent: they provide the same functionality, accept the same parameters, and return the same status and error codes.

Each API request has its own list of accepted and mandatory parameters, gives different responses, and is subject to different request limits. To understand what is happening in the background, it is always advisable to also study the official documentation of Unicorn Binance WebSocket API and the Binance WebSocket API.

  • ubwa.api.spot.cancel_order()
  • ubwa.api.spot.create_order()

Cancel open orders

Cancel all open orders on a symbol, including OCO orders.

Docs: UBWA, Binance

If you cancel an order that is a part of an OCO pair, the entire OCO is canceled.

https://gist.github.com/oliver-zehentleitner/1ca32c20ff397e91f3466f788a718a6e

Output:

{"id":"d37149a608c4-7951-fbbb-33f6-93d68e2d","status":200,"result":{"symbol":"BUSDUSDT","origClientOrderId":"1","orderId":942396461,"orderListId":-1,"clientOrderId":"rkAnxL9oEZr7wzKaCMiAuQ","price":"1.00000000","origQty":"15.00000000","executedQty":"0.00000000","cummulativeQuoteQty":"0.00000000","status":"CANCELED","timeInForce":"GTC","type":"LIMIT","side":"SELL","selfTradePreventionMode":"NONE"},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200,"count":3}]}

Cancel an order

Cancel an active order.

Docs: UBWA, Binance

If you cancel an order that is a part of an OCO pair, the entire OCO is canceled.

https://gist.github.com/oliver-zehentleitner/1ca32c20ff397e91f3466f788a718a6e

Output:

{"id":"d37149a608c4-7951-fbbb-33f6-93d68e2d","status":200,"result":{"symbol":"BUSDUSDT","origClientOrderId":"1","orderId":942396461,"orderListId":-1,"clientOrderId":"rkAnxL9oEZr7wzKaCMiAuQ","price":"1.00000000","origQty":"15.00000000","executedQty":"0.00000000","cummulativeQuoteQty":"0.00000000","status":"CANCELED","timeInForce":"GTC","type":"LIMIT","side":"SELL","selfTradePreventionMode":"NONE"},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200,"count":3}]}

Create an order

Create a new order.

Docs: UBWA, Binance

ubwa.api.spot.create_order() automatically creates a client_order_id and returns it. This way you can always uniquely identify the order in further steps.

https://gist.github.com/oliver-zehentleitner/adf377f0c26740e7195d45d5f6bf93b3

Output:

{"id":"4db8f58ee69e-d597-fb26-8551-786707fa","status":200,"result":{"symbol":"BUSDUSDT","orderId":942394911,"orderListId":-1,"clientOrderId":"1","transactTime":1680911574690,"price":"1.00000000","origQty":"15.00000000","executedQty":"0.00000000","cummulativeQuoteQty":"0.00000000","status":"NEW","timeInForce":"GTC","type":"LIMIT","side":"SELL","workingTime":1680911574690,"fills":[],"selfTradePreventionMode":"NONE"},"rateLimits":[{"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":10,"limit":50,"count":1},{"rateLimitType":"ORDERS","interval":"DAY","intervalNum":1,"limit":160000,"count":20},{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200,"count":4}]}

6. Further information

There are many other functions to send requests to the Binance API.

If you find bugs or have suggestions for improving the API implementation, you can open an issue via GitHub.

For more information please read the documentation for unicorn-binance-websocket-api.


I hope you found this tutorial informative and enjoyable! Follow me on GitHub and LinkedIn to stay updated on my latest releases. Your constructive feedback is always appreciated!

41 views

UNICORN Binance Suite

Part 1 of 6

Open source Python libraries for automated trading on Binance โ€” WebSocket streams, REST API, local order books, trailing stop loss, and Kubernetes-scale depth caching. Engineering decisions, real-world findings, and lessons from production.

Up next

Restful Binance Requests in Python with UNICORN Binance REST API

In this article I will show you an easy way to get started with the Binance REST API using Python. We use the unicorn-binance-rest-api package: PyPI: https://pypi.org/project/unicorn-binance-rest-api