Created
January 12, 2019 11:36
-
-
Save mahdi13/edf2c6a20ed6d4141fb6b261571a0e2a to your computer and use it in GitHub Desktop.
The RPC HTTP API client of viabtc exchange server (https://github.com/viabtc/viabtc_exchange_server)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import json | |
import requests | |
""" | |
Created by @mahdi13 | |
The RPC HTTP API client of viabtc exchange server (https://github.com/viabtc/viabtc_exchange_server) | |
Reference: https://github.com/viabtc/viabtc_exchange_server/wiki/HTTP-Protocol | |
""" | |
class ViaBtcExchangeHttpClient: | |
""" | |
""" | |
def __init__(self, server_url, headers=None): | |
self.server_url = server_url | |
self.headers = {'content-type': 'application/json'} | |
self.headers.update(headers or {}) | |
self.request_id = 0 | |
def _next_request_id(self): | |
self.request_id += 1 | |
return self.request_id | |
def _execute(self, method, params, error_mapper=None): | |
payload = json.dumps({"method": method, "params": params, "id": self._next_request_id()}) | |
print(f"Requesting {method} with id:{params['id']} with parameters: {'.'.join(params)}") | |
try: | |
response = requests.post(self, data=payload, headers=self.headers).json() | |
except Exception as e: | |
raise ViaBtcUnknownException(f"Request error: {str(e)}") | |
if response["error"] is not None and len(response["error"] > 0): | |
error_mapper = error_mapper or {} | |
error_mapper.update(GENERAL_ERROR_CODE_MAP) | |
raise ( | |
error_mapper[response["code"]](response["id"]) if (response["code"] in error_mapper) | |
else ViaBtcUnknownException(f"id: {response['id']} Unknown error code: {response['code']}") | |
) | |
elif response["result"] is not None and len(response["result"] > 0): | |
print(f"Request {response['id']} respond") | |
return response["result"] | |
else: | |
return ViaBtcUnknownException("Neither error and result fields available") | |
""" | |
Asset APIs: | |
""" | |
def balance_query(self, user_id, *asset_name): | |
""" | |
Asset inquiry: | |
method: balance.query | |
params: unfixed parameters, first parameter is user ID, followed by list of asset names. | |
Return to user's overall asset if the list is blank. | |
:param user_id: user ID,Integer | |
:param asset_name: list | |
:return: {"asset": {"available": "amount", "freeze": "amount"}} | |
Example: | |
"params": [1, "BTC"] | |
"result": {"BTC": {"available": "1.10000000","freeze": "9.90000000"}} | |
""" | |
return self._execute( | |
"balance.query", | |
[user_id, *asset_name] | |
) | |
def balance_update(self, user_id, asset, business, business_id, change, detail): | |
""" | |
Asset change: | |
method: balance.update | |
:param user_id: user ID,Integer | |
:param asset: asset name,String | |
:param business: business type,String | |
:param business_id: business ID,Integer, but it will only succeed once with multiple operations of the same user_id, asset, business or business_id | |
:param change: balance change,String, negative numbers for deduction | |
:param detail: Json object,attached information | |
:return: "success" | |
Example: | |
"params": [1, "BTC", "deposit", 100, "1.2345"] | |
"result": "success" | |
:raise RepeatUpdateException: repeat update | |
:raise BalanceNotEnough: balance not enough | |
""" | |
return self._execute( | |
"balance.update", | |
[user_id, asset, business, business_id, change, detail], | |
{10: RepeatUpdateException.__class__, 11: BalanceNotEnoughException} | |
) | |
def balance_history(self, user_id, asset, business, start_time, end_time, offset, limit): | |
""" | |
Asset history: | |
method: balance.history | |
:param user_id: user ID, Integer | |
:param asset: asset name,which can be null | |
:param business: business,which can be null,use ',' to separate types | |
:param start_time: start time,0 for unlimited,Integer | |
:param end_time: end time,0 for unlimited, Integer | |
:param offset: offset position,Integer | |
:param limit: count limit,Integer | |
:return: { | |
"offset": | |
"limit": | |
"records": [ | |
{ | |
"time": timestamp, | |
"asset": asset, | |
"business": business, | |
"change": change, | |
"balance":balance, | |
"detail": detail | |
} | |
... | |
] | |
} | |
""" | |
return self._execute( | |
"balance.history", | |
[user_id, asset, business, start_time, end_time, offset, limit] | |
) | |
def asset_list(self): | |
""" | |
Asset list: | |
method: asset.list | |
""" | |
return self._execute( | |
"asset.list", | |
[] | |
) | |
def asset_summary(self): | |
""" | |
Asset summary: | |
method: asset.summary | |
""" | |
return self._execute( | |
"asset.summary", | |
[] | |
) | |
""" | |
Trade APIs: | |
""" | |
def order_put_limit(self, user_id, market, side, amount, price, taker_fee_rate, maker_fee_rate, source): | |
""" | |
Place limit order: | |
method: order.put_limit | |
:param user_id: user ID,Integer | |
:param market: market name,String | |
:param side: 1: sell, 2: buy,Integer | |
:param amount: count,String | |
:param price: price,String | |
:param taker_fee_rate: String, taker fee | |
:param taker_fee_rate: String, taker fee | |
:param maker_fee_rate: String, maker fee | |
:param source: String, source,up to 30 bytes | |
:return: order detail: | |
Example: | |
params: [1, "BTCCNY", 1, "10", "8000", "0.002", "0.001"] | |
:raise BalanceNotEnough: balance not enough | |
""" | |
return self._execute( | |
"order.put_limit", | |
[user_id, market, side, amount, price, taker_fee_rate, maker_fee_rate, source], | |
{10: BalanceNotEnoughException} | |
) | |
def order_put_market(self, user_id, market, side, amount, taker_fee_rate, source): | |
""" | |
Place market order: | |
method: order.put_market | |
:param user_id: user ID,Integer | |
:param market: market name,String | |
:param side: 1: sell, 2: buy,Integer | |
:param amount: count or amount,String | |
:param taker_fee_rate: taker fee | |
:param source: String, source,up to 30 bytes | |
:return: order detail: | |
Example: | |
params: '[1, "BTCCNY", 1, "10","0.002"]' | |
:raise BalanceNotEnough: balance not enough | |
""" | |
return self._execute( | |
"market.put_limit", | |
[user_id, market, side, amount, taker_fee_rate, source], | |
{10: BalanceNotEnoughException} | |
) | |
def order_cancel(self, user_id, market, order_id): | |
""" | |
Cancel order: | |
method: order.cancel | |
:param user_id: user ID,Integer | |
:param market: market name,String | |
:param order_id: order ID | |
:return: order detail | |
:raise OrderNotFoundException: order not found | |
:raise UserNotMatchException: user not match | |
""" | |
return self._execute( | |
"order.cancel", | |
[user_id, market, order_id], | |
{10: OrderNotFoundException.__class__, 11: UserNotMatchException} | |
) | |
def order_deals(self, order_id, offset, limit): | |
""" | |
Order details: | |
method: order.deals | |
:param order_id: order ID, Integer | |
:param offset | |
:param limit | |
:return: | |
"result": { | |
"offset": | |
"limit": | |
"records": [ | |
{ | |
"id": Executed ID | |
"time": timestamp | |
"user": user ID | |
"role": role,1:Maker, 2: Taker | |
"amount": count | |
"price": price | |
"deal": order amount | |
"fee": fee | |
"deal_order_id": Counterpart transaction ID | |
} | |
... | |
] | |
""" | |
return self._execute( | |
"order.deals", | |
[order_id, offset, limit] | |
) | |
def order_book(self, market, side, offset, limit): | |
""" | |
Order list: | |
method: order.book | |
:param market: | |
:param side: side,1:sell,2:buy | |
:param offset | |
:param limit | |
:return: | |
""" | |
return self._execute( | |
"order.book", | |
[market, side, offset, limit] | |
) | |
def order_depth(self, market, limit, interval): | |
""" | |
Order depth: | |
method: order.depth | |
:param market:market name | |
:param limit: count limit,Integer | |
:param interval: interval,String, e.g. "1" for 1 unit interval, "0" for no interval | |
:return: | |
"result": { | |
"asks": [ | |
[ | |
"8000.00", | |
"9.6250" | |
] | |
], | |
"bids": [ | |
[ | |
"7000.00", | |
"0.1000" | |
] | |
] | |
} | |
""" | |
return self._execute( | |
"order.depth", | |
[market, limit, interval] | |
) | |
def order_pending(self, user_id, market, offset, limit): | |
""" | |
Inquire unexecuted orders: | |
method: order.pending | |
:param user_id: user ID,Integer | |
:param market: market name,String | |
:param offset: offset,Integer | |
:param limit: limit,Integer | |
:return: | |
"params": [1, "BTCCNY", 0, 100]" | |
"result": { | |
"offset": 0, | |
"limit": 100, | |
"total": 1, | |
"records": [ | |
{ | |
"id": 2, | |
"ctime": 1492616173.355293, | |
"mtime": 1492697636.238869, | |
"market": "BTCCNY", | |
"user": 2, | |
"type": 1, // 1: limit order,2:market order | |
"side": 2, // 1:sell,2:buy | |
"amount": "1.0000". | |
"price": "7000.00", | |
"taker_fee": "0.0020", | |
"maker_fee": "0.0010", | |
"source": "web", | |
"deal_money": "6300.0000000000", | |
"deal_stock": "0.9000000000", | |
"deal_fee": "0.0009000000" | |
} | |
] | |
} | |
""" | |
return self._execute( | |
"order.pending", | |
[user_id, market, offset, limit] | |
) | |
def order_pending_detail(self, market, order_id): | |
""" | |
Unexecuted order details: | |
method: order.pending_detail | |
:param market: market name,String | |
:param order_id: order ID,Integer | |
""" | |
return self._execute( | |
"order.pending_detail", | |
[market, order_id] | |
) | |
def order_finished(self, user_id, market, start_time, end_time, offset, limit, side): | |
""" | |
Inquire executed orders: | |
method: order.finished | |
:param user_id: user ID,Integer | |
:param market: market name,String | |
:param start_time: start time,0 for unlimited,Integer | |
:param end_time: end time,0 for unlimited, Integer | |
:param offset: offset,Integer | |
:param limit: limit,Integer | |
:param side: side,0 for no limit,1 for sell,2 for buy | |
""" | |
return self._execute( | |
"order.finished", | |
[user_id, market, start_time, end_time, offset, limit, side] | |
) | |
def order_finished_detail(self, order_id): | |
""" | |
Executed order details: | |
method: order.finished_detail | |
:param order_id: order ID,Integer | |
""" | |
return self._execute( | |
"order.finished_detail", | |
[order_id] | |
) | |
""" | |
Market APIs: | |
""" | |
def market_last(self, market): | |
""" | |
Market price: | |
method: market.last | |
:param market | |
:return: "price" | |
""" | |
return self._execute( | |
"market.last", | |
[market] | |
) | |
def market_deals(self, market, limit, last_id): | |
""" | |
Executed history: | |
method: market.deals | |
:param market | |
:param limit: count,no more than 10000 | |
:param last_id: count,no more than 10000 | |
:return: "result": [ | |
{ | |
"id": 5, | |
"time": 1492697636.238869, | |
"type": "sell", | |
"amount": "0.1000", | |
"price": "7000.00" | |
}, | |
{ | |
"id": 4, | |
"time": 1492697467.1411841, | |
"type": "sell", | |
"amount": "0.1000" | |
"price": "7000.00", | |
} | |
] | |
""" | |
return self._execute( | |
"market.deals", | |
[market, limit, last_id] | |
) | |
def market_user_deals(self, user_id, market, offset, limit): | |
""" | |
User Executed history: | |
method: market.user_deals | |
:param user_id: user ID,Integer | |
:param market: market name,String | |
:param offset: offset,Integer | |
:param limit: limit,Integer | |
:return: "result": [ | |
"offset": | |
"limit": | |
"records": [ | |
{ | |
"id": Executed ID | |
"time": timestamp | |
"user": user ID | |
"side": side,1:sell,2:buy | |
"role": role,1:Maker, 2: Taker | |
"amount": count | |
"price": price | |
"deal": amount | |
"fee": fee | |
"deal_order_id": Counterpart Transaction ID | |
} | |
... | |
] | |
] | |
""" | |
return self._execute( | |
"market.user_deals", | |
[user_id, market, offset, limit] | |
) | |
def market_kline(self, market, start, end, interval): | |
""" | |
KLine: | |
method: market.kline | |
:param market: market name,String | |
:param start: start,Integer | |
:param end: end,Integer | |
:param interval: interval,Integer | |
:return: "result": [ | |
[ | |
1492358400, time | |
"7000.00", open | |
"8000.0", close | |
"8100.00", highest | |
"6800.00", lowest | |
"1000.00" volume | |
"123456.78" amount | |
"BTCCNY" market name | |
] | |
] | |
""" | |
return self._execute( | |
"market.kline", | |
[market, start, end, interval] | |
) | |
def market_status(self, market, period): | |
""" | |
Market status: | |
method: market.status | |
:param market: market name,String | |
:param period: cycle period,Integer, e.g. 86400 for last 24 hours | |
:return: "result": { | |
"period": 86400, | |
"last": "7000.00", | |
"open": "0", | |
"close": "0", | |
"high": "0", | |
"low": "0", | |
"volume": "0" | |
} | |
""" | |
return self._execute( | |
"market.status", | |
[market, period] | |
) | |
def market_status_today(self, market): | |
""" | |
Market status today: | |
method: market.status_today | |
:param market: market name,String | |
:return: { | |
"open": Open today | |
"last": Latest price | |
"high": Highest price | |
"low": Lowest price | |
"deal": 24H amount | |
"volume": 24H volume | |
} | |
""" | |
return self._execute( | |
"market.status_today", | |
[market] | |
) | |
def market_list(self): | |
""" | |
Market list: | |
method: market.list | |
:return: | |
""" | |
return self._execute( | |
"market.list", | |
[] | |
) | |
def market_summary(self, market): | |
""" | |
Market summary: | |
method: market.summary | |
:param market: list, return to market if null | |
:return: | |
""" | |
return self._execute( | |
"market.summary", | |
[market] | |
) | |
class ViaBtcException(Exception): | |
def __init__(self, message): | |
""" | |
:param message: error message | |
""" | |
print(f"ViaBtc RPC Error: {message}") | |
class ViaBtcUnknownException(Exception): | |
def __init__(self, message=""): | |
super(f"unknown exception: {message}") | |
class InvalidArgumentException(ViaBtcException): | |
def __init__(self, _id): | |
super(f"id: ${_id} invalid argument") | |
class InternalErrorException(ViaBtcException): | |
def __init__(self, _id): | |
super(f"id: ${_id} internal error") | |
class ServiceUnavailableException(ViaBtcException): | |
def __init__(self, _id): | |
super(f"id: ${_id} service unavailable") | |
class MethodNotFoundException(ViaBtcException): | |
def __init__(self, _id): | |
super(f"id: ${_id} method not found") | |
class ServiceTimoutException(ViaBtcException): | |
def __init__(self, _id): | |
super(5, "service timeout") | |
class OrderNotFoundException(ViaBtcException): | |
def __init__(self, _id): | |
super(f"id: ${_id} order not found") | |
class UserNotMatchException(ViaBtcException): | |
def __init__(self, _id): | |
super(f"id: ${_id} user not match") | |
class RepeatUpdateException(ViaBtcException): | |
def __init__(self, _id): | |
super(f"id: ${_id} repeat update") | |
class BalanceNotEnoughException(ViaBtcException): | |
def __init__(self, _id): | |
super(f"id: ${_id} balance not enough") | |
GENERAL_ERROR_CODE_MAP = { | |
1: InvalidArgumentException.__class__, | |
2: InternalErrorException.__class__, | |
3: ServiceUnavailableException.__class__, | |
4: MethodNotFoundException.__class__, | |
5: ServiceTimoutException.__class__, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment