This post is a step-by-step tutorial on how to receive real-time price data from the Binance exchange using the WebSocket protocol. The price data can then be used to quickly make buying or selling decisions. This functionality can be used as part of a trading bot for crypto or as part of a larger trading system.
What is Binance?
Binance is the largest exchange in the world in terms of daily trading volume of cryptocurrencies. It was founded in 2017.
What is the Binance API?
Instead of just a single Binance API, there is actually a collection of APIs for different purposes, e.g. test/production, country and asset type.
For each of these, a collection of Binance APIs exist in the form of REST APIs and Web socket interfaces.
There are Binance APIs for Binance (+Testnet), Binance Margin (+Testnet), Binance Isolated Margin (+Testnet), Binance Futures (+Testnet), Binance COIN-M Futures, Binance US, Binance TR, Binance JEX, Binance DEX and Binance DEX Testnet.
The Binance APIs provides access to all major Binance functionality, such as:
-
Authentication
-
Wallets
-
Exchange info
-
Orders
-
Market Data
-
User Data
-
Filters.
What is a WebSocket Interface?
WebSockets are a network technology that makes it possible to open a two-way interactive communication session between the user's browser and a server. This is useful for example to get real-time price updates for cryptocurrencies from the Binance exchange.
The Binance Websocket interface provides access to public data streams (such as all public streams like trade, kline, ticker, depth, bookTicker, forceOrder, compositeIndex and blockheight and also all private user data streams) and private user data streams (e.g. account information).
One can set listeners to these data streams to start receiving updates and stop listening to end receiving updates.
Python Libraries Used
Before we get into the actual implementation, I wanted to go over the two Python libraries we will use in this implementation: Python-Binance and Unicorn Binance WebSocket API. The former is not actually needed to receive price data streams but it provides access to all major other APIs so you will definitely need it.
Python-Binance
The Python Binance library is a MIT-licensed library developed by Sam Mchardy. The library lets you instantiate a client that provides access to all the Binance test- and production APIs such as General, Market and Account APIs. You can find the documentation here on GitHub.
Unicorn Binance WebSocket API
The Unicorn Binance WebSocket API by Lucit Systems provides a convenient wrapper implementation to access Binance WebSockets. You can find the GitHub repository here.
This library provides access to the following Binance WebSocket APIS: Binance (+Testnet), Binance Margin (+Testnet), Binance Isolated Margin (+Testnet), Binance Futures (+Testnet), Binance COIN-M Futures, Binance US, Binance TR, Binance JEX, Binance DEX and Binance DEX Testnet.
It also supports the streaming of all public streams like trade, kline, ticker, depth, bookTicker, forceOrder, compositeIndex and blockheight and also all private userData streams.
I chose this library because it offers a lot of connectivity handling and convenience functionality right out of the box, such as:
-
Async stream processing
-
Provides convenient subscribe/unsubscribe functions
-
Reconnection handling in case the connection is lost according to Binance guidelines, which protects from bans.
-
Support for multiple private user streams
-
Helper functions for managing WebSockets, e.g. get API status, get speed.
Implementation
You can find the complete implementation of this part on my GitHub repository here.
First, here the required Python imports. You may have to install them in your environment, if you haven’t already installed them.
import json
import threading
import time
from binance import Client
from pprint import pformat
from unicorn_binance_websocket_api.manager import BinanceWebSocketApiManager
The next section defines the test or production API keys for the Binance exchange you are working with. In order to get the API key, you will need to create a Binance account by registering with the Binance Website. For me, for example, that was https://www.binance.us.
You can find the Binance API endpoint for the exchange you are working with in the Binance documentation. Here for example, the docs for Binance.us.
TEST_API_KEY = '<your API Key>'
TEST_API_SECRET = '<your API secret>'
base_endpoint = 'https://api.binance.us' # Select the Binance API endpoint for your exchange
The next block is just a util function to check if the message from Binance is empty.
def is_empty_message(message):
if message is False:
return True
if '"result":null' in message:
return True
if '"result":None' in message:
return True
return False
The next function handles price changes. This is the integration point where you want to add your code for evaluating the price change, e.g. for generating buy or sell signals.
def handle_price_change(symbol, timestamp, price):
print(f"Handle price change for symbol: {symbol}, timestamp: {timestamp}, price: {price}")
The next function pops the last message from the data stream buffer and converts it to JSON and then calls the handle_price_change() function.
def process_stream_data(binance_websocket_api_manager):
while True:
if binance_websocket_api_manager.is_manager_stopping():
exit(0)
oldest_data = binance_websocket_api_manager.pop_stream_data_from_stream_buffer()
is_empty = is_empty_message(oldest_data)
if is_empty:
time.sleep(0.01)
else:
oldest_data_dict = json.loads(oldest_data)
data = oldest_data_dict['data']
# Handle price change
handle_price_change(symbol=data['s'], timestamp=data['T'], price=data['p'])
The next function establishes a web socket manager connection using the Unicorn BinanceWebSocketApiManager. As you can see, this section also sets the exchange, the streams and symbols to listen to and sets the API keys for authentication.
Then it starts a new thread to listen to changes and passes the process_stream_data() function to handle events.
def start_websocket_listener():
binance_us_websocket_api_manager = BinanceWebSocketApiManager(exchange="binance.us")
channels = {'trade', }
binance_us_websocket_api_manager.create_stream(channels, markets=lc_symbols, api_key=TEST_API_KEY, api_secret=TEST_API_SECRET)
# Start a worker process to move the received stream_data from the stream_buffer to a print function
worker_thread = threading.Thread(target=process_stream_data, args=(binance_us_websocket_api_manager,))
worker_thread.start()
The next function is used to compare Binance server time with your local computer’s time. In case they are out of sync, you will get an error message. So I recommend setting your computer’s date/time to auto sync with an internet time provider.
def compare_server_times():
server_time = client.get_server_time()
aa = str(server_time)
bb = aa.replace("{'serverTime': ", "")
aa = bb.replace("}", "")
gg = int(aa)
ff = gg - 10799260
uu = ff / 1000
yy = int(uu)
tt = time.localtime(yy)
print(f"Binance Server time: {tt}")
print(f"Local time: {time.localtime()}")
The next two functions are used to get the supported symbols for the exchange and exchange info using the Binance client. They are not needed, I just added them to show some of the functionality of the Binance client.
def get_traded_symbols():
symbols = []
exchange_info = client.get_exchange_info()
for s in exchange_info['symbols']:
symbols.append(s['symbol'])
return symbols
def get_exchange_info():
info = client.get_exchange_info()
print(pformat(info))
tickers = client.get_all_tickers()
print(pformat(tickers))
This block is basically the main() function in which we are defining the crypto symbols we want to get price updates for, then initializing the Binance client, calling some Binance client APIs to get exchange info and finally we are starting the WebSocket listener to get price updates.
# Define symbols
symbols = ['ETHUSDT', 'BTCUSDT', 'LTCUSDT',]
lc_symbols = []
for symbol in symbols:
lc_symbols.append(symbol.lower())
# Initialize binance client
client = Client(api_key=TEST_API_KEY, api_secret=TEST_API_SECRET, testnet=True, tld='us')
# Compare server and local times
compare_server_times()
# Get traded symbols
traded_symbols = get_traded_symbols()
print("Traded symbols: ", traded_symbols)
start_websocket_listener()
Output:
Handle price changes here for symbol: ETHUSDT, timestamp: 1652100656516, price: 2411.49000000
Handle price changes here for symbol: BTCUSDT, timestamp: 1652100666815, price: 33026.23000000
Handle price changes here for symbol: ETHUSDT, timestamp: 1652100669875, price: 2410.87000000
And that’s it. Now you can get real-time crypto price updates and process them in your bot or trading system.
Wrapping Up
In this post we reviewed what the Binance exchange is, we discussed Binance APIs and WebSocket APIs and went over the implementation of how to use the Binance API client and Unicorn WebSocket library to get real-time price updates.
You can find the complete implementation on my GitHub repository here.
I hope that this post was useful for you.
Happy trading!
