Merge pull request #8 from libcryptomarket/feature/#7

[#7] Add exchange Poloniex for private API
This commit is contained in:
Gavin Chan
2017-12-01 07:24:37 +08:00
committed by GitHub
2 changed files with 224 additions and 0 deletions
+119
View File
@@ -1,5 +1,11 @@
#!/bin/python
import requests
import hmac
import hashlib
import urllib
from functools import partial
from libcryptomarket.api.rest_api_connector import RestApiConnector
API_URL = "https://poloniex.com/public?command="
VALID_PERIODS = [300, 900, 1800, 7200, 14400, 86400]
@@ -58,3 +64,116 @@ def get_return_order_book(currency_pair, depth=10):
raise ValueError("Query error from Poloniex API ({0})".format(rjson))
return r.json()
class PoloniexApi(RestApiConnector):
"""Poloniex API.
"""
URL = 'https://poloniex.com/tradingApi'
AVAILABLE_TRADING_API = [
"returnBalances",
"returnCompleteBalances",
"returnDepositAddresses",
"generateNewAddress",
"returnDepositsWithdrawals",
"returnOpenOrders",
"returnTradeHistory",
"returnOrderTrades",
"buy",
"sell",
"cancelOrder",
"moveOrder",
"withdraw",
"returnFeeInfo",
"returnAvailableAccountBalances",
"returnTradableBalances",
"transferBalance",
"returnMarginAccountSummary",
"marginBuy",
"marginSell",
"getMarginPosition",
"closeMarginPosition",
"createLoanOffer",
"cancelLoanOffer",
"returnOpenLoanOffers",
"returnActiveLoans",
"returnLendingHistory",
"toggleAutoRenew",
]
def __init__(self, public_key, private_key, logger=None):
"""Constructor.
:param public_key: Public key.
:param private_key: Private key.
"""
RestApiConnector.__init__(self, url=PoloniexApi.URL, logger=logger)
self.__public_key = public_key
self.__private_key = private_key
def __getattr__(self, name):
"""Get attribute.
"""
if name in PoloniexApi.AVAILABLE_TRADING_API:
return partial(self._request, command=name, http_method="POST")
else:
raise AttributeError("Trading method ({0}) ".format(name) +
"is not defined.")
def _request(self, command, **kwargs):
"""Send request.
:param kwargs: Named arguments.
"""
kwargs["command"] = command
kwargs["nonce"] = self._generate_nonce()
return self._send_request(
command="",
http_method="POST",
public_key=self.__public_key,
private_key=self.__private_key,
params=None,
data=kwargs)
def _generate_auth(self, public_key, private_key):
"""Generate authentication.
:param public_key: Public key.
:param private_key: Private key.
"""
return None
def _generate_headers(self, 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.
"""
signature = hmac.new(private_key.encode(),
urllib.parse.urlencode(data).encode(),
digestmod=hashlib.sha512).hexdigest()
header = {
'Key': public_key,
'Sign': signature
}
return header
def _format_data(self, data):
"""Format the data to exchange desirable format.
:param data: Data.
"""
if data is None:
return ""
else:
return data
+105
View File
@@ -0,0 +1,105 @@
#!/bin/python
import requests
import urllib
import json
from time import time
class RestApiConnector(object):
"""REST API connector.
"""
def __init__(self, url, logger=None):
"""Constructor.
:param url: URL address.
:param logger: Logger. Default is None.
"""
self._logger = logger
self._url = url
@classmethod
def _generate_nonce(cls):
"""Generate an increasing unique number.
"""
return int(round(time() * 1000))
def _generate_headers(self, 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.")
def _generate_auth(self, public_key, private_key):
"""Generate authentication.
:param public_key: Public key.
:param private_key: Private key.
"""
raise NotImplementedError("Not yet implemented.")
def _format_data(self, data):
"""Format the data to exchange desirable format.
:param data: Data.
"""
raise NotImplementedError("Not yet implemented.")
def _send_request(self, command, http_method, public_key, private_key,
params=None, data=None):
"""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.
: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.")
url = urllib.parse.urljoin(self._url, command)
data = self._format_data(data)
headers = self._generate_headers(command, http_method, params,
data, public_key, private_key)
auth = self._generate_auth(public_key, private_key)
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