forked from cheaphunter/binance
-
Notifications
You must be signed in to change notification settings - Fork 0
/
binance.py
263 lines (205 loc) · 6.73 KB
/
binance.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
import hmac
import hashlib
import logging
import requests
import time
try:
from urllib import urlencode
# for python3
except ImportError:
from urllib.parse import urlencode
ENDPOINT = "https://www.binance.com"
BUY = "BUY"
SELL = "SELL"
LIMIT = "LIMIT"
MARKET = "MARKET"
GTC = "GTC"
IOC = "IOC"
options = {}
def set(apiKey, secret):
"""Set API key and secret.
Must be called before any making any signed API calls.
"""
options["apiKey"] = apiKey
options["secret"] = secret
def prices():
"""Get latest prices for all symbols."""
data = request("GET", "/api/v1/ticker/allPrices")
return {d["symbol"]: d["price"] for d in data}
def tickers():
"""Get best price/qty on the order book for all symbols."""
data = request("GET", "/api/v1/ticker/allBookTickers")
return {d["symbol"]: {
"bid": d["bidPrice"],
"ask": d["askPrice"],
"bidQty": d["bidQty"],
"askQty": d["askQty"],
} for d in data}
def depth(symbol, **kwargs):
"""Get order book.
Args:
symbol (str)
limit (int, optional): Default 100. Must be one of 50, 20, 100, 500, 5,
200, 10.
"""
params = {"symbol": symbol}
params.update(kwargs)
data = request("GET", "/api/v1/depth", params)
return {
"bids": {px: qty for px, qty, _ in data["bids"]},
"asks": {px: qty for px, qty, _ in data["asks"]},
}
def klines(symbol, interval, **kwargs):
"""Get kline/candlestick bars for a symbol.
Klines are uniquely identified by their open time. If startTime and endTime
are not sent, the most recent klines are returned.
Args:
symbol (str)
interval (str)
limit (int, optional): Default 500; max 500.
startTime (int, optional)
endTime (int, optional)
"""
params = {"symbol": symbol, "interval": interval}
params.update(kwargs)
data = request("GET", "/api/v1/klines", params)
return [{
"openTime": d[0],
"open": d[1],
"high": d[2],
"low": d[3],
"close": d[4],
"volume": d[5],
"closeTime": d[6],
"quoteVolume": d[7],
"numTrades": d[8],
} for d in data]
def balances():
"""Get current balances for all symbols."""
data = signedRequest("GET", "/api/v3/account", {})
if 'msg' in data:
raise ValueError("Error from exchange: {}".format(data['msg']))
return {d["asset"]: {
"free": d["free"],
"locked": d["locked"],
} for d in data.get("balances", [])}
def order(symbol, side, quantity, price, orderType=LIMIT, timeInForce=GTC,
test=False, **kwargs):
"""Send in a new order.
Args:
symbol (str)
side (str): BUY or SELL.
quantity (float, str or decimal)
price (float, str or decimal)
orderType (str, optional): LIMIT or MARKET.
timeInForce (str, optional): GTC or IOC.
test (bool, optional): Creates and validates a new order but does not
send it into the matching engine. Returns an empty dict if
successful.
newClientOrderId (str, optional): A unique id for the order.
Automatically generated if not sent.
stopPrice (float, str or decimal, optional): Used with stop orders.
icebergQty (float, str or decimal, optional): Used with iceberg orders.
"""
params = {
"symbol": symbol,
"side": side,
"type": orderType,
"timeInForce": timeInForce,
"quantity": formatNumber(quantity),
"price": formatNumber(price),
}
params.update(kwargs)
path = "/api/v3/order/test" if test else "/api/v3/order"
data = signedRequest("POST", path, params)
return data
def orderStatus(symbol, **kwargs):
"""Check an order's status.
Args:
symbol (str)
orderId (int, optional)
origClientOrderId (str, optional)
recvWindow (int, optional)
"""
params = {"symbol": symbol}
params.update(kwargs)
data = signedRequest("GET", "/api/v3/order", params)
return data
def cancel(symbol, **kwargs):
"""Cancel an active order.
Args:
symbol (str)
orderId (int, optional)
origClientOrderId (str, optional)
newClientOrderId (str, optional): Used to uniquely identify this
cancel. Automatically generated by default.
recvWindow (int, optional)
"""
params = {"symbol": symbol}
params.update(kwargs)
data = signedRequest("DELETE", "/api/v3/order", params)
return data
def openOrders(symbol, **kwargs):
"""Get all open orders on a symbol.
Args:
symbol (str)
recvWindow (int, optional)
"""
params = {"symbol": symbol}
params.update(kwargs)
data = signedRequest("GET", "/api/v3/openOrders", params)
return data
def allOrders(symbol, **kwargs):
"""Get all account orders; active, canceled, or filled.
If orderId is set, it will get orders >= that orderId. Otherwise most
recent orders are returned.
Args:
symbol (str)
orderId (int, optional)
limit (int, optional): Default 500; max 500.
recvWindow (int, optional)
"""
params = {"symbol": symbol}
params.update(kwargs)
data = signedRequest("GET", "/api/v3/allOrders", params)
return data
def myTrades(symbol, **kwargs):
"""Get trades for a specific account and symbol.
Args:
symbol (str)
limit (int, optional): Default 500; max 500.
fromId (int, optional): TradeId to fetch from. Default gets most recent
trades.
recvWindow (int, optional)
"""
params = {"symbol": symbol}
params.update(kwargs)
data = signedRequest("GET", "/api/v3/myTrades", params)
return data
def request(method, path, params=None):
resp = requests.request(method, ENDPOINT + path, params=params)
data = resp.json()
if "msg" in data:
logging.error(data['msg'])
return data
def signedRequest(method, path, params):
if "apiKey" not in options or "secret" not in options:
raise ValueError("Api key and secret must be set")
query = urlencode(sorted(params.items()))
query += "×tamp={}".format(int(time.time() * 1000))
secret = bytes(options["secret"].encode("utf-8"))
signature = hmac.new(secret, query.encode("utf-8"),
hashlib.sha256).hexdigest()
query += "&signature={}".format(signature)
resp = requests.request(method,
ENDPOINT + path + "?" + query,
headers={"X-MBX-APIKEY": options["apiKey"]})
data = resp.json()
if "msg" in data:
logging.error(data['msg'])
return data
def formatNumber(x):
if isinstance(x, float):
return "{:.8f}".format(x)
else:
return str(x)