# coding=utf-8
import jwt
import requests
import time
from .exceptions import BigoneAPIException, BigoneRequestException
[docs]class Client(object):
API_URL = 'https://big.one/api/v2'
SIDE_BID = 'BID'
SIDE_ASK = 'ASK'
[docs] def __init__(self, api_key, api_secret):
"""Big.One API Client constructor
https://open.big.one/
:param api_key: Api Key
:type api_key: str
:param api_key: Api Secret
:type api_key: str
.. code:: python
client = Client(api_key, api_secret)
"""
self.API_KEY = api_key
self.API_SECRET = api_secret
self.session = self._init_session()
def _init_session(self):
session = requests.session()
headers = {'Accept': 'application/json',
'User-Agent': 'python-bigone'}
session.headers.update(headers)
return session
def _create_uri(self, path):
return '{}/{}'.format(self.API_URL, path)
def _create_signature(self, ):
headers = {'typ': 'JWT', 'alg': 'HS256'}
payload = {
'type': 'OpenAPI',
'sub': self.API_KEY,
'nonce': int(time.time() * 1000000000) # convert to nanoseconds
}
sig = jwt.encode(payload, self.API_SECRET, algorithm='HS256', headers=headers)
return sig.decode("utf-8")
def _request(self, method, path, signed, **kwargs):
data = kwargs.get('data', None)
if signed:
kwargs['headers'] = {
'Authorization': 'Bearer {}'.format(self._create_signature())
}
uri = self._create_uri(path)
if method == 'get' and data:
kwargs['params'] = kwargs['data']
del(kwargs['data'])
if method == 'post' and data:
kwargs['json'] = kwargs['data']
del(kwargs['data'])
response = getattr(self.session, method)(uri, **kwargs)
return self._handle_response(response)
def _handle_response(self, response):
"""Internal helper for handling API responses from the Quoine server.
Raises the appropriate exceptions when necessary; otherwise, returns the
response.
"""
if not str(response.status_code).startswith('2'):
raise BigoneAPIException(response)
try:
json = response.json()
if 'msg' in json or 'errors' in json:
raise BigoneAPIException(response)
# by default return full response
res = json
# if it's a normal response we have a data attribute, return that
if 'data' in json:
res = json['data']
return res
except ValueError:
raise BigoneRequestException('Invalid Response: {}'.format(response.text))
def _get(self, path, signed=False, **kwargs):
return self._request('get', path, signed, **kwargs)
def _post(self, path, signed=False, **kwargs):
return self._request('post', path, signed, **kwargs)
def _put(self, path, signed=False, **kwargs):
return self._request('put', path, signed, **kwargs)
def _delete(self, path, signed=False, **kwargs):
return self._request('delete', path, signed, **kwargs)
# Account endpoints
[docs] def get_accounts(self):
"""List accounts of current user
.. code:: python
accounts = client.get_accounts()
:return: list of dicts
.. code:: python
[
{
"type": "account",
"user_id": "5c1b7700-c903-41f8-9b4c-d78be9b2685d",
"account_id": "7eb4201b-9ae8-450d-819a-1c7dfd272e3d",
"account_type": "BTC",
"account_name": "Bitcoin",
"account_logo_url": "https://storage.googleapis.com/big-one/coins/BTC.svg",
"public_key": "16zy7YGERPEygQitMFaqP4afPJBZeo2WK6",
"system_account": "",
"active_balance": "0.00000000",
"frozen_balance": "0.00000000",
"estimated_btc_price": "1.00000000",
"verification": "EMAIL_VERIFIED",
"has_asset_pin": false
},
{
"type": "account",
"user_id": "5c1b7700-c903-41f8-9b4c-d78be9b2685d",
"account_id": "3d7c2353-add3-4e03-8637-c78e4d2b17cb",
"account_type": "ETH",
"account_name": "Ether",
"account_logo_url": "https://storage.googleapis.com/big-one/coins/ETH.svg",
"public_key": "0xaefde180aae6e0916632dabfdd2f8733c8a03826",
"system_account": "",
"active_balance": "0.00000000",
"frozen_balance": "0.00000000",
"estimated_btc_price": "0.06500000",
"verification": "EMAIL_VERIFIED",
"has_asset_pin": false
}
]
:raises: BigoneRequestException, BigoneAPIException
"""
return self._get('viewer/accounts', True)
[docs] def get_account(self, currency):
"""Get account for a currency
:param currency: Name of currency
:type currency: str
.. code:: python
account = client.get_account('BTC')
:return: dict
.. code:: python
{
"type": "account",
"user_id": "5c1b7700-c903-41f8-9b4c-d78be9b2685d",
"account_id": "7eb4201b-9ae8-450d-819a-1c7dfd272e3d",
"account_type": "BTC",
"account_name": "Bitcoin",
"account_logo_url": "https://storage.googleapis.com/big-one/coins/BTC.svg",
"public_key": "16zy7YGERPEygQitMFaqP4afPJBZeo2WK6",
"system_account": "",
"active_balance": "0.00000000",
"frozen_balance": "0.00000000",
"estimated_btc_price": "1.00000000",
"verification": "EMAIL_VERIFIED",
"has_asset_pin": false,
"withdrawal": {
"fee": "0.002",
"fee_type": "BTC"
},
"deposits": [],
"withdrawals": [],
"recipients": []
}
:raises: BigoneRequestException, BigoneAPIException
"""
return self._get('accounts/{}'.format(currency), True)
# Market endpoints
[docs] def get_markets(self):
"""List markets
https://open.big.one/docs/api_market.html#all-markets
.. code:: python
markets = client.get_markets()
:return: list of dicts
.. code:: python
[
{
"uuid": "d2185614-50c3-4588-b146-b8afe7534da6",
"quoteScale": 8,
"quoteAsset": {
"uuid": "0df9c3c3-255a-46d7-ab82-dedae169fba9",
"symbol": "BTC",
"name": "Bitcoin"
},
"name": "BTG/BTC",
"baseScale": 4,
"baseAsset": {
"uuid": "5df3b155-80f5-4f5a-87f6-a92950f0d0ff",
"symbol": "BTG",
"name": "Bitcoin Gold"
}
}
]
:raises: BigoneRequestException, BigoneAPIException
"""
return self._get('markets')
[docs] def get_tickers(self):
"""List market tickers
https://open.big.one/docs/api_tickers.html#tickers-of-all-market
.. code:: python
markets = client.get_tickers()
:return: list of dicts
.. code:: python
[
{
"volume": null,
"open": "1.0000000000000000",
"market_uuid": "ETH-EOS",
"low": null,
"high": null,
"daily_change_perc": "0",
"daily_change": "0E-16",
"close": "1.0000000000000000",
"bid": {
"price": "1.0000000000000000",
"amount": "106.0000000000000000"
},
"ask": {
"price": "45.0000000000000000",
"amount": "4082.3283464000000000"
}
}
]
:raises: BigoneRequestException, BigoneAPIException
"""
return self._get('tickers')
[docs] def get_ticker(self, symbol):
"""Get symbol market details
https://open.big.one/docs/api_tickers.html#ticker-of-one-market
:param symbol: Name of symbol
:type symbol: str
.. code:: python
# using market ID
market = client.get_ticker('ETH-BTC')
# using market UUID
market = client.get_ticker('d2185614-50c3-4588-b146-b8afe7534da6')
:return: dict
.. code:: python
{
"volume": null,
"open": "42.0000000000000000",
"market_uuid": "BTC-EOS",
"low": "42.0000000000000000",
"high": null,
"daily_change_perc": "0",
"daily_change": "0E-16",
"close": "42.0000000000000000",
"bid": {
"price": "42.0000000000000000",
"amount": "3.3336371100000000"
},
"ask": {
"price": "45.0000000000000000",
"amount": "4082.3283464000000000"
}
}
:raises: BigoneRequestException, BigoneAPIException
"""
return self._get('markets/{}/ticker'.format(symbol))
[docs] def get_order_book(self, symbol):
"""Get symbol market details
:param symbol: Name of symbol
:type symbol: str
.. code:: python
# Using market ID
book = client.get_order_book('ETH-BTC')
# Using market UUID
book = client.get_order_book('d2185614-50c3-4588-b146-b8afe7534da6')
:return: dict
.. code:: python
{
"market_uuid": "BTC-EOS",
"bids": [
{
"price": "42",
"order_count": 4,
"amount": "23.33363711"
}
],
"asks": [
{
"price": "45",
"order_count": 2,
"amount": "4193.3283464"
}
]
}
:raises: BigoneRequestException, BigoneAPIException
"""
return self._get('markets/{}/depth'.format(symbol))
[docs] def get_market_trades(self, symbol, after=None, before=None, first=None, last=None):
"""Get market trades - max 50
https://open.big.one/docs/api_market_trade.html#trades-of-a-market
:param symbol: Name of symbol
:type symbol: str
:param after: Return trades after this id
:type after: int
:param before: Return trades before this id
:type before: int
:param first: Slicing count
:type first: int
:param last: Slicing count
:type last: int
.. code:: python
trades = client.get_market_trades('ETH-BTC')
# using after trade ID
trades = client.get_market_trades('ETH-BTC', after=1)
# using first slice value
trades = client.get_market_trades('ETH-BTC', first=20)
:return: list of dicts
.. code:: python
{
"edges": [
{
"node": {
"taker_side": "BID",
"price": "46.1450000000000000",
"market_uuid": "BTC-EOS",
"id": 1,
"amount": "0.2465480000000000"
},
"cursor": "dGVzdGN1cmVzZQo="
}
],
"page_info": {
"end_cursor": "dGVzdGN1cmVzZQo=",
"start_cursor": "dGVzdGN1cmVzZQo=",
"has_next_page": true,
"has_previous_page": false
}
}
:raises: BigoneRequestException, BigoneAPIException
"""
data = {}
if after:
data['after'] = after
if before:
data['before'] = before
if first:
data['first'] = first
if last:
data['last'] = last
return self._get('markets/{}/trades'.format(symbol), data=data)
# Order Endpoints
[docs] def create_order(self, symbol, side, price, amount):
"""Create a new order
:param symbol: Name of symbol
:type symbol: str
:param side: side of order (BID or ASK)
:type side: str
:param price: Price as string
:type price: str
:param amount: Amount as string
:type amount: str
.. code:: python
order = client.create_order('ETH-BTC', 'BID', '1.0', '1.0')
:return: dict
.. code:: python
{
"id": 10,
"market_uuid": "BTC-EOS",
"price": "10.00",
"amount": "10.00",
"filled_amount": "9.0",
"avg_deal_price": "12.0",
"side": "ASK",
"state": "FILLED"
}
:raises: BigoneRequestException, BigoneAPIException
"""
data = {
'market_id': symbol,
'side': side,
'price': price,
'amount': amount
}
return self._post('viewer/orders', True, data=data)
[docs] def get_orders(self, symbol, after=None, before=None, first=None, last=None, side=None, state=None):
"""Get a list of orders
:param symbol: Name of symbol
:type symbol: str
:param after: Return trades after this id
:type after: int
:param before: Return trades before this id
:type before: int
:param first: Slicing count
:type first: int
:param last: Slicing count
:type last: int
:param side: Order Side ASK|BID
:type side: str
:param state: Order State CANCELED|FILLED|PENDING
:type state: str
.. code:: python
orders = client.get_orders('ETH-BTC')
:return: dict
.. code:: python
{
"edges": [
{
"node": {
"id": 10,
"market_uuid": "d2185614-50c3-4588-b146-b8afe7534da6",
"price": "10.00",
"amount": "10.00",
"filled_amount": "9.0",
"avg_deal_price": "12.0",
"side": "ASK",
"state": "FILLED"
},
"cursor": "dGVzdGN1cmVzZQo="
}
],
"page_info": {
"end_cursor": "dGVzdGN1cmVzZQo=",
"start_cursor": "dGVzdGN1cmVzZQo=",
"has_next_page": true,
"has_previous_page": false
}
}
:raises: BigoneRequestException, BigoneAPIException
"""
data = {
'market_id': symbol
}
if after:
data['after'] = after
if before:
data['before'] = before
if first:
data['first'] = first
if last:
data['last'] = last
if side:
data['side'] = side
if state:
data['state'] = state
return self._get('viewer/orders', True, data=data)
[docs] def get_order(self, order_id):
"""Get an order
https://open.big.one/docs/api_orders.html#get-one-order
:param order_id: Id of order
:type order_id: str
.. code:: python
orders = client.get_order('10')
:return: dict
.. code:: python
{
"id": 10,
"market_uuid": "d2185614-50c3-4588-b146-b8afe7534da6",
"price": "10.00",
"amount": "10.00",
"filled_amount": "9.0",
"avg_deal_price": "12.0",
"side": "ASK",
"state": "FILLED"
}
:raises: BigoneRequestException, BigoneAPIException
"""
return self._get('viewer/orders/{}'.format(order_id), True)
[docs] def cancel_order(self, order_id):
"""Cancel an order
https://open.big.one/docs/api_orders.html#cancle-order
:param order_id: Id of order
:type order_id: str
.. code:: python
res = client.cancel_order('10')
:return: dict
.. code:: python
{}
:raises: BigoneRequestException, BigoneAPIException
"""
return self._post('viewer/orders/{}/cancel'.format(order_id), True)
[docs] def cancel_orders(self):
"""Cancel all orders
https://open.big.one/docs/api_orders.html#cancle-all-orders
.. code:: python
res = client.cancel_orders()
:return: dict
.. code:: python
{}
:raises: BigoneRequestException, BigoneAPIException
"""
return self._post('viewer/orders/cancel_all', True)
# Trade endpoints
[docs] def get_trades(self, symbol=None, after=None, before=None, first=None, last=None):
"""Get a list of your trades
:param symbol: Name of symbol
:type symbol: str
:type after: int
:param before: Return trades before this id
:type before: int
:param first: Slicing count
:type first: int
:param last: Slicing count
:type last: int
.. code:: python
trades = client.get_trades('ETH-BTC')
:return: dict
.. code:: python
{
"edges": [
{
"node": {
"viewer_side": "ASK" // ASK, BID, SELF_TRADING
"taker_side": "BID",
"price": "46.1450000000000000",
"market_uuid": "BTC-EOS",
"id": 1,
"amount": "0.2465480000000000"
},
"cursor": "dGVzdGN1cmVzZQo="
}
],
"page_info": {
"end_cursor": "dGVzdGN1cmVzZQo=",
"start_cursor": "dGVzdGN1cmVzZQo=",
"has_next_page": true,
"has_previous_page": false
}
}
:raises: BigoneRequestException, BigoneAPIException
"""
data = {}
if symbol:
data['market_id'] = symbol
if after:
data['after'] = after
if before:
data['before'] = before
if first:
data['first'] = first
if last:
data['last'] = last
return self._get('viewer/trades', True, data=data)
# Withdraw endpoints
[docs] def withdrawals(self, first=None, after=None):
"""Get a list of withdrawals
https://open.big.one/docs/api_withdrawal.html#get-withdrawals-of-user
:param first: Slicing count
:type first: str
:param after: Return withdrawals after this value
:type after: str
.. code:: python
withdrawal = client.withdrawals()
:return: dict
.. code:: python
{
"edges": [
{
"node": {
"id": 10,
"customer_id": "ETH",
"asset_uuid": "ETH",
"amount": "5",
"state": "CONFIRMED",
"note": "2018-03-15T16:13:45.610463Z",
"txid": "0x4643bb6b393ac20a6175c713175734a72517c63d6f73a3ca90a15356f2e967da0",
"completed_at": "2018-03-15T16:13:45.610463Z",
"inserted_at": "2018-03-15T16:13:45.610463Z",
"is_internal": true,
"target_address": "0x4643bb6b393ac20a6175c713175734a72517c63d6f7"
},
"cursor": "dGVzdGN1cmVzZQo="
}
],
"page_info": {
"end_cursor": "dGVzdGN1cmVzZQo=",
"start_cursor": "dGVzdGN1cmVzZQo=",
"has_next_page": true,
"has_previous_page": false
}
}
:raises: BigoneRequestException, BigoneAPIException
"""
data = {}
if after:
data['after'] = after
if first:
data['first'] = first
return self._get('viewer/withdrawals', True, data=data)
# Deposit endpoints
[docs] def get_deposits(self, first=None, after=None):
"""Get a list of deposits
https://open.big.one/docs/api_deposit.html#deposit-of-user
:param first: Slicing count
:type first: str
:param after: Return withdrawals after this value
:type after: str
.. code:: python
# get all deposits
deposits = client.get_deposits()
# get BTC deposits
deposits = client.get_deposits('BTC')
:return: list of dicts
.. code:: python
[
{
"type": "deposit",
"deposit_id": "7b76603c-db87-4c4a-b36f-9981058d885e",
"deposit_type": "ETH",
"amount": "1.00000000",
"confirmations": 2231,
"state": "confirmed",
"scanner_url": "https://etherscan.io/tx/0x181a1d616b5b07d5a404636c527ca5432878664d4b85a20976bbd72c07e58abe",
"created_at": "2017-09-06T07:53:41.297047856Z"
}
]
:raises: BigoneRequestException, BigoneAPIException
"""
data = {}
if after:
data['after'] = after
if first:
data['first'] = first
return self._get('viewer/deposits', True, data=data)