Files
libcryptomarket/libcryptomarket/api/exchange_api.py
T
2017-12-29 08:31:51 +00:00

217 lines
6.6 KiB
Python

#!/bin/python
import requests
import hmac
import hashlib
import urllib
import json
from functools import partial
from time import time
class ExchangeApi:
"""Exchange API connector.
"""
def __init__(self, public_key, private_key, logger=None):
"""Constructor.
:param public_key: Public key.
:param private_key: Private key.
:param logger: Logger.
"""
self._public_key = public_key
self._private_key = private_key
self._logger = logger
@classmethod
def get_url(cls):
"""Get API url.
"""
raise NotImplementedError("Url getter not implemented")
@classmethod
def get_public_url(cls):
"""Get public API url.
"""
raise NotImplementedError("Public url getter not implemented")
@classmethod
def get_private_url(cls):
"""Get private API url.
"""
raise NotImplementedError("Private url getter not implemented")
@classmethod
def get_public_calls(cls):
"""Get public API calls.
"""
raise NotImplementedError("Public API calls getter not implemented")
@classmethod
def get_private_calls(cls):
"""Get private API calls.
"""
raise NotImplementedError("Private API calls getter not implemented")
@classmethod
def translate_call_name(cls, name):
"""Translate API call name.
The class method name is always underscored (aligned with Python
standard.) This method is to translate underscored name to exchange
API call name.
:param name: Method name (underscored).
"""
return name
def __getattr__(self, name):
"""Get attribute.
"""
name = self.translate_call_name(name)
if name in self.get_public_calls().keys():
return partial(self._request_public, name=name,
http_method=self.get_public_calls()[name])
elif name in self.get_private_calls().keys():
return partial(self._request_private, name=name,
http_method=self.get_private_calls()[name])
else:
raise AttributeError("Trading method ({0}) ".format(name) +
"is not defined.")
def _request_public(self, name, http_method, **kwargs):
"""Request public API call.
:param name: Method name.
:param http_method: HTTP method (POST, GET, DELETE).
"""
raise NotImplementedError("Public API request not implemented")
def _request_private(self, name, http_method, *kwargs):
"""Request private API call.
:param name: Method name.
:param http_method: HTTP method (POST, GET, DELETE).
"""
raise NotImplementedError("Private API request not implemented")
# ======================================================================
# Requests
# ======================================================================
@classmethod
def _generate_nonce(cls):
"""Generate an increasing unique number.
"""
return int(round(time() * 1000))
@classmethod
def _generate_headers(cls, command, http_method, params, data,
public_key, private_key):
"""Generate headers.
:param command: Command.
:param http_method: HTTP method, for example GET.
:param params: Parameters.
:param data: Data.
:param public_key: Public key.
:param private_key: Private key.
"""
raise NotImplementedError("Not yet implemented.")
@classmethod
def _generate_auth(cls, public_key, private_key):
"""Generate authentication.
:param public_key: Public key.
:param private_key: Private key.
"""
raise NotImplementedError("Not yet implemented.")
@classmethod
def _generate_data(cls, data):
"""Generate data.
:param data: Dict containing data information.
"""
raise NotImplementedError("Not yet implemented.")
def _send_request(self, command, http_method, params=None, data=None,
public_method=False):
"""Send request.
:param command: API command.
:param http_method: Http method.
:param api_key: API key.
:param params: Input parameters, which will be parsed
as "?key1=value1...".
:param data: Data.
:param public_method: Indicate if the request is a public method.
:return: JSON object.
"""
http_method = http_method.upper()
if http_method == "DELETE":
R = requests.delete
elif http_method == "GET":
R = requests.get
elif http_method == "POST":
R = requests.post
else:
raise ValueError("Http method must be either DELETE, GET or "
"POST.")
# Get url
url = self.get_url()
if url is None:
if public_method:
url = self.get_public_url()
else:
url = self.get_private_url()
if url is None:
raise NotImplementedError("Url cannot be None.")
if command is not None or command == "":
url = '/'.join([url, command])
# Get data
if data is not None:
data = self._format_data(data)
else:
data = ""
# Get headers and auth
if (self._public_key is not None and self._private_key is not None and
not public_method):
headers = self._generate_headers(command, http_method, params,
data, self._public_key,
self._private_key)
auth = self._generate_auth(self._public_key, self._private_key)
else:
headers = None
auth = None
if self._logger is not None:
self._logger.info(">>> OUT:\n%s" % json.dumps({
"Method": http_method,
"Url": url,
"Params": params,
"Data": data,
"Headers": headers
}))
if auth is None:
response = R(url, params=params, data=data, headers=headers)
else:
response = R(url, params=params, data=data, headers=headers,
auth=auth)
if self._logger is not None:
self._logger.info("<<< IN:\n%s" % json.dumps({
"Status code": response.status_code,
"Text": response.text
}))
return response