Package: binance-sdk
binance-sdkbinance-sdkis an another unofficial Binance SDK for python 3.7+, which:Based onBinance Official API Docs v3.Uses Binance's new websocket stream which supports live pub/sub so that we only needONEwebsocket connection.Has an optionalpandas.DataFramesupport. Ifpandasis installed, columns of all stream data frames are renamed for readability.Based on pythonasync/awaitManages the order book for you (handled byOrderBookHandlerBase), so that you need not to worry about websocket reconnection and message losses. For details, see the sectionOrderBookHandlerBaseSupports to change API endpoints, so that we could use faster API hosts.Install# Without pandas supportpipinstallbinance-sdkor# With pandas supportpipinstallbinance-sdk[pandas]Basic Usage#!/usr/bin/env pythonimportasynciofrombinanceimportClientclient=Client()asyncdefmain():print(awaitclient.get_exchange_info())loop=asyncio.get_event_loop()loop.run_until_complete(main())Handling messagesBinance-sdk provides handler-based APIs to handle all websocket messages, and you are able to not worry about websockets.#!/usr/bin/env pythonfrombinanceimportClient,TickerHandlerBase,SubTypeclient=Client(api_key)asyncdefmain():# Implement your own TickerHandler.classTickerPrinter(TickerHandlerBase):asyncdefreceive(self,payload):"""The function to receive ticker streams.The function could either be sync or asyncArgs:payload (dict): the raw stream payload which ismessage['data'] of the original stream message"""# If binance-sdk is installed with pandas support, then#   `ticker` will be a `DataFrame` with columns renamed# Otherwise, it is unnecessary to call `super().receive`.ticker_df=super().receive(payload)# Just print the tickerprint(ticker_df)# Register the handler for `SubType.TICKER`client.handler(TickerPrinter())# Subscribe to ticker change for symbol BTCUSDTawaitclient.subscribe(SubType.TICKER,'BTCUSDT')loop=asyncio.get_event_loop()loop.run_until_complete(main())# Run the loop forever to keep receiving messagesloop.run_forever()# It prints a pandas.DataFrame for each message#    type        event_time     symbol   open            high            low            ...# 0  24hrTicker  1581597461196  BTCUSDT  10328.26000000  10491.00000000  10080.00000000 ...# ...(to be continued)Subscribe to more symbol pairs and types# This will subscribe to# - bnbusdt@aggTrade# - bnbusdt@depth# - bnbbtc@aggTrade# - bnbbtc@depthawaitclient.subscribe(# We could also subscribe multiple types#   for both `BNBUSDT` and 'BNBBTC'[SubType.AGG_TRADE,SubType.ORDER_BOOK],# We could subscribe more than one symbol pairs at a time[# Which is equivalent to `BNBUSDT`'BNB_USDT','BNBBTC'])And since we subscribe toTHREEnew types of messages, we need to set the handlers each of which shouldisinstance()of one ofTradeHandlerBaseAggTradeHandlerBaseOrderBookHandlerBaseKlineHandlerBaseMiniTickerHandlerBaseTickerHandlerBaseAllMarketMiniTickersHandlerBaseAllMarketTickersHandlerBaseAccountInfoHandlerBaseAccountPositionHandlerBaseBalanceUpdateHandlerBaseOrderUpdateHandlerBaseOrderListStatusHandlerBaseHandlerExceptionHandlerBasea special handler to handle stream exceptionsclient.handler(MyTradeHandler(),MyOrderBookHandler(),MyKlineHandler())Subscribe to user streams# Before subscribe to user stream, you need to provide `api_secret` (and also `api_key`)client.secret(api_secret)# Or, you should provide `api_secret` when initialize the client# ```# client = Client(api_key, api_secret)# ```# binance-sdk will handle user listen key internally without your concernawaitclient.subscribe(SubType.USER)Subscribe to handler exceptionsBinance-sdkreceives stream messages in background tasks, so sometimes it is difficult to detect the exceptions raised inreceivefunction of user handlers.Fortunately, we could useHandlerExceptionHandlerBasefrombinanceimport(HandlerExceptionHandlerBase,KlineHandlerBase)classKlineHandler(KlineHandlerBase):defreceive(self,payload):raiseRuntimeError('this will ruin my day')classHandlerExceptionHandler(HandlerExceptionHandlerBase):asyncdefreceive(self,exception):# By calling `super().receive(exception)`,# it will print the error stack.super().receive(exception)awaitsend_to_monitor(exception)client.handler(KlineHandler())client.handler(HandlerExceptionHandler())If you just want to print error stacks, we could:client.handler(HandlerExceptionHandlerBase())APIsClient(**kwargs)All arguments of the constructor Client are keyworded arguments and all optional.api_key?str=Nonebinance api keyapi_secret?str=Nonebinance api secretrequest_params?dict=Noneglobal request params for aiohttpstream_retry_policy?Callable[[int, Exception], Tuple[bool, int, bool]]retry policy for websocket stream. For details, seeRetryPolicystream_timeout?int=5seconds util the stream reach an timeout errorapi_host?str='https://api.binance.com'to specify another API host for rest API requests. 这个参数的存在意义，使用方法，不累述，你懂的。stream_host?str='wss://stream.binance.com'to specify another stream host for websocket connections.Create a binance client.Each API method accepts only keyworded arguments (kwargs) and has verbosed Python doc strings (Google style) which you could check out when you are coding.The following example shows how to create a new order.frombinanceimport(OrderSide,OrderType,TimeInForce)# All arguments are keyworded arguments.awaitclient.create_order(symbol='BTCUSDT',# You could use string `BUY` (NOT recommended) instead of# the built-in enum types of Binance-sdk.# But it is a good practise to use enums which could help# us to avoid spelling mistakes, and save our money.side=OrderSide.BUY,type=OrderType.LIMIT,timeInForce=TimeInForce.GTC,# Binance-sdk will not handle Decimals for you,# so you'd better to know how to deal with python float precisions.# Or you could use string-type quantity.quantity=10.,# It is better to use string type instead of float.# The same as `quantity`price='7000.1')client.key(api_key) -> selfDefine or change api key. This method is unnecessary if we only request APIs ofSecurityType.NONEclient.secret(api_secret) -> selfDefine or change api secret, especially when we have not define api secret inClientconstructor.api_secretis not always required for using binance-sdk. SeeEndpoint security typeawait client.get(uri, **kwargs)await client.post(uri, **kwargs)await client.put(uri, **kwargs)await client.delete(uri, **kwargs)uristrthe request urlsecurity_type?SecurityTypeendpoint security type. Defaults toSecurityType.NONE.Send a GET/POST/PUT/DELETE HTTPs request.await client.subscribe(subtype, *subtype_params) -> Noneawait client.subscribe(*subscriptions) -> Nonesubtypestrsubscription type, should be one ofSubType.*s. For details, seeSubTypesubtype_paramsListparams for a certainsubtypesubscriptionsList[Tuple]a pack of subscriptions each of which is a tuple ofsubtypeand*subtype_params.Subscribe to a stream or multiple streams. If no websocket connection is made up,client.subscribewill also create a websocket connection.frombinanceimportSubType,KlineIntervalawaitclient.subscribe(SubType.TICKER,'BNBUSDT')# SubType.ALL_MARKET_MINI_TICKERS with default paramawaitclient.subscribe(SubType.ALL_MARKET_MINI_TICKERS)# SubType.ALL_MARKET_MINI_TICKERS with update interval 3000msawaitclient.subscribe(SubType.ALL_MARKET_MINI_TICKERS,3000)# Subcribe to multiple typesawaitclient.subscribe((SubType.KLINE,'BTC_USDT',KlineInterval.DAY),(SubType.TICKER,'BNBUSDT'),([SubType.ORDER_BOOK,SubType.TRADE],['BNBUSDT','BTCUSDT']),(SubType.ALL_MARKET_MINI_TICKERS,)# <-- PAY ATTENTION to the `,` here)Possible exceptions:InvalidSubParamsExceptionUnsupportedSubTypeExceptionInvalidSubTypeParamExceptionStreamAbandonedExceptionclient.start() -> selfStart receiving streamsclient.stop() -> selfStop receiving streamsawait client.close(code=4999) -> Nonecodeint=4999the custom close code for websocket. It should be in therange 4000 - 4999Close stream connection, clear all stream subscriptions and clear all handlers.client.handler(*handlers) -> selfhandlersList[Union[HandlerExceptionHandlerBase,TradeHandlerBase,...]]Register message handlers for streams. If we've subscribed to a stream of a certainsubtypewith no corresponding handler provided, the messages ofsubtypewill not be handled.Except forHandlerExceptionHandlerBase, handlers each of whose name ends withBaseshould be inherited before use.Typically, we need to override thedef receive(self, payload)method.classMyTradeHandler(TradeHandlerBase):asyncdefreceive(self,payload):# If pandas is installed, then `payload` is a `pandas.DataFrame`,#   otherwise is a dict.df=super().receive(payload)# If you don't want the `pandas.DataFrame`, use `payload` directlyawaitsaveTrade(df)client.handler(MyTradeHandler())We could also register multiple handlers at one timeclient.handler(MyTradeHandler(),MyTickerHandler())If we register an invalid handler, anInvalidHandlerExceptionexception will be raised.SubTypeIn this section, we will note the parameters for eachsubtypesSubTypewith parameterssymbolandintervalSubType.KLINEAndintervalshould be one of theKlineIntervalenumerablesSubTypes with a paramsymbolSubType.TRADESubType.AGG_TRADESubType.MINI_TICKERSubType.TICKERSubType.ORDER_BOOKSubTypes with an optional paramupdateInterval=1000(ms)SubType.ALL_MARKET_MINI_TICKERSSubType.ALL_MARKET_TICKERSSubtypewith no paramSubType.USERRetryPolicyRetry policy is used by binance-sdk to determine what to do next after the client fails to do some certain thing.abandon,delay=stream_retry_policy(info)# `info.fails` is the counter number of#   how many times has the stream encountered the connection failure.# If the stream is disconnected just now for the first time, `info.fails` will be `1`# `info.exception` is the exception that raised which caused the failure# If abandon is `True`, then the client will give up reconnecting.# Otherwise:# - The client will asyncio.sleep `delay` seconds before reconnecting.OrderBookHandlerBase(**kwargs)kwargslimit?int=100the limit of the depth snapshotretry_policy?RetryPolicy=By default, binance-sdk maintains the orderbook for you according to the rules ofthe official documentation.Specifically,OrderBookHandlerBasedoes the job.We could get the managedOrderBookobject by methodhandler.orderbook(symbol).asyncdefmain():client=Client(api_key)# Unlike other handlers, we usually do not need to inherit `OrderBookHandlerBase`,#   unless we need to receive the raw payload of 'depthUpdate' messagehandler=OrderBookHandlerBase()client.handler(handler)awaitclient.subscribe(SubType.ORDER_BOOK,'BTCUSDT')# Get the reference of OrderBook object for 'BTCUSDT'orderbook=handler.orderbook('BTCUSDT')whileTrue:# If the `retry_policy` never abandon a retry,#   the 'try' block could be emittedtry:awaitorderbook.updated()exceptExceptionase:print('exception occurred')else:awaitdoSomethingWith(orderbook.asks,orderbook.bids)loop=asyncio.get_event_loop()loop.run_until_complete(main())loop.run_forever()OrderBook(symbol, **kwargs)symbolstrthe symbol namekwargslimit?int=100limit of the orderbookclientClient=Nonethe instance ofbinance.Clientretry_policy?Callable[[int, Exception], (bool, int, bool)]retry policy for depth snapshot which has the same mechanism asClient::stream_retry_policyOrderBookis another public class that we could import from binance-sdk and you could also construct your ownOrderBookinstance.asyncdefmain():# PAY attention that `orderbook` should be run in an event looporderbook=OrderBook('BTCUSDT',client=client)awaitorderbook.updated()print(orderbook.asks)orderbook.set_client(client) -> NoneclientClientthe instance ofbinance.ClientSet the client. Ifclientis not specified in the constructor, then executing this method will make the orderbook to fetch the snapshot for the first time.orderbook.set_limit(limit) -> NonelimitintSet depth limit which is used bybinance reset api.orderbook.set_retry_policy(retry_policy) -> Noneretry_policyRetryPolicySet retry policy of the certain orderbookpropertyorderbook.ready-> boolThere is a property getter inorderbookto detect whether the asks and bids are updated in the orderbook.If there is a network malfunction of the stream which causing the gap between two depth update messages,orderbookwill fetch a new snapshot from the server, and during that time and before we merge the snapshot,orderbook.readyisFalse.propertyorderbook.asks-> listpropertyorderbook.bids-> listGet asks and bids in ascending order.orderbook.update(payload) -> boolpayloaddictthe data payload of thedepthUpdatestream messageReturnsTrueif the payload is valid and is updated to the orderbook, otherwiseFalseIf the return value isFalse, the orderbook will automatically start fetching the snapshotawait orderbook.fetch() -> NoneManually fetch the snapshot.For most scenarios, you needNOTto call this method because once
there is an invalid payload, the orderbook will fetch the snapshot itself.await orderbook.updated() -> NoneWait for the next update of the orderbook.We could also awaitorderbook.updated()to make sure the orderbook is ready.If the orderbook fails to fetch depth snapshot for so many times which means the fetching is abanboned by theretry_policy, anaiohttpexception will be raised.Listen to the updates oforderbookasyncdefstart_listening_updates(orderbook):# This is an infinite loopwhileTrue:awaitorderbook.updated()# do somethingdefstart():returnasyncio.create_task(start_listening_updates(orderbook))task=start()# If we want to stop listeningtask.cancel()LicenseMIT