├── readme.md └── binance_api.py /readme.md: -------------------------------------------------------------------------------- 1 | # Tiny, but extremely powerful 2 | 3 | 4 | Provided code allows to call every binance api method and is easy ti integrate. 5 | Also, it will (highly likely) support most methods that may appear in future. 6 | It was tested by many users ([Bablofil website](https://bablofil.ru) subscribers) and proved it's functionality. 7 | Below you can find all the source and examples. 8 | 9 | 10 | ``` 11 | import ssl 12 | import time 13 | import json 14 | import urllib 15 | import hmac, hashlib 16 | import requests 17 | 18 | from urllib.parse import urlparse, urlencode 19 | from urllib.request import Request, urlopen 20 | 21 | class Binance(): 22 | 23 | methods = { 24 | # public methods 25 | 'ping': {'url':'api/v1/ping', 'method': 'GET', 'private': False}, 26 | 'time': {'url':'api/v1/time', 'method': 'GET', 'private': False}, 27 | 'exchangeInfo': {'url':'api/v1/exchangeInfo', 'method': 'GET', 'private': False}, 28 | 'depth': {'url': 'api/v1/depth', 'method': 'GET', 'private': False}, 29 | 'trades': {'url': 'api/v1/trades', 'method': 'GET', 'private': False}, 30 | 'historicalTrades': {'url': 'api/v1/historicalTrades', 'method': 'GET', 'private': False}, 31 | 'aggTrades': {'url': 'api/v1/aggTrades', 'method': 'GET', 'private': False}, 32 | 'klines': {'url': 'api/v1/klines', 'method': 'GET', 'private': False}, 33 | 'ticker24hr': {'url': 'api/v1/ticker/24hr', 'method': 'GET', 'private': False}, 34 | 'tickerPrice': {'url': 'api/v3/ticker/price', 'method': 'GET', 'private': False}, 35 | 'tickerBookTicker': {'url': 'api/v3/ticker/bookTicker', 'method': 'GET', 'private': False}, 36 | # private methods 37 | 'createOrder': {'url': 'api/v3/order', 'method': 'POST', 'private': True}, 38 | 'testOrder': {'url': 'api/v3/order/test', 'method': 'POST', 'private': True}, 39 | 'orderInfo': {'url': 'api/v3/order', 'method': 'GET', 'private': True}, 40 | 'cancelOrder': {'url': 'api/v3/order', 'method': 'DELETE', 'private': True}, 41 | 'openOrders': {'url': 'api/v3/openOrders', 'method': 'GET', 'private': True}, 42 | 'allOrders': {'url': 'api/v3/allOrders', 'method': 'GET', 'private': True}, 43 | 'account': {'url': 'api/v3/account', 'method': 'GET', 'private': True}, 44 | 'myTrades': {'url': 'api/v3/myTrades', 'method': 'GET', 'private': True}, 45 | # wapi 46 | 'depositAddress': {'url': '/wapi/v3/depositAddress.html', 'method':'GET', 'private':True}, 47 | 'withdraw': {'url': '/wapi/v3/withdraw.html', 'method':'POST', 'private':True}, 48 | 'depositHistory': {'url': '/wapi/v3/depositHistory.html', 'method':'GET', 'private':True}, 49 | 'withdrawHistory': {'url': '/wapi/v3/withdrawHistory.html', 'method':'GET', 'private':True}, 50 | 'withdrawFee': {'url': '/wapi/v3/withdrawFee.html', 'method':'GET', 'private':True}, 51 | 'accountStatus': {'url': '/wapi/v3/accountStatus.html', 'method':'GET', 'private':True}, 52 | 'systemStatus': {'url': '/wapi/v3/systemStatus.html', 'method':'GET', 'private':True}, 53 | } 54 | 55 | def __init__(self, API_KEY, API_SECRET): 56 | self.API_KEY = API_KEY 57 | self.API_SECRET = bytearray(API_SECRET, encoding='utf-8') 58 | self.shift_seconds = 0 59 | 60 | def __getattr__(self, name): 61 | def wrapper(*args, **kwargs): 62 | kwargs.update(command=name) 63 | return self.call_api(**kwargs) 64 | return wrapper 65 | 66 | def set_shift_seconds(self, seconds): 67 | self.shift_seconds = seconds 68 | 69 | def call_api(self, **kwargs): 70 | 71 | command = kwargs.pop('command') 72 | api_url = 'https://api.binance.com/' + self.methods[command]['url'] 73 | 74 | payload = kwargs 75 | headers = {} 76 | 77 | payload_str = urllib.parse.urlencode(payload) 78 | if self.methods[command]['private']: 79 | payload.update({'timestamp': int(time.time() + self.shift_seconds - 1) * 1000}) 80 | payload_str = urllib.parse.urlencode(payload).encode('utf-8') 81 | sign = hmac.new( 82 | key=self.API_SECRET, 83 | msg=payload_str, 84 | digestmod=hashlib.sha256 85 | ).hexdigest() 86 | 87 | payload_str = payload_str.decode("utf-8") + "&signature="+str(sign) 88 | headers = {"X-MBX-APIKEY": self.API_KEY} 89 | 90 | if self.methods[command]['method'] == 'GET': 91 | api_url += '?' + payload_str 92 | 93 | response = requests.request(method=self.methods[command]['method'], url=api_url, data="" if self.methods[command]['method'] == 'GET' else payload_str, headers=headers) 94 | 95 | 96 | if 'code' in response.text: 97 | print(response.text) 98 | return response.json() 99 | ``` 100 | 101 | ## Python 3+ interface for binance 102 | 103 | examples: 104 | 105 | 106 | ### check connection 107 | 108 | ``` 109 | from binance_api import Binance 110 | bot = Binance( 111 | API_KEY='D7...Ejj', 112 | API_SECRET='gwQ...u3A' 113 | ) 114 | print(bot.ping()) 115 | ``` 116 | 117 | ### get depth 118 | 119 | ``` 120 | from binance_api import Binance 121 | bot = Binance( 122 | API_KEY='D7...Ejj', 123 | API_SECRET='gwQ...u3A' 124 | ) 125 | print('depth', bot.depth( 126 | symbol='BNBBTC', 127 | limit=5 128 | )) 129 | ``` 130 | 131 | ### Place order 132 | 133 | ``` 134 | from binance_api import Binance 135 | bot = Binance( 136 | API_KEY='D7...Ejj', 137 | API_SECRET='gwQ...u3A' 138 | ) 139 | # Buy 0.1 LTC with BTC 140 | print('createOrder', bot.createOrder( 141 | symbol='LTCBTC', 142 | recvWindow=5000, 143 | side='BUY', 144 | type='LIMIT', 145 | timeInForce='GTC', 146 | quantity=0.1, 147 | price=0.1 148 | )) 149 | ``` 150 | 151 | ### Get order info 152 | 153 | ``` 154 | from binance_api import Binance 155 | bot = Binance( 156 | API_KEY='D7...Ejj', 157 | API_SECRET='gwQ...u3A' 158 | ) 159 | print('orderInfo', bot.orderInfo( 160 | orderId=123123, 161 | symbol='LTCBTC', 162 | )) 163 | ``` 164 | 165 | ### Get account info 166 | 167 | ``` 168 | from binance_api import Binance 169 | bot = Binance( 170 | API_KEY='D7...Ejj', 171 | API_SECRET='gwQ...u3A' 172 | ) 173 | 174 | print('account', bot.account()) 175 | ``` 176 | 177 | ETC.. 178 | 179 | ### More info: 180 | 181 | 182 | You just take methods names and params from from [official api](https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md) and use it with the provided interface. 183 | 184 | 185 | You can get more examples in Russian [here](https://bablofil.ru/binance-api/) 186 | 187 | 188 | If you happen to have some time-related issues, like "Timestamp for this request was 1000ms ahead of the server's time." or "Timestamp for this request is outside of the recvWindow", you should synchronize your local time with a ntp server or use "set_shift_seconds" method to adjust difference between your local time and Binance's; 189 | 190 | 191 | Here's an example: 192 | 193 | 194 | ``` 195 | local_time = int(time.time()) 196 | server_time = int(limits['serverTime'])//1000 197 | 198 | 199 | shift_seconds = server_time-local_time 200 | bot.set_shift_seconds(shift_seconds) 201 | ``` 202 | -------------------------------------------------------------------------------- /binance_api.py: -------------------------------------------------------------------------------- 1 | import ssl 2 | import time 3 | import json 4 | import urllib 5 | import hmac, hashlib 6 | import requests 7 | 8 | from urllib.parse import urlparse, urlencode 9 | from urllib.request import Request, urlopen 10 | 11 | class Binance(): 12 | 13 | methods = { 14 | # public methods 15 | 'ping': {'url':'api/v1/ping', 'method': 'GET', 'private': False}, 16 | 'time': {'url':'api/v1/time', 'method': 'GET', 'private': False}, 17 | 'exchangeInfo': {'url':'api/v1/exchangeInfo', 'method': 'GET', 'private': False}, 18 | 'depth': {'url': 'api/v1/depth', 'method': 'GET', 'private': False}, 19 | 'trades': {'url': 'api/v1/trades', 'method': 'GET', 'private': False}, 20 | 'historicalTrades': {'url': 'api/v1/historicalTrades', 'method': 'GET', 'private': False}, 21 | 'aggTrades': {'url': 'api/v1/aggTrades', 'method': 'GET', 'private': False}, 22 | 'klines': {'url': 'api/v1/klines', 'method': 'GET', 'private': False}, 23 | 'ticker24hr': {'url': 'api/v1/ticker/24hr', 'method': 'GET', 'private': False}, 24 | 'tickerPrice': {'url': 'api/v3/ticker/price', 'method': 'GET', 'private': False}, 25 | 'tickerBookTicker': {'url': 'api/v3/ticker/bookTicker', 'method': 'GET', 'private': False}, 26 | # private methods 27 | 'createOrder': {'url': 'api/v3/order', 'method': 'POST', 'private': True}, 28 | 'testOrder': {'url': 'api/v3/order/test', 'method': 'POST', 'private': True}, 29 | 'orderInfo': {'url': 'api/v3/order', 'method': 'GET', 'private': True}, 30 | 'cancelOrder': {'url': 'api/v3/order', 'method': 'DELETE', 'private': True}, 31 | 'openOrders': {'url': 'api/v3/openOrders', 'method': 'GET', 'private': True}, 32 | 'allOrders': {'url': 'api/v3/allOrders', 'method': 'GET', 'private': True}, 33 | 'account': {'url': 'api/v3/account', 'method': 'GET', 'private': True}, 34 | 'myTrades': {'url': 'api/v3/myTrades', 'method': 'GET', 'private': True}, 35 | # wapi 36 | 'depositAddress': {'url': 'wapi/v3/depositAddress.html', 'method':'GET', 'private':True}, 37 | 'withdraw': {'url': 'wapi/v3/withdraw.html', 'method':'POST', 'private':True}, 38 | 'depositHistory': {'url': 'wapi/v3/depositHistory.html', 'method':'GET', 'private':True}, 39 | 'withdrawHistory': {'url': 'wapi/v3/withdrawHistory.html', 'method':'GET', 'private':True}, 40 | 'assetDetail': {'url': 'wapi/v3/assetDetail.html', 'method':'GET', 'private':True}, 41 | 'tradeFee': {'url': 'wapi/v3/tradeFee.html', 'method':'GET', 'private':True}, 42 | 'accountStatus': {'url': 'wapi/v3/accountStatus.html', 'method':'GET', 'private':True}, 43 | 'systemStatus': {'url': 'wapi/v3/systemStatus.html', 'method':'GET', 'private':True}, 44 | 'assetDust': {'url': 'sapi/v1/asset/dust', 'method':'POST', 'private':True}, 45 | 'dustLog': {'url': 'wapi/v3/userAssetDribbletLog.html', 'method':'GET', 'private':True}, 46 | 'assetAssetDividend': {'url': 'sapi/v1/asset/assetDividend', 'method':'GET', 'private':True}, 47 | #sapi 48 | 'marginTransfer': {'url': 'sapi/v1/margin/transfer', 'method': 'POST', 'private':True}, 49 | 'marginLoan': {'url': 'sapi/v1/margin/loan', 'method': 'POST', 'private': True}, 50 | 'marginLoanGet': {'url': 'sapi/v1/margin/loan', 'method': 'GET', 'private': True}, 51 | 'marginRepay': {'url': 'sapi/v1/margin/repay', 'method': 'POST', 'private': True}, 52 | 'marginRepayGet': {'url': 'sapi/v1/margin/repay', 'method': 'GET', 'private': True}, 53 | 'marginCreateOrder': {'url': 'sapi/v1/margin/order', 'method': 'POST', 'private':True}, 54 | 'marginCancelOrder': {'url': 'sapi/v1/margin/order', 'method': 'DELETE', 'private':True}, 55 | 'marginOrderInfo': {'url': 'sapi/v1/margin/order', 'method': 'GET', 'private':True}, 56 | 'marginAccount': {'url': 'sapi/v1/margin/account', 'method': 'POST', 'private':True}, 57 | 'marginOpenOrders': {'url': 'sapi/v1/margin/openOrders', 'method': 'GET', 'private':True}, 58 | 'marginAllOrders': {'url': 'sapi/v1/margin/allOrders', 'method': 'GET', 'private':True}, 59 | 'marginAsset': {'url': 'sapi/v1/margin/asset', 'method': 'POST', 'private':True}, 60 | 'marginPair': {'url': 'sapi/v1/margin/pair', 'method': 'POST', 'private':True}, 61 | 'marginPriceIndex': {'url': 'sapi/v1/margin/priceIndex', 'method': 'POST', 'private':True}, 62 | 'marginMyTrades': {'url': 'sapi/v1/margin/myTrades', 'method': 'GET', 'private':True}, 63 | 'marginMaxBorrowable': {'url': 'sapi/v1/margin/maxBorrowable', 'method': 'GET', 'private':True}, 64 | 'marginmaxTransferable': {'url': 'sapi/v1/margin/maxTransferable', 'method': 'GET', 'private':True}, 65 | #futures 66 | 'futuresExchangeInfo': {'url': 'fapi/v1/exchangeInfo', 'method': 'GET', 'private': False, 'futures': True}, 67 | 'futuresKlines': {'url': 'fapi/v1/klines', 'method': 'GET', 'private': False, 'futures': True}, 68 | 'futuresCreateOrder': {'url': 'fapi/v1/order', 'method': 'POST', 'private': True, 'futures': True}, 69 | 'futuresAccount': {'url': 'fapi/v1/account', 'method': 'POST', 'private': True, 'futures': True}, 70 | 'futuresBalance': {'url': 'fapi/v1/balance', 'method': 'GET', 'private': True, 'futures': True}, 71 | 'futuresSymbolPriceTicker': {'url': 'fapi/v1/ticker/price', 'method': 'GET', 'private': True, 'futures': True}, 72 | 'futuresOrderInfo': {'url': 'fapi/v1/order', 'method': 'GET', 'private': True, 'futures': True}, 73 | 'futuresCancelOrder': {'url': 'fapi/v1/order', 'method': 'DELETE', 'private': True, 'futures': True}, 74 | } 75 | 76 | def __init__(self, API_KEY, API_SECRET): 77 | self.API_KEY = API_KEY 78 | self.API_SECRET = bytearray(API_SECRET, encoding='utf-8') 79 | self.shift_seconds = 0 80 | 81 | def __getattr__(self, name): 82 | def wrapper(*args, **kwargs): 83 | kwargs.update(command=name) 84 | return self.call_api(**kwargs) 85 | return wrapper 86 | 87 | def set_shift_seconds(self, seconds): 88 | self.shift_seconds = seconds 89 | 90 | def call_api(self, **kwargs): 91 | 92 | command = kwargs.pop('command') 93 | 94 | base_url ='https://api.binance.com/' 95 | if self.methods[command].get('futures'): 96 | base_url = 'https://fapi.binance.com/' 97 | api_url = base_url + self.methods[command]['url'] 98 | 99 | payload = kwargs 100 | headers = {} 101 | 102 | payload_str = urllib.parse.urlencode(payload) 103 | if self.methods[command]['private']: 104 | payload.update({'timestamp': int(time.time() + self.shift_seconds - 1) * 1000}) 105 | payload_str = urllib.parse.urlencode(payload).encode('utf-8') 106 | sign = hmac.new( 107 | key=self.API_SECRET, 108 | msg=payload_str, 109 | digestmod=hashlib.sha256 110 | ).hexdigest() 111 | 112 | payload_str = payload_str.decode("utf-8") + "&signature="+str(sign) 113 | headers = {"X-MBX-APIKEY": self.API_KEY, "Content-Type":"application/x-www-form-urlencoded"} 114 | 115 | if self.methods[command]['method'] == 'GET' or self.methods[command]['url'].startswith('sapi'): 116 | api_url += '?' + payload_str 117 | 118 | response = requests.request(method=self.methods[command]['method'], url=api_url, data="" if self.methods[command]['method'] == 'GET' else payload_str, headers=headers) 119 | 120 | if 'code' in response.text: 121 | raise Exception(response.text) 122 | 123 | return response.json() 124 | 125 | --------------------------------------------------------------------------------