· 6 years ago · Dec 20, 2019, 03:28 PM
1import requests as r
2import time as t
3import hashlib
4import hmac
5from urllib.parse import urlencode # to make a string query of all the parameters
6
7apikey = "apikeyhere"
8secret = "secrethere"
9
10
11def postorder(x, y):
12 params = { # just for posting orders
13 'symbol': 'ENGBTC',
14 'side': 'BUY',
15 'type': 'LIMIT',
16 'timeInForce': 'GTC',
17 'price': x,
18 'quantity': y,
19 'timestamp': gettime()
20 }
21 params = sigmaker(params) # concatenates and encodes the query + secret
22 post_api = r.post("https://api.binance.com/api/v3/order", # open orders endpoint
23 params=params,
24 headers={'X-MBX-APIKEY': apikey})
25 if post_api.status_code != 200: # in case there is any error in the endpoint
26 raise Exception("PostOrder exception: status code {0}, possible: check order size".format(post_api.status_code))
27 print("New order placed\norder size {0}\tprice {1}\n=======".format(y, x))
28
29
30def cancelorder(x):
31 params = { # just for closing orders
32 'symbol': 'ENGBTC',
33 'timestamp': gettime(),
34 'orderId': x
35 }
36 params = sigmaker(params)
37 post_api = r.delete("https://api.binance.com/api/v3/order", # open orders endpoint
38 params=params,
39 headers={'X-MBX-APIKEY': apikey})
40 if post_api.status_code != 200: # in case there is any error in the endpoint
41 print("API unreachable, cannot close order")
42 print("Closing all open orders and aborting code")
43 closeallorders()
44 raise Exception("CancelOrder exception: status code {0}".format(post_api.status_code))
45
46
47def sigmaker(x): # this function will always generate the signature
48 query_string = urlencode(x) # make the parameters (argument here) a query string
49 x['signature'] = hmac.new(secret.encode('utf-8'), query_string.encode('utf-8'),
50 hashlib.sha256).hexdigest() # convert into hmac sha256 and add to parameters
51 return x # returns the new parameters (argument + signature key)
52
53
54def closeallorders():
55 order_id_list = list() # will be a list of all open orders
56 params= {'symbol': "ENGBTC",
57 'timestamp': gettime()}
58 params = sigmaker(params)
59 openorders = r.get("https://api.binance.com/api/v3/openOrders", # open orders endpoint
60 params=params,
61 headers={'X-MBX-APIKEY': apikey})
62 for i in range(0, len(openorders.json())): # the length is the number of open orders
63 x = openorders.json()[i]['orderId']
64 order_id_list.append(x)
65
66 for i in order_id_list: #
67 params_close = { # just for closing orders
68 'symbol': 'ENGBTC',
69 'timestamp': gettime(),
70 'orderId': i
71 }
72 params_close = sigmaker(params_close)
73 post_api = r.delete("https://api.binance.com/api/v3/order", # open orders endpoint
74 params=params_close,
75 headers={'X-MBX-APIKEY': apikey})
76 if post_api.status_code != 200: # in case there is any error in the endpoint
77 raise Exception("CancelOrder exception: status code " + str(post_api.status_code) +
78 " Warning: API unreachable, orders might have not been closed")
79 print("All orders have been closed") # this wont be reached if there is an error in the api
80
81
82def gettime(): # a function to generate current timestamp of the server
83 servertime = r.get("https://api.binance.com/api/v3/time")
84 timestamp = str(servertime.json()['serverTime'])
85 return timestamp
86
87# The loop below should not be broken unless we abort the algo
88# If we run out of position size, it will simply raise an exception (it will restart the loop and get index error on api
89# If position gets partially taken, it will use what's left in the next order rather than renewing amount
90# If more than one active order are found, all orders will be canceled and the execution stops
91# If the market price is less than (our order price + 0.25%), no action will be taken
92
93
94while True: # This loop should never be broken!
95 params = {
96 'symbol': 'ENGBTC',
97 'timestamp': gettime()}
98 params = sigmaker(params)
99
100 ob_api = r.get(
101 'https://api.binance.com/api/v3/depth',
102 params={'symbol': "ENGBTC", 'limit': 5}) # order book endpoint
103 try:
104 hbp = ob_api.json()['bids'][0][0] # highest bid price
105 except IndexError: # no list = empty order book
106 raise Exception("Index error - Can't retrieve order book from Binance")
107
108 tl_api = r.get('https://api.binance.com/api/v3/trades',
109 params={'symbol': "ENGBTC", 'limit': 1}) # our trade book endpoint
110 mp = tl_api.json()[0]['price'] # market price (last trade price)
111
112 coo_api = r.get("https://api.binance.com/api/v3/openOrders", # open orders endpoint
113 params=params,
114 headers={'X-MBX-APIKEY': apikey})
115 if len(coo_api.json()) > 1: # if locates more than one open order - close all orders and abort
116 print("Warning: More than one open orders found! Closing all orders immediately")
117 closeallorders()
118 exit("Abort - There were more than one order present")
119 try:
120 cos = float((coo_api.json()[0]['origQty'])) - float(coo_api.json()[0]['executedQty']) # current open position
121 except IndexError: # no list = empty order list
122 print("No open orders on Binance - Check position")
123 raise Exception("Index error - no open orders on Binance - Check position")
124 # this was meant to post the new order with what is left of the old order, rather than new 50k
125 cop = coo_api.json()[0]['price'] # current order price
126 order_id = coo_api.json()[0]['orderId'] # for cancelling in the future, always changing
127
128 if (float(hbp) > 1.0025 * float(cop)) and (float(mp) > 1.0025 * float(cop)): # if both market
129 # price and highest bid are at least +0.25%, raise order price by 0.25%
130 cancelorder(order_id) # calls the function to cancel old order
131 postorder("{:9.8f}".format(1.0025*float(cop)), cos) # post order with 1.0025 * current price, also
132 # I converted format decimals because of the scientific writing (api error), this also eliminates
133 # the extra decimal that Binance doesn't accept.
134 else:
135 print("Order price left unchanged - too close to the market price")
136 pass # this is redundant but I wrote it just to e2mphasize the second scenario where we don't
137 # modify the order because of insignificant price change
138 t.sleep(5) # repeat process after 30 minutes