forked from BitMEX/api-connectors
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBitMEXAPIKeyAuthenticator.py
65 lines (56 loc) · 2.39 KB
/
BitMEXAPIKeyAuthenticator.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
import urllib.parse
import time
import hashlib
import hmac
from bravado.requests_client import Authenticator
class APIKeyAuthenticator(Authenticator):
"""?api_key authenticator.
This authenticator adds BitMEX API key support via header.
:param host: Host to authenticate for.
:param api_key: API key.
:param api_secret: API secret.
"""
def __init__(self, host, api_key, api_secret):
super(APIKeyAuthenticator, self).__init__(host)
self.api_key = api_key
self.api_secret = api_secret
# Forces this to apply to all requests.
def matches(self, url):
if "swagger.json" in url:
return False
return True
# Add the proper headers via the `expires` scheme.
def apply(self, r):
# 5s grace period in case of clock skew
expires = int(round(time.time()) + 5)
r.headers['api-expires'] = str(expires)
r.headers['api-key'] = self.api_key
prepared = r.prepare()
body = prepared.body or ''
url = prepared.path_url
# print(json.dumps(r.data, separators=(',',':')))
r.headers['api-signature'] = self.generate_signature(self.api_secret, r.method, url, expires, body)
return r
# Generates an API signature.
# A signature is HMAC_SHA256(secret, verb + path + nonce + data), hex encoded.
# Verb must be uppercased, url is relative, nonce must be an increasing 64-bit integer
# and the data, if present, must be JSON without whitespace between keys.
#
# For example, in psuedocode (and in real code below):
#
# verb=POST
# url=/api/v1/order
# nonce=1416993995705
# data={"symbol":"XBTZ14","quantity":1,"price":395.01}
# signature = HEX(HMAC_SHA256(secret, 'POST/api/v1/order1416993995705{"symbol":"XBTZ14","quantity":1,"price":395.01}'))
def generate_signature(self, secret, verb, url, nonce, data):
"""Generate a request signature compatible with BitMEX."""
# Parse the url so we can remove the base and extract just the path.
parsedURL = urllib.parse.urlparse(url)
path = parsedURL.path
if parsedURL.query:
path = path + '?' + parsedURL.query
message = bytes(verb + path + str(nonce) + data, 'utf-8')
# print("Computing HMAC: %s" % message)
signature = hmac.new(bytes(secret, 'utf-8'), message, digestmod=hashlib.sha256).hexdigest()
return signature