├── README.md ├── check_order_update_latency.py ├── list_break_symbols.py ├── manage_local_order_book.py ├── multi_subs_market_ws.py └── place_order.py /README.md: -------------------------------------------------------------------------------- 1 | # Binance Toolbox Python 2 | 3 | ## Preparation 4 | 1. It's required to install the `binance-connector` package 5 | 6 | ```shell 7 | pip install binance-connector 8 | ``` 9 | 10 | 2. Some api endpoints requires sending a valid API-Key and signature, so in order for some scripts to work, please set up 11 | your account's api key pair as environment variables. 12 | 13 | Note: By default, the scripts uses Testnet's REST and Websocket as base urls. 14 | 15 | ```shell 16 | export BINANCE_API_KEY= 17 | export BINANCE_API_SECRET= 18 | ``` 19 | 20 | ## License 21 | MIT 22 | -------------------------------------------------------------------------------- /check_order_update_latency.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Incrementally creates order every second while listening to the account's websocket to check the 5 | Transaction vs Event time (T vs E) and Event vs Received time (E vs Now) for every received order update. 6 | Processes ends when 5 orders is reached and all open orders are canceled. 7 | 8 | T - time the transaction happened 9 | E - time the payload was created 10 | Now - time the payload was received locally 11 | 12 | Instructions: 13 | 1. Have binance-connector installed 14 | 2. Set up your account's api key as BINANCE_API_KEY environment variable 15 | 3. Set up your account's api secret key as BINANCE_API_SECRET environment variable 16 | 4. Define symbol in this file and adjust other fields if needed; 17 | 5. python check_order_update_latency.py 18 | 19 | """ 20 | 21 | import asyncio 22 | import logging 23 | from binance.lib.utils import config_logging, get_timestamp 24 | from binance.spot import Spot as Client 25 | from binance.websocket.spot.websocket_stream import SpotWebsocketStreamClient 26 | import json 27 | import os 28 | 29 | 30 | config_logging(logging, logging.INFO) 31 | 32 | # Testnet 33 | client = Client( 34 | os.getenv('BINANCE_API_KEY'), 35 | os.getenv('BINANCE_API_SECRET'), 36 | base_url="https://testnet.binance.vision", 37 | ) 38 | ws_client = SpotWebsocketStreamClient(stream_url="wss://testnet.binance.vision/ws") 39 | 40 | symbol = "" # Example: BNBUSDT 41 | 42 | 43 | def message_handler(message): 44 | if "executionReport" in json.dumps(message): 45 | print( 46 | f"Time diff >> " 47 | f"Order id:{message['data']['i']}, " 48 | f"Execution type:{message['data']['x']}, " 49 | f"TvsE: {message['data']['E'] - message['data']['T']} ms, " 50 | f"EvsNow: {get_timestamp() - message['data']['E']} ms" 51 | ) 52 | 53 | 54 | def get_parameters(): 55 | 56 | quantity = 1 57 | price = float(client.ticker_price(symbol)["price"]) 58 | 59 | params = { 60 | "symbol": symbol, 61 | "side": "SELL", 62 | "timeInForce": "GTC", 63 | "type": "STOP_LOSS_LIMIT", 64 | "quantity": quantity, 65 | "price": price, 66 | "stopPrice": price - 1, 67 | } 68 | return params 69 | 70 | 71 | async def listen_ws(): 72 | 73 | response = client.new_listen_key() 74 | logging.info("Starting ws connection") 75 | ws_client.user_data( 76 | listen_key=response["listenKey"], 77 | id=1, 78 | callback=message_handler, 79 | ) 80 | 81 | 82 | async def create_orders(): 83 | 84 | # Time to finalise websocket connection set up 85 | await asyncio.sleep(1) 86 | 87 | order_counter = 1 88 | max_orders = 5 # Optional 89 | 90 | while order_counter <= max_orders: 91 | params = get_parameters() 92 | response = client.new_order(**params) 93 | logging.info(f"Created order id {response['orderId']} with params: {params})") 94 | await asyncio.sleep(1) 95 | order_counter += 1 96 | 97 | # Cancel all open orders 98 | client.cancel_open_orders(symbol) 99 | logging.info("closing ws connection") 100 | ws_client.stop() 101 | 102 | 103 | async def main(): 104 | await asyncio.gather(listen_ws(), create_orders()) 105 | 106 | 107 | asyncio.run(main()) 108 | -------------------------------------------------------------------------------- /list_break_symbols.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Lists all symbols in production that's with BREAK status 5 | 6 | Instructions: 7 | python list_break_symbols.py 8 | """ 9 | 10 | from binance.spot import Spot as Client 11 | 12 | # For production 13 | client = Client() 14 | 15 | response = client.exchange_info() 16 | 17 | for symbol in response['symbols']: 18 | s = symbol['symbol'] 19 | if symbol['status'] == 'BREAK': 20 | print(s) 21 | -------------------------------------------------------------------------------- /manage_local_order_book.py: -------------------------------------------------------------------------------- 1 | """ 2 | Manages a local order book and prints out its best prices. 3 | Based on https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#diff-depth-stream 4 | 5 | Instructions: 6 | 1. Have binance-connector installed 7 | 2. Define symbol in this file and adjust other fields if needed 8 | 3. python manage_local_order_book.py 9 | 10 | """ 11 | 12 | from binance.spot import Spot as Client 13 | from binance.websocket.spot.websocket_stream import SpotWebsocketStreamClient 14 | from binance.lib.utils import config_logging 15 | import os 16 | import logging 17 | import json 18 | import asyncio 19 | 20 | 21 | config_logging(logging, logging.INFO) 22 | 23 | symbol = 'BNBUSDT' # Example: BNBUSDT 24 | base_url = 'https://testnet.binance.vision' 25 | stream_url = 'wss://testnet.binance.vision/ws' 26 | 27 | client = Client(base_url=base_url) 28 | 29 | 30 | order_book = { 31 | "lastUpdateId": 0, 32 | "bids": [], 33 | "asks": [] 34 | } 35 | 36 | 37 | def get_snapshot(): 38 | """ 39 | Retrieve order book 40 | """ 41 | 42 | return client.depth(symbol, limit=1000) 43 | 44 | 45 | def manage_order_book(side, update): 46 | """ 47 | Updates local order book's bid or ask lists based on the received update ([price, quantity]) 48 | """ 49 | 50 | price, quantity = update 51 | 52 | # price exists: remove or update local order 53 | for i in range(0, len(order_book[side])): 54 | if price == order_book[side][i][0]: 55 | # quantity is 0: remove 56 | if float(quantity) == 0: 57 | order_book[side].pop(i) 58 | return 59 | else: 60 | # quantity is not 0: update the order with new quantity 61 | order_book[side][i] = update 62 | return 63 | 64 | # price not found: add new order 65 | if float(quantity) != 0: 66 | order_book[side].append(update) 67 | if side == 'asks': 68 | order_book[side] = sorted(order_book[side], key=lambda order: float(order[0])) # asks prices in ascendant order 69 | else: 70 | order_book[side] = sorted(order_book[side], key=lambda order: float(order[0]), reverse=True) # bids prices in descendant order 71 | 72 | # maintain side depth <= 1000 73 | if len(order_book[side]) > 1000: 74 | order_book[side].pop(len(order_book[side]) - 1) 75 | 76 | def process_updates(message): 77 | """ 78 | Updates bid and ask orders in the local order book. 79 | """ 80 | 81 | for update in message['b']: 82 | manage_order_book('bids', update) 83 | for update in message['a']: 84 | manage_order_book('asks', update) 85 | # logging.info("Condition 'U' <= last_update_id + 1 <= 'u' matched! Process diff update") 86 | 87 | 88 | def message_handler(_, message): 89 | """ 90 | Syncs local order book with depthUpdate message's u (Final update ID in event) and U (First update ID in event). 91 | If synced, then the message will be processed. 92 | """ 93 | 94 | global order_book 95 | 96 | json_data = json.loads(message) 97 | 98 | depth_update_event = json_data.get('e') 99 | 100 | if depth_update_event == "depthUpdate": 101 | last_update_id = order_book['lastUpdateId'] 102 | 103 | # logging.info('LastUpdateId:' + str(last_update_id)) 104 | # logging.info('Message U:' + str(message['U']) + ' u:' + str(message['u'])) 105 | 106 | if json_data['u'] <= last_update_id: 107 | return # Not an update, wait for next message 108 | if json_data['U'] <= last_update_id + 1 <= json_data['u']: # U <= lastUpdateId+1 AND u >= lastUpdateId+1. 109 | order_book['lastUpdateId'] = json_data['u'] 110 | process_updates(json_data) 111 | else: 112 | logging.info('Out of sync, re-syncing...') 113 | order_book = get_snapshot() 114 | 115 | async def listen_ws(): 116 | """ 117 | Listens to the ws to get the updates messages. 118 | """ 119 | ws_client = SpotWebsocketStreamClient(stream_url=stream_url, on_message=message_handler) 120 | 121 | ws_client.diff_book_depth( 122 | symbol=symbol.lower(), 123 | id=1, 124 | speed=1000 125 | ) 126 | 127 | 128 | async def get_best_price(): 129 | """ 130 | Gets best available prices (for bid and ask) every second 131 | """ 132 | 133 | while True: 134 | if order_book.get('lastUpdateId') > 0: 135 | print(f'Best prices -> bid:{order_book["bids"][0][0]} , ask:{order_book["asks"][0][0]}') 136 | await asyncio.sleep(1) 137 | 138 | 139 | def main(): 140 | loop = asyncio.get_event_loop() 141 | loop.run_until_complete(asyncio.gather(listen_ws(), get_best_price())) 142 | loop.close() 143 | 144 | main() 145 | -------------------------------------------------------------------------------- /multi_subs_market_ws.py: -------------------------------------------------------------------------------- 1 | """ 2 | Subscribes to 2 market streams and prints out their updates with associated symbols. 3 | 4 | Instructions: 5 | 1. Have binance-connector installed 6 | 2. Define symbol_1 and symbol_2 in this file and adjust other stream subscription fields if needed 7 | 3. python multi_subs_market_ws.py 8 | """ 9 | 10 | import asyncio 11 | import logging 12 | import time 13 | 14 | from binance.lib.utils import config_logging 15 | from binance.websocket.spot.websocket_stream import SpotWebsocketStreamClient 16 | 17 | symbol_1 = 'btcusdt' 18 | symbol_2 = 'ethusdt' 19 | 20 | config_logging(logging, logging.DEBUG) 21 | 22 | def msg_handler(_, message): 23 | logging.info(f"{message}\n") 24 | 25 | my_client = SpotWebsocketStreamClient( 26 | on_message=msg_handler, 27 | is_combined=True, 28 | ) 29 | 30 | async def monitor(): 31 | """Subscribes to 2 partial book depth market streams for 2 symbols: 32 | https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams#partial-book-depth-streams 33 | """ 34 | my_client.partial_book_depth(symbol=f"{symbol_1}", level=5, speed=1000) 35 | my_client.partial_book_depth(symbol=f"{symbol_2}", level=5, speed=1000) 36 | time.sleep(10) 37 | my_client.stop() 38 | 39 | 40 | def run(): 41 | asyncio.run(monitor()) 42 | 43 | 44 | run() 45 | -------------------------------------------------------------------------------- /place_order.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | A simple script to place an order and cancel it every second. 5 | Feel free to adjust the parameters or change from Testnet to Production setting. 6 | 7 | Instructions: 8 | 1. Have binance-connector installed 9 | 2. Set up your account's api key as BINANCE_API_KEY environment variable 10 | 3. Set up your account's api secret key as BINANCE_API_SECRET environment variable 11 | 4. Define symbol in this file and adjust other fields if needed 12 | 5. python place_order.py 13 | 14 | Note: 15 | Make sure to respect exchangeInfo endpoint's filters for price and quantity: 16 | https://developers.binance.com/docs/binance-spot-api-docs/filters 17 | 18 | """ 19 | 20 | import os 21 | import time 22 | from binance.spot import Spot as Client 23 | 24 | 25 | key = os.getenv('BINANCE_API_KEY') 26 | secret = os.getenv('BINANCE_API_SECRET') 27 | 28 | # For Testnet 29 | client = Client(key, secret, base_url='https://testnet.binance.vision') 30 | 31 | # # For Production 32 | # client = Client(key, secret) 33 | 34 | symbol = '' # Example: BNBUSDT 35 | 36 | while True: 37 | # Create an order and cancel the same order every second 38 | params = { 39 | 'symbol': symbol, 40 | 'side': 'BUY', 41 | 'type': 'LIMIT_MAKER', 42 | 'quantity': 10, 43 | 'price': 300 44 | } 45 | 46 | response = client.new_order(**params) 47 | print("Created order ID: {}".format(response['orderId'])) 48 | 49 | # Cancel the order 50 | response = client.cancel_order(symbol, orderId=response['orderId']) 51 | print("Canceled order ID: {}".format(response['orderId'])) 52 | 53 | time.sleep(1) 54 | --------------------------------------------------------------------------------