import base64
import calendar
import hashlib
import hmac
import time
from datetime import datetime
import requests
from .exceptions import (
KucoinAPIException, KucoinRequestException, MarketOrderException, LimitOrderException
)
from .utils import compact_json_dict, flat_uuid
[docs]class Client(object):
REST_API_URL = 'https://openapi-v2.kucoin.com'
SANDBOX_API_URL = 'https://openapi-sandbox.kucoin.com'
API_VERSION = 'v1'
SIDE_BUY = 'buy'
SIDE_SELL = 'sell'
ACCOUNT_MAIN = 'main'
ACCOUNT_TRADE = 'trade'
ORDER_LIMIT = 'limit'
ORDER_MARKET = 'market'
ORDER_LIMIT_STOP = 'limit_stop'
ORDER_MARKET_STOP = 'market_stop'
STOP_LOSS = 'loss'
STOP_ENTRY = 'entry'
STP_CANCEL_NEWEST = 'CN'
STP_CANCEL_OLDEST = 'CO'
STP_DECREASE_AND_CANCEL = 'DC'
STP_CANCEL_BOTH = 'CB'
TIMEINFORCE_GOOD_TILL_CANCELLED = 'GTC'
TIMEINFORCE_GOOD_TILL_TIME = 'GTT'
TIMEINFORCE_IMMEDIATE_OR_CANCEL = 'IOC'
TIMEINFORCE_FILL_OR_KILL = 'FOK'
[docs] def __init__(self, api_key, api_secret, passphrase, sandbox=False, requests_params=None):
"""Kucoin API Client constructor
https://docs.kucoin.com/
:param api_key: Api Token Id
:type api_key: string
:param api_secret: Api Secret
:type api_secret: string
:param passphrase: Api Passphrase used to create API
:type passphrase: string
:param sandbox: (optional) Use the sandbox endpoint or not (default False)
:type sandbox: bool
:param requests_params: (optional) Dictionary of requests params to use for all calls
:type requests_params: dict.
.. code:: python
client = Client(api_key, api_secret, api_passphrase)
"""
self.API_KEY = api_key
self.API_SECRET = api_secret
self.API_PASSPHRASE = passphrase
if sandbox:
self.API_URL = self.SANDBOX_API_URL
else:
self.API_URL = self.REST_API_URL
self._requests_params = requests_params
self.session = self._init_session()
def _init_session(self):
session = requests.session()
headers = {'Accept': 'application/json',
'User-Agent': 'python-kucoin',
'Content-Type': 'application/json',
'KC-API-KEY': self.API_KEY,
'KC-API-PASSPHRASE': self.API_PASSPHRASE}
session.headers.update(headers)
return session
@staticmethod
def _get_params_for_sig(data):
"""Convert params to ordered string for signature
:param data:
:return: ordered parameters like amount=10&price=1.1&type=BUY
"""
return '&'.join(["{}={}".format(key, data[key]) for key in data])
def _generate_signature(self, nonce, method, path, data):
"""Generate the call signature
:param path:
:param data:
:param nonce:
:return: signature string
"""
data_json = ""
endpoint = path
if method == "get":
if data:
query_string = self._get_params_for_sig(data)
endpoint = "{}?{}".format(path, query_string)
elif data:
data_json = compact_json_dict(data)
sig_str = ("{}{}{}{}".format(nonce, method.upper(), endpoint, data_json)).encode('utf-8')
m = hmac.new(self.API_SECRET.encode('utf-8'), sig_str, hashlib.sha256)
return base64.b64encode(m.digest())
def _create_path(self, path):
return '/api/{}/{}'.format(self.API_VERSION, path)
def _create_uri(self, path):
return '{}{}'.format(self.API_URL, path)
def _request(self, method, path, signed, **kwargs):
# set default requests timeout
kwargs['timeout'] = 10
# add our global requests params
if self._requests_params:
kwargs.update(self._requests_params)
kwargs['data'] = kwargs.get('data', {})
kwargs['headers'] = kwargs.get('headers', {})
full_path = self._create_path(path)
uri = self._create_uri(full_path)
if signed:
# generate signature
nonce = int(time.time() * 1000)
kwargs['headers']['KC-API-TIMESTAMP'] = str(nonce)
kwargs['headers']['KC-API-SIGN'] = self._generate_signature(nonce, method, full_path, kwargs['data'])
if kwargs['data'] and method == 'get':
kwargs['params'] = kwargs['data']
del(kwargs['data'])
if signed and method != 'get' and kwargs['data']:
kwargs['data'] = compact_json_dict(kwargs['data'])
response = getattr(self.session, method)(uri, **kwargs)
return self._handle_response(response)
@staticmethod
def _handle_response(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 KucoinAPIException(response)
try:
res = response.json()
if 'code' in res and res['code'] != "200000":
raise KucoinAPIException(response)
if 'success' in res and not res['success']:
raise KucoinAPIException(response)
# by default return full response
# if it's a normal response we have a data attribute, return that
if 'data' in res:
res = res['data']
return res
except ValueError:
raise KucoinRequestException('Invalid Response: %s' % 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)
[docs] def get_timestamp(self):
"""Get the server timestamp
https://docs.kucoin.com/#time
:return: response timestamp in ms
"""
return self._get("timestamp")
# Currency Endpoints
[docs] def get_currencies(self):
"""List known currencies
https://docs.kucoin.com/#get-currencies
.. code:: python
currencies = client.get_currencies()
:returns: API Response
.. code-block:: python
[
{
"currency": "BTC",
"name": "BTC",
"fullName": "Bitcoin",
"precision": 8
},
{
"currency": "ETH",
"name": "ETH",
"fullName": "Ethereum",
"precision": 7
}
]
:raises: KucoinResponseException, KucoinAPIException
"""
return self._get('currencies', False)
[docs] def get_currency(self, currency):
"""Get single currency detail
https://docs.kucoin.com/#get-currency-detail
.. code:: python
# call with no coins
currency = client.get_currency('BTC')
:returns: API Response
.. code-block:: python
{
"currency": "BTC",
"name": "BTC",
"fullName": "Bitcoin",
"precision": 8,
"withdrawalMinSize": "0.002",
"withdrawalMinFee": "0.0005",
"isWithdrawEnabled": true,
"isDepositEnabled": true
}
:raises: KucoinResponseException, KucoinAPIException
"""
return self._get('currencies/{}'.format(currency), False)
# User Account Endpoints
[docs] def get_accounts(self):
"""Get a list of accounts
https://docs.kucoin.com/#accounts
.. code:: python
accounts = client.get_accounts()
:returns: API Response
.. code-block:: python
[
{
"id": "5bd6e9286d99522a52e458de",
"currency": "BTC",
"type": "main",
"balance": "237582.04299",
"available": "237582.032",
"holds": "0.01099"
},
{
"id": "5bd6e9216d99522a52e458d6",
"currency": "BTC",
"type": "trade",
"balance": "1234356",
"available": "1234356",
"holds": "0"
}
]
:raises: KucoinResponseException, KucoinAPIException
"""
return self._get('accounts', True)
[docs] def get_account(self, account_id):
"""Get an individual account
https://docs.kucoin.com/#get-an-account
:param account_id: ID for account - from list_accounts()
:type account_id: string
.. code:: python
account = client.get_account('5bd6e9216d99522a52e458d6')
:returns: API Response
.. code-block:: python
{
"currency": "KCS",
"balance": "1000000060.6299",
"available": "1000000060.6299",
"holds": "0"
}
:raises: KucoinResponseException, KucoinAPIException
"""
return self._get('accounts/{}'.format(account_id), True)
[docs] def create_account(self, account_type, currency):
"""Create an account
https://docs.kucoin.com/#create-an-account
:param account_type: Account type - main or trade
:type account_type: string
:param currency: Currency code
:type currency: string
.. code:: python
account = client.create_account('trade', 'BTC')
:returns: API Response
.. code-block:: python
{
"id": "5bd6e9286d99522a52e458de"
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {
'type': account_type,
'currency': currency
}
return self._post('accounts', True, data=data)
[docs] def get_account_activity(self, account_id, start=None, end=None, page=None, limit=None):
"""Get list of account activity
https://docs.kucoin.com/#get-account-history
:param account_id: ID for account - from list_accounts()
:type account_id: string
:param start: (optional) Start time as unix timestamp
:type start: string
:param end: (optional) End time as unix timestamp
:type end: string
:param page: (optional) Current page - default 1
:type page: int
:param limit: (optional) Number of results to return - default 50
:type limit: int
.. code:: python
history = client.get_account_activity('5bd6e9216d99522a52e458d6')
history = client.get_account_activity('5bd6e9216d99522a52e458d6', start='1540296039000')
history = client.get_account_activity('5bd6e9216d99522a52e458d6', page=2, page_size=10)
:returns: API Response
.. code-block:: python
{
"currentPage": 1,
"pageSize": 10,
"totalNum": 2,
"totalPage": 1,
"items": [
{
"currency": "KCS",
"amount": "0.0998",
"fee": "0",
"balance": "1994.040596",
"bizType": "withdraw",
"direction": "in",
"createdAt": 1540296039000,
"context": {
"orderId": "5bc7f080b39c5c03286eef8a",
"currency": "BTC"
}
},
{
"currency": "KCS",
"amount": "0.0998",
"fee": "0",
"balance": "1994.140396",
"bizType": "trade exchange",
"direction": "in",
"createdAt": 1540296039000,
"context": {
"orderId": "5bc7f080b39c5c03286eef8e",
"tradeId": "5bc7f080b3949c03286eef8a",
"symbol": "BTC-USD"
}
}
]
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {}
if start:
data['startAt'] = start
if end:
data['endAt'] = end
if page:
data['currentPage'] = page
if limit:
data['pageSize'] = limit
return self._get('accounts/{}/ledgers'.format(account_id), True, data=data)
[docs] def get_account_holds(self, account_id, page=None, page_size=None):
"""Get account holds placed for any active orders or pending withdraw requests
https://docs.kucoin.com/#get-holds
:param account_id: ID for account - from list_accounts()
:type account_id: string
:param page: (optional) Current page - default 1
:type page: int
:param page_size: (optional) Number of results to return - default 50
:type page_size: int
.. code:: python
holds = client.get_account_holds('5bd6e9216d99522a52e458d6')
holds = client.get_account_holds('5bd6e9216d99522a52e458d6', page=2, page_size=10)
:returns: API Response
.. code-block:: python
{
"currentPage": 1,
"pageSize": 10,
"totalNum": 2,
"totalPage": 1,
"items": [
{
"currency": "ETH",
"holdAmount": "5083",
"bizType": "Withdraw",
"orderId": "5bc7f080b39c5c03286eef8e",
"createdAt": 1545898567000,
"updatedAt": 1545898567000
},
{
"currency": "ETH",
"holdAmount": "1452",
"bizType": "Withdraw",
"orderId": "5bc7f518b39c5c033818d62d",
"createdAt": 1545898567000,
"updatedAt": 1545898567000
}
]
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {}
if page:
data['currentPage'] = page
if page_size:
data['pageSize'] = page_size
return self._get('accounts/{}/holds'.format(account_id), True, data=data)
[docs] def create_inner_transfer(self, from_account_id, to_account_id, amount, order_id=None):
"""Get account holds placed for any active orders or pending withdraw requests
https://docs.kucoin.com/#get-holds
:param from_account_id: ID of account to transfer funds from - from list_accounts()
:type from_account_id: str
:param to_account_id: ID of account to transfer funds to - from list_accounts()
:type to_account_id: str
:param amount: Amount to transfer
:type amount: int
:param order_id: (optional) Request ID (default flat_uuid())
:type order_id: string
.. code:: python
transfer = client.create_inner_transfer('5bd6e9216d99522a52e458d6', 5bc7f080b39c5c03286eef8e', 20)
:returns: API Response
.. code-block:: python
{
"orderId": "5bd6e9286d99522a52e458de"
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {
'payAccountId': from_account_id,
'recAccountId': to_account_id,
'amount': amount
}
if order_id:
data['clientOid'] = order_id
else:
data['clientOid'] = flat_uuid()
return self._post('accounts/inner-transfer', True, data=data)
# Deposit Endpoints
[docs] def create_deposit_address(self, currency):
"""Create deposit address of currency for deposit. You can just create one deposit address.
https://docs.kucoin.com/#create-deposit-address
:param currency: Name of currency
:type currency: string
.. code:: python
address = client.create_deposit_address('NEO')
:returns: ApiResponse
.. code:: python
{
"address": "0x78d3ad1c0aa1bf068e19c94a2d7b16c9c0fcd8b1",
"memo": "5c247c8a03aa677cea2a251d"
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {
'currency': currency
}
return self._post('deposit-addresses', True, data=data)
[docs] def get_deposit_address(self, currency):
"""Get deposit address for a currency
https://docs.kucoin.com/#get-deposit-address
:param currency: Name of currency
:type currency: string
.. code:: python
address = client.get_deposit_address('NEO')
:returns: ApiResponse
.. code:: python
{
"address": "0x78d3ad1c0aa1bf068e19c94a2d7b16c9c0fcd8b1",
"memo": "5c247c8a03aa677cea2a251d"
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {
'currency': currency
}
return self._get('deposit-addresses', True, data=data)
[docs] def get_deposits(self, currency=None, status=None, start=None, end=None, page=None, limit=None):
"""Get deposit records for a currency
https://docs.kucoin.com/#get-deposit-list
:param currency: Name of currency (optional)
:type currency: string
:param status: optional - Status of deposit (PROCESSING, SUCCESS, FAILURE)
:type status: string
:param start: (optional) Start time as unix timestamp
:type start: string
:param end: (optional) End time as unix timestamp
:type end: string
:param page: (optional) Page to fetch
:type page: int
:param limit: (optional) Number of transactions
:type limit: int
.. code:: python
deposits = client.get_deposits('NEO')
:returns: ApiResponse
.. code:: python
{
"currentPage": 1,
"pageSize": 5,
"totalNum": 2,
"totalPage": 1,
"items": [
{
"address": "0x5f047b29041bcfdbf0e4478cdfa753a336ba6989",
"memo": "5c247c8a03aa677cea2a251d",
"amount": 1,
"fee": 0.0001,
"currency": "KCS",
"isInner": false,
"walletTxId": "5bbb57386d99522d9f954c5a@test004",
"status": "SUCCESS",
"createdAt": 1544178843000,
"updatedAt": 1544178891000
}, {
"address": "0x5f047b29041bcfdbf0e4478cdfa753a336ba6989",
"memo": "5c247c8a03aa677cea2a251d",
"amount": 1,
"fee": 0.0001,
"currency": "KCS",
"isInner": false,
"walletTxId": "5bbb57386d99522d9f954c5a@test003",
"status": "SUCCESS",
"createdAt": 1544177654000,
"updatedAt": 1544178733000
}
]
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {}
if currency:
data['currency'] = currency
if status:
data['status'] = status
if start:
data['startAt'] = start
if end:
data['endAt'] = end
if limit:
data['pageSize'] = limit
if page:
data['page'] = page
return self._get('deposits', True, data=data)
# Withdraw Endpoints
[docs] def get_withdrawals(self, currency=None, status=None, start=None, end=None, page=None, limit=None):
"""Get deposit records for a currency
https://docs.kucoin.com/#get-withdrawals-list
:param currency: Name of currency (optional)
:type currency: string
:param status: optional - Status of deposit (PROCESSING, SUCCESS, FAILURE)
:type status: string
:param start: (optional) Start time as unix timestamp
:type start: string
:param end: (optional) End time as unix timestamp
:type end: string
:param page: (optional) Page to fetch
:type page: int
:param limit: (optional) Number of transactions
:type limit: int
.. code:: python
withdrawals = client.get_withdrawals('NEO')
:returns: ApiResponse
.. code:: python
{
"currentPage": 1,
"pageSize": 10,
"totalNum": 1,
"totalPage": 1,
"items": [
{
"id": "5c2dc64e03aa675aa263f1ac",
"address": "0x5bedb060b8eb8d823e2414d82acce78d38be7fe9",
"memo": "",
"currency": "ETH",
"amount": 1.0000000,
"fee": 0.0100000,
"walletTxId": "3e2414d82acce78d38be7fe9",
"isInner": false,
"status": "FAILURE",
"createdAt": 1546503758000,
"updatedAt": 1546504603000
}
]
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {}
if currency:
data['currency'] = currency
if status:
data['status'] = status
if start:
data['startAt'] = start
if end:
data['endAt'] = end
if limit:
data['pageSize'] = limit
if page:
data['page'] = page
return self._get('withdrawals', True, data=data)
[docs] def get_withdrawal_quotas(self, currency):
"""Get withdrawal quotas for a currency
https://docs.kucoin.com/#get-withdrawal-quotas
:param currency: Name of currency
:type currency: string
.. code:: python
quotas = client.get_withdrawal_quotas('ETH')
:returns: ApiResponse
.. code:: python
{
"currency": "ETH",
"availableAmount": 2.9719999,
"remainAmount": 2.9719999,
"withdrawMinSize": 0.1000000,
"limitBTCAmount": 2.0,
"innerWithdrawMinFee": 0.00001,
"isWithdrawEnabled": true,
"withdrawMinFee": 0.0100000,
"precision": 7
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {
'currency': currency
}
return self._get('withdrawals/quotas', True, data=data)
[docs] def create_withdrawal(self, currency, amount, address, memo=None, is_inner=False, remark=None):
"""Process a withdrawal
https://docs.kucoin.com/#apply-withdraw
:param currency: Name of currency
:type currency: string
:param amount: Amount to withdraw
:type amount: number
:param address: Address to withdraw to
:type address: string
:param memo: (optional) Remark to the withdrawal address
:type memo: string
:param is_inner: (optional) Remark to the withdrawal address
:type is_inner: bool
:param remark: (optional) Remark
:type remark: string
.. code:: python
withdrawal = client.create_withdrawal('NEO', 20, '598aeb627da3355fa3e851')
:returns: ApiResponse
.. code:: python
{
"withdrawalId": "5bffb63303aa675e8bbe18f9"
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {
'currency': currency,
'amount': amount,
'address': address
}
if memo:
data['memo'] = memo
if is_inner:
data['isInner'] = is_inner
if remark:
data['remark'] = remark
return self._post('withdrawals', True, data=data)
[docs] def cancel_withdrawal(self, withdrawal_id):
"""Cancel a withdrawal
https://docs.kucoin.com/#cancel-withdrawal
:param withdrawal_id: ID of withdrawal
:type withdrawal_id: string
.. code:: python
client.cancel_withdrawal('5bffb63303aa675e8bbe18f9')
:returns: None
:raises: KucoinResponseException, KucoinAPIException
"""
return self._delete('withdrawals/{}'.format(withdrawal_id), True)
# Order Endpoints
[docs] def create_market_order(self, symbol, side, size=None, funds=None, client_oid=None, remark=None, stp=None):
"""Create a market order
One of size or funds must be set
https://docs.kucoin.com/#place-a-new-order
:param symbol: Name of symbol e.g. KCS-BTC
:type symbol: string
:param side: buy or sell
:type side: string
:param size: (optional) Desired amount in base currency
:type size: string
:param funds: (optional) Desired amount of quote currency to use
:type funds: string
:param client_oid: (optional) Unique order id (default flat_uuid())
:type client_oid: string
:param remark: (optional) remark for the order, max 100 utf8 characters
:type remark: string
:param stp: (optional) self trade protection CN, CO, CB or DC (default is None)
:type stp: string
.. code:: python
order = client.create_market_order('NEO', Client.SIDE_BUY, size=20)
:returns: ApiResponse
.. code:: python
{
"orderOid": "596186ad07015679730ffa02"
}
:raises: KucoinResponseException, KucoinAPIException, MarketOrderException
"""
if not size and not funds:
raise MarketOrderException('Need size or fund parameter')
if size and funds:
raise MarketOrderException('Need size or fund parameter not both')
data = {
'side': side,
'symbol': symbol,
'type': self.ORDER_MARKET
}
if size:
data['size'] = size
if funds:
data['funds'] = funds
if client_oid:
data['clientOid'] = client_oid
else:
data['clientOid'] = flat_uuid()
if remark:
data['remark'] = remark
if stp:
data['stp'] = stp
return self._post('orders', True, data=data)
[docs] def create_limit_order(self, symbol, side, price, size, client_oid=None, remark=None,
time_in_force=None, stop=None, stop_price=None, stp=None, cancel_after=None, post_only=None,
hidden=None, iceberg=None, visible_size=None):
"""Create an order
https://docs.kucoin.com/#place-a-new-order
:param symbol: Name of symbol e.g. KCS-BTC
:type symbol: string
:param side: buy or sell
:type side: string
:param price: Name of coin
:type price: string
:param size: Amount of base currency to buy or sell
:type size: string
:param client_oid: (optional) Unique order_id default flat_uuid()
:type client_oid: string
:param remark: (optional) remark for the order, max 100 utf8 characters
:type remark: string
:param stp: (optional) self trade protection CN, CO, CB or DC (default is None)
:type stp: string
:param time_in_force: (optional) GTC, GTT, IOC, or FOK (default is GTC)
:type time_in_force: string
:param stop: (optional) stop type loss or entry - requires stop_price
:type stop: string
:param stop_price: (optional) trigger price for stop order
:type stop_price: string
:param cancel_after: (optional) number of seconds to cancel the order if not filled
required time_in_force to be GTT
:type cancel_after: string
:param post_only: (optional) indicates that the order should only make liquidity. If any part of
the order results in taking liquidity, the order will be rejected and no part of it will execute.
:type post_only: bool
:param hidden: (optional) Orders not displayed in order book
:type hidden: bool
:param iceberg: (optional) Only visible portion of the order is displayed in the order book
:type iceberg: bool
:param visible_size: (optional) The maximum visible size of an iceberg order
:type visible_size: bool
.. code:: python
order = client.create_limit_order('KCS-BTC', Client.SIDE_BUY, '0.01', '1000')
:returns: ApiResponse
.. code:: python
{
"orderOid": "596186ad07015679730ffa02"
}
:raises: KucoinResponseException, KucoinAPIException, LimitOrderException
"""
if stop and not stop_price:
raise LimitOrderException('Stop order needs stop_price')
if stop_price and not stop:
raise LimitOrderException('Stop order type required with stop_price')
if cancel_after and time_in_force != self.TIMEINFORCE_GOOD_TILL_TIME:
raise LimitOrderException('Cancel after only works with time_in_force = "GTT"')
if hidden and iceberg:
raise LimitOrderException('Order can be either "hidden" or "iceberg"')
if iceberg and not visible_size:
raise LimitOrderException('Iceberg order requires visible_size')
data = {
'symbol': symbol,
'side': side,
'type': self.ORDER_LIMIT,
'price': price,
'size': size
}
if client_oid:
data['clientOid'] = client_oid
else:
data['clientOid'] = flat_uuid()
if remark:
data['remark'] = remark
if stp:
data['stp'] = stp
if time_in_force:
data['timeInForce'] = time_in_force
if cancel_after:
data['cancelAfter'] = cancel_after
if post_only:
data['postOnly'] = post_only
if stop:
data['stop'] = stop
data['stopPrice'] = stop_price
if hidden:
data['hidden'] = hidden
if iceberg:
data['iceberg'] = iceberg
data['visible_size'] = visible_size
return self._post('orders', True, data=data)
[docs] def cancel_order(self, order_id):
"""Cancel an order
https://docs.kucoin.com/#cancel-an-order
:param order_id: Order id
:type order_id: string
.. code:: python
res = client.cancel_order('5bd6e9286d99522a52e458de)
:returns: ApiResponse
.. code:: python
{
"cancelledOrderIds": [
"5bd6e9286d99522a52e458de"
]
}
:raises: KucoinResponseException, KucoinAPIException
KucoinAPIException If order_id is not found
"""
return self._delete('orders/{}'.format(order_id), True)
[docs] def cancel_all_orders(self, symbol=None):
"""Cancel all orders
https://docs.kucoin.com/#cancel-all-orders
.. code:: python
res = client.cancel_all_orders()
:returns: ApiResponse
.. code:: python
{
"cancelledOrderIds": [
"5bd6e9286d99522a52e458de"
]
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {}
if symbol is not None:
data['symbol'] = symbol
return self._delete('orders', True, data=data)
[docs] def get_orders(self, symbol=None, status=None, side=None, order_type=None,
start=None, end=None, page=None, limit=None):
"""Get list of orders
https://docs.kucoin.com/#list-orders
:param symbol: (optional) Name of symbol e.g. KCS-BTC
:type symbol: string
:param status: (optional) Specify status active or done (default done)
:type status: string
:param side: (optional) buy or sell
:type side: string
:param order_type: (optional) limit, market, limit_stop or market_stop
:type order_type: string
:param start: (optional) Start time as unix timestamp
:type start: string
:param end: (optional) End time as unix timestamp
:type end: string
:param page: (optional) Page to fetch
:type page: int
:param limit: (optional) Number of orders
:type limit: int
.. code:: python
orders = client.get_orders(symbol='KCS-BTC', status='active')
:returns: ApiResponse
.. code:: python
{
"currentPage": 1,
"pageSize": 1,
"totalNum": 153408,
"totalPage": 153408,
"items": [
{
"id": "5c35c02703aa673ceec2a168",
"symbol": "BTC-USDT",
"opType": "DEAL",
"type": "limit",
"side": "buy",
"price": "10",
"size": "2",
"funds": "0",
"dealFunds": "0.166",
"dealSize": "2",
"fee": "0",
"feeCurrency": "USDT",
"stp": "",
"stop": "",
"stopTriggered": false,
"stopPrice": "0",
"timeInForce": "GTC",
"postOnly": false,
"hidden": false,
"iceberge": false,
"visibleSize": "0",
"cancelAfter": 0,
"channel": "IOS",
"clientOid": null,
"remark": null,
"tags": null,
"isActive": false,
"cancelExist": false,
"createdAt": 1547026471000
}
]
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {}
if symbol:
data['symbol'] = symbol
if status:
data['status'] = status
if side:
data['side'] = side
if order_type:
data['type'] = order_type
if start:
data['startAt'] = start
if end:
data['endAt'] = end
if page:
data['page'] = page
if limit:
data['pageSize'] = limit
return self._get('orders', True, data=data)
[docs] def get_historical_orders(self, symbol=None, side=None,
start=None, end=None, page=None, limit=None):
"""List of KuCoin V1 historical orders.
https://docs.kucoin.com/#get-v1-historical-orders-list
:param symbol: (optional) Name of symbol e.g. KCS-BTC
:type symbol: string
:param side: (optional) buy or sell
:type side: string
:param start: (optional) Start time as unix timestamp
:type start: string
:param end: (optional) End time as unix timestamp
:type end: string
:param page: (optional) Page to fetch
:type page: int
:param limit: (optional) Number of orders
:type limit: int
.. code:: python
orders = client.get_historical_orders(symbol='KCS-BTC')
:returns: ApiResponse
.. code:: python
{
"currentPage": 1,
"pageSize": 50,
"totalNum": 1,
"totalPage": 1,
"items": [
{
"symbol": "SNOV-ETH",
"dealPrice": "0.0000246",
"dealValue": "0.018942",
"amount": "770",
"fee": "0.00001137",
"side": "sell",
"createdAt": 1540080199
}
]
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {}
if symbol:
data['symbol'] = symbol
if side:
data['side'] = side
if start:
data['startAt'] = start
if end:
data['endAt'] = end
if page:
data['page'] = page
if limit:
data['pageSize'] = limit
return self._get('hist-orders', True, data=data)
[docs] def get_order(self, order_id):
"""Get order details
https://docs.kucoin.com/#get-an-order
:param order_id: orderOid value
:type order_id: str
.. code:: python
order = client.get_order('5c35c02703aa673ceec2a168')
:returns: ApiResponse
.. code:: python
{
"id": "5c35c02703aa673ceec2a168",
"symbol": "BTC-USDT",
"opType": "DEAL",
"type": "limit",
"side": "buy",
"price": "10",
"size": "2",
"funds": "0",
"dealFunds": "0.166",
"dealSize": "2",
"fee": "0",
"feeCurrency": "USDT",
"stp": "",
"stop": "",
"stopTriggered": false,
"stopPrice": "0",
"timeInForce": "GTC",
"postOnly": false,
"hidden": false,
"iceberge": false,
"visibleSize": "0",
"cancelAfter": 0,
"channel": "IOS",
"clientOid": null,
"remark": null,
"tags": null,
"isActive": false,
"cancelExist": false,
"createdAt": 1547026471000
}
:raises: KucoinResponseException, KucoinAPIException
"""
return self._get('orders/{}'.format(order_id), True)
# Fill Endpoints
[docs] def get_fills(self, order_id=None, symbol=None, side=None, order_type=None,
start=None, end=None, page=None, limit=None):
"""Get a list of recent fills.
https://docs.kucoin.com/#list-fills
:param order_id: (optional) generated order id
:type order_id: string
:param symbol: (optional) Name of symbol e.g. KCS-BTC
:type symbol: string
:param side: (optional) buy or sell
:type side: string
:param order_type: (optional) limit, market, limit_stop or market_stop
:type order_type: string
:param start: Start time as unix timestamp (optional)
:type start: string
:param end: End time as unix timestamp (optional)
:type end: string
:param page: optional - Page to fetch
:type page: int
:param limit: optional - Number of orders
:type limit: int
.. code:: python
fills = client.get_fills()
:returns: ApiResponse
.. code:: python
{
"currentPage":1,
"pageSize":1,
"totalNum":251915,
"totalPage":251915,
"items":[
{
"symbol":"BTC-USDT",
"tradeId":"5c35c02709e4f67d5266954e",
"orderId":"5c35c02703aa673ceec2a168",
"counterOrderId":"5c1ab46003aa676e487fa8e3",
"side":"buy",
"liquidity":"taker",
"forceTaker":true,
"price":"0.083",
"size":"0.8424304",
"funds":"0.0699217232",
"fee":"0",
"feeRate":"0",
"feeCurrency":"USDT",
"stop":"",
"type":"limit",
"createdAt":1547026472000
}
]
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {}
if order_id:
data['orderId'] = order_id
if symbol:
data['symbol'] = symbol
if side:
data['side'] = side
if order_type:
data['type'] = order_type
if start:
data['startAt'] = start
if end:
data['endAt'] = end
if page:
data['page'] = page
if limit:
data['pageSize'] = limit
return self._get('fills', True, data=data)
# Market Endpoints
[docs] def get_symbols(self):
"""Get a list of available currency pairs for trading.
https://docs.kucoin.com/#symbols-amp-ticker
.. code:: python
symbols = client.get_symbols()
:returns: ApiResponse
.. code:: python
[
{
"symbol": "BTC-USDT",
"name": "BTC-USDT",
"baseCurrency": "BTC",
"quoteCurrency": "USDT",
"baseMinSize": "0.00000001",
"quoteMinSize": "0.01",
"baseMaxSize": "10000",
"quoteMaxSize": "100000",
"baseIncrement": "0.00000001",
"quoteIncrement": "0.01",
"priceIncrement": "0.00000001",
"enableTrading": true
}
]
:raises: KucoinResponseException, KucoinAPIException
"""
return self._get('symbols', False)
[docs] def get_ticker(self, symbol=None):
"""Get symbol tick
https://docs.kucoin.com/#get-ticker
:param symbol: (optional) Name of symbol e.g. KCS-BTC
:type symbol: string
.. code:: python
all_ticks = client.get_ticker()
ticker = client.get_ticker('ETH-BTC')
:returns: ApiResponse
.. code:: python
{
"sequence": "1545825031840", # now sequence
"price": "3494.367783", # last trade price
"size": "0.05027185", # last trade size
"bestBid": "3494.367783", # best bid price
"bestBidSize": "2.60323254", # size at best bid price
"bestAsk": "3499.12", # best ask price
"bestAskSize": "0.01474011" # size at best ask price
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {}
tick_path = 'market/allTickers'
if symbol is not None:
tick_path = 'market/orderbook/level1'
data = {
'symbol': symbol
}
return self._get(tick_path, False, data=data)
[docs] def get_fiat_prices(self, base=None, symbol=None):
"""Get fiat price for currency
https://docs.kucoin.com/#get-fiat-price
:param base: (optional) Fiat,eg.USD,EUR, default is USD.
:type base: string
:param symbol: (optional) Cryptocurrencies.For multiple cyrptocurrencies, please separate them with
comma one by one. default is all
:type symbol: string
.. code:: python
prices = client.get_fiat_prices()
:returns: ApiResponse
.. code:: python
{
"BTC": "3911.28000000",
"ETH": "144.55492453",
"LTC": "48.45888179",
"KCS": "0.45546856"
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {}
if base is not None:
data['base'] = base
if symbol is not None:
data['currencies'] = symbol
return self._get('prices', False, data=data)
[docs] def get_24hr_stats(self, symbol):
"""Get 24hr stats for a symbol. Volume is in base currency units. open, high, low are in quote currency units.
:param symbol: (optional) Name of symbol e.g. KCS-BTC
:type symbol: string
.. code:: python
stats = client.get_24hr_stats('ETH-BTC')
:returns: ApiResponse
Without a symbol param
.. code:: python
{
"symbol": "BTC-USDT",
"changeRate": "0.0128", # 24h change rate
"changePrice": "0.8", # 24h rises and falls in price (if the change rate is a negative number,
# the price rises; if the change rate is a positive number, the price falls.)
"open": 61, # Opening price
"close": 63.6, # Closing price
"high": "63.6", # Highest price filled
"low": "61", # Lowest price filled
"vol": "244.78", # Transaction quantity
"volValue": "15252.0127" # Transaction amount
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {
'symbol': symbol
}
return self._get('market/stats', False, data=data)
[docs] def get_markets(self):
"""Get supported market list
https://docs.kucoin.com/#get-market-list
.. code:: python
markets = client.get_markets()
:returns: ApiResponse
.. code:: python
{
"data": [
"BTC",
"ETH",
"USDT"
]
}
:raises: KucoinResponseException, KucoinAPIException
"""
return self._get('markets', False)
[docs] def get_order_book(self, symbol):
"""Get a list of bids and asks aggregated by price for a symbol.
Returns up to 100 depth each side. Fastest Order book API
https://docs.kucoin.com/#get-part-order-book-aggregated
:param symbol: Name of symbol e.g. KCS-BTC
:type symbol: string
.. code:: python
orders = client.get_order_book('KCS-BTC')
:returns: ApiResponse
.. code:: python
{
"sequence": "3262786978",
"bids": [
["6500.12", "0.45054140"], # [price, size]
["6500.11", "0.45054140"]
],
"asks": [
["6500.16", "0.57753524"],
["6500.15", "0.57753524"]
]
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {
'symbol': symbol
}
return self._get('market/orderbook/level2_100', False, data=data)
[docs] def get_full_order_book(self, symbol):
"""Get a list of all bids and asks aggregated by price for a symbol.
This call is generally used by professional traders because it uses more server resources and traffic,
and Kucoin has strict access frequency control.
https://docs.kucoin.com/#get-full-order-book-aggregated
:param symbol: Name of symbol e.g. KCS-BTC
:type symbol: string
.. code:: python
orders = client.get_order_book('KCS-BTC')
:returns: ApiResponse
.. code:: python
{
"sequence": "3262786978",
"bids": [
["6500.12", "0.45054140"], # [price size]
["6500.11", "0.45054140"]
],
"asks": [
["6500.16", "0.57753524"],
["6500.15", "0.57753524"]
]
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {
'symbol': symbol
}
return self._get('market/orderbook/level2', False, data=data)
[docs] def get_full_order_book_level3(self, symbol):
"""Get a list of all bids and asks non-aggregated for a symbol.
This call is generally used by professional traders because it uses more server resources and traffic,
and Kucoin has strict access frequency control.
https://docs.kucoin.com/#get-full-order-book-atomic
:param symbol: Name of symbol e.g. KCS-BTC
:type symbol: string
.. code:: python
orders = client.get_order_book('KCS-BTC')
:returns: ApiResponse
.. code:: python
{
"sequence": "1545896707028",
"bids": [
[
"5c2477e503aa671a745c4057", # orderId
"6", # price
"0.999" # size
],
[
"5c2477e103aa671a745c4054",
"5",
"0.999"
]
],
"asks": [
[
"5c24736703aa671a745c401e",
"200",
"1"
],
[
"5c2475c903aa671a745c4033",
"201",
"1"
]
]
}
:raises: KucoinResponseException, KucoinAPIException
"""
data = {
'symbol': symbol
}
return self._get('market/orderbook/level3', False, data=data)
[docs] def get_trade_histories(self, symbol):
"""List the latest trades for a symbol
https://docs.kucoin.com/#get-trade-histories
:param symbol: Name of symbol e.g. KCS-BTC
:type symbol: string
.. code:: python
orders = client.get_trade_histories('KCS-BTC')
:returns: ApiResponse
.. code:: python
[
{
"sequence": "1545896668571",
"price": "0.07", # Filled price
"size": "0.004", # Filled amount
"side": "buy", # Filled side. The filled side is set to the taker by default.
"time": 1545904567062140823 # Transaction time
},
{
"sequence": "1545896668578",
"price": "0.054",
"size": "0.066",
"side": "buy",
"time": 1545904581619888405
}
]
:raises: KucoinResponseException, KucoinAPIException
"""
data = {
'symbol': symbol
}
return self._get('market/histories', False, data=data)
[docs] def get_kline_data(self, symbol, kline_type='5min', start=None, end=None):
"""Get kline data
For each query, the system would return at most 1500 pieces of data.
To obtain more data, please page the data by time.
:param symbol: Name of symbol e.g. KCS-BTC
:type symbol: string
:param kline_type: type of symbol, type of candlestick patterns: 1min, 3min, 5min, 15min, 30min, 1hour, 2hour,
4hour, 6hour, 8hour, 12hour, 1day, 1week
:type kline_type: string
:param start: Start time as unix timestamp (optional) default start of day in UTC
:type start: int
:param end: End time as unix timestamp (optional) default now in UTC
:type end: int
https://docs.kucoin.com/#get-historic-rates
.. code:: python
klines = client.get_kline_data('KCS-BTC', '5min', 1507479171, 1510278278)
:returns: ApiResponse
.. code:: python
[
[
"1545904980", //Start time of the candle cycle
"0.058", //opening price
"0.049", //closing price
"0.058", //highest price
"0.049", //lowest price
"0.018", //Transaction amount
"0.000945" //Transaction volume
],
[
"1545904920",
"0.058",
"0.072",
"0.072",
"0.058",
"0.103",
"0.006986"
]
]
:raises: KucoinResponseException, KucoinAPIException
"""
data = {
'symbol': symbol
}
if kline_type is not None:
data['type'] = kline_type
if start is not None:
data['startAt'] = start
else:
data['startAt'] = calendar.timegm(datetime.utcnow().date().timetuple())
if end is not None:
data['endAt'] = end
else:
data['endAt'] = int(time.time())
return self._get('market/candles', False, data=data)
# Websocket Endpoints
[docs] def get_ws_endpoint(self, private=False):
"""Get websocket channel details
:param private: Name of symbol e.g. KCS-BTC
:type private: bool
https://docs.kucoin.com/#websocket-feed
.. code:: python
ws_details = client.get_ws_endpoint(private=True)
:returns: ApiResponse
.. code:: python
{
"code": "200000",
"data": {
"instanceServers": [
{
"pingInterval": 50000,
"endpoint": "wss://push1-v2.kucoin.net/endpoint",
"protocol": "websocket",
"encrypt": true,
"pingTimeout": 10000
}
],
"token": "vYNlCtbz4XNJ1QncwWilJnBtmmfe4geLQDUA62kKJsDChc6I4bRDQc73JfIrlFaVYIAE0Gv2--MROnLAgjVsWkcDq_MuG7qV7EktfCEIphiqnlfpQn4Ybg==.IoORVxR2LmKV7_maOR9xOg=="
}
}
:raises: KucoinResponseException, KucoinAPIException
"""
path = 'bullet-public'
signed = private
if private:
path = 'bullet-private'
return self._post(path, signed)