def __init__(self, *args, **kwargs): """ Create the request client instance. :param kwargs: The option of request connection. api_key: The public key applied from Huobi. secret_key: The private key applied from Huobi. url: The URL name like "https://api.huobi.pro". init_log: to init logger """ self.__kwargs = kwargs self.market_service = RestApiSyncClient(*args, **kwargs) self.market_service_sub = SubscribeClient(**kwargs) self.market_service_socket = WebSocketReqClient(**kwargs)
def __init__(self, *args, **kwargs): """ Create the request client instance. :param kwargs: The option of request connection. api_key: The public key applied from Huobi. secret_key: The private key applied from Huobi. url: The URL name like "https://api.huobi.pro". init_log: Init logger, default is False, True will init logger handler """ self.__kwargs = kwargs self.rest_api_sync_client = RestApiSyncClient(*args, **kwargs) self.web_socket_req_client = WebSocketReqClient(*args, **kwargs) self.sub_socket_req_client = SubscribeClient(*args, **kwargs)
class MarketClient(object): def __init__(self, *args, **kwargs): """ Create the request client instance. :param kwargs: The option of request connection. api_key: The public key applied from Huobi. secret_key: The private key applied from Huobi. url: The URL name like "https://api.huobi.pro". init_log: to init logger """ self.__kwargs = kwargs self.market_service = RestApiSyncClient(*args, **kwargs) self.market_service_sub = SubscribeClient(**kwargs) self.market_service_socket = WebSocketReqClient(**kwargs) def get_candlestick(self, symbol, period, size=200): """ Get the candlestick/kline for the specified symbol. The data number is 150 as default. :param symbol: The symbol, like "btcusdt". To query hb10, put "hb10" at here. (mandatory) :param period: The candlestick/kline interval, MIN1, MIN5, DAY1 etc. (mandatory) :param size: The start time of of requested candlestick/kline data. (optional) :return: The list of candlestick/kline data. """ check_symbol(symbol) check_should_not_none(period, "period") check_range(size, 1, 2000, "size") params = {"symbol": symbol, "period": period, "size": size} channel = "/market/history/kline" return self.market_service.request_process(HttpMethod.GET, channel, params) def sub_candlestick(self, symbols: str, interval: CandlestickInterval, callback, error_handler): """ Subscribe candlestick/kline event. If the candlestick/kline is updated, server will send the data to client and onReceive in callback will be called. :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt". :param interval: The candlestick/kline interval, MIN1, MIN5, DAY1 etc. :param callback: The implementation is required. onReceive will be called if receive server's update. example: def callback(candlestick_event: 'CandlestickEvent'): pass :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server example: def error_handler(exception: 'HuobiApiException') pass :return: No return """ symbol_list = symbols.split(",") check_symbol_list(symbol_list) check_should_not_none(interval, "interval") check_should_not_none(callback, "callback") def kline_channel(symbol, _interval): channel = dict() channel["sub"] = "market." + symbol + ".kline." + _interval channel["id"] = str(get_current_timestamp()) return json.dumps(channel) def subscription(connection): for symbol in symbol_list: connection.send(kline_channel(symbol, interval)) time.sleep(0.01) self.market_service_sub.execute_subscribe_v1(subscription, callback, error_handler) def req_candlestick(self, symbols: str, interval: CandlestickInterval, callback, from_ts_second=None, end_ts_second=None, error_handler=None): """ Subscribe candlestick/kline event. If the candlestick/kline is updated, server will send the data to client and onReceive in callback will be called. :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt". :param interval: The candlestick/kline interval, MIN1, MIN5, DAY1 etc. :param callback: The implementation is required. onReceive will be called if receive server's update. example: def callback(candlestick_event: 'CandlestickEvent'): pass :param from_ts_second : data from timestamp [it's second] :param end_ts_second : data util timestamp [it's second] :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server example: def error_handler(exception: 'HuobiApiException') pass :return: No return """ symbol_list = symbols.split(",") check_symbol_list(symbol_list) check_should_not_none(interval, "interval") check_should_not_none(callback, "callback") def request_kline_channel(symbol, _interval, _from_ts_second=None, to_ts_second=None): channel = dict() channel["req"] = "market." + symbol + ".kline." + _interval channel["id"] = str(get_current_timestamp()) if _from_ts_second: channel["from"] = int(_from_ts_second) if to_ts_second: channel["to"] = int(to_ts_second) return json.dumps(channel) def subscription(connection): for symbol in symbol_list: connection.send( request_kline_channel(symbol, interval, from_ts_second, end_ts_second)) time.sleep(0.01) self.market_service_socket.execute_subscribe_v1( subscription, callback, error_handler) def get_price_depth(self, symbol: str, depth_type: str, depth_size: int = None): """ Get the Market Depth of a symbol. :param symbol: The symbol, like "btcusdt". (mandatory) :param depth_type: The tpye, like "step0" to "step5". (mandatory) :param depth_size: (optional) The maximum number of Market Depth step0 requested. range [1 - 150], default is 150 The maximum number of Market Depth step1,step2,step3,step4,step5 requested. size is in [5, 10, 20], default is 20. :return: Market Depth data. """ channel = "/market/depth" check_symbol(symbol) check_in_list(depth_type, [ DepthStep.STEP0, DepthStep.STEP1, DepthStep.STEP2, DepthStep.STEP3, DepthStep.STEP4, DepthStep.STEP5 ], "depth_type") params = { "symbol": symbol, "type": depth_type, # "depth": depth_size } ret_data = self.market_service.request_process(HttpMethod.GET, channel, params) if depth_size is not None: if (ret_data.bids is not None) and (len(ret_data.bids) > depth_size): ret_data.bids = ret_data.bids[0:depth_size] if (ret_data.asks is not None) and (len(ret_data.asks) > depth_size): ret_data.asks = ret_data.asks[0:depth_size] return ret_data @staticmethod def get_depth_step_list(): return [ DepthStep.STEP0, DepthStep.STEP1, DepthStep.STEP2, DepthStep.STEP3, DepthStep.STEP4, DepthStep.STEP5 ] @staticmethod def get_valid_depth_step(value, defalut_value): step_list = MarketClient.get_depth_step_list() if value in step_list: return value else: return defalut_value def sub_price_depth(self, symbols: str, depth_step: str, callback, error_handler=None): """ Subscribe price depth event. If the price depth is updated, server will send the data to client and onReceive in callback will be called. :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt". :param depth_step: The depth precision, string from step0 to step5. :param callback: The implementation is required. onReceive will be called if receive server's update. example: def callback(price_depth_event: 'PriceDepthEvent'): pass :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server example: def error_handler(exception: 'HuobiApiException') pass :return: No return """ symbol_list = symbols.split(",") check_symbol_list(symbol_list) step = MarketClient.get_valid_depth_step(value=depth_step, defalut_value=DepthStep.STEP0) check_should_not_none(callback, "callback") def price_depth_channel(symbol, step_type=DepthStep.STEP0): channel = dict() channel["sub"] = "market." + symbol + ".depth." + step_type channel["id"] = str(get_current_timestamp()) return json.dumps(channel) def subscription(connection): for symbol in symbol_list: connection.send(price_depth_channel(symbol, step)) time.sleep(0.01) self.market_service_sub.execute_subscribe_v1(subscription, callback, error_handler) def sub_price_depth_bbo(self, symbols: str, callback, error_handler=None): """ Subscribe price depth event. If the price depth is updated, server will send the data to client and onReceive in callback will be called. :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt". :param callback: The implementation is required. onReceive will be called if receive server's update. example: def callback(price_depth_event: 'PriceDepthEvent'): pass :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server example: def error_handler(exception: 'HuobiApiException') pass :return: No return """ symbol_list = symbols.split(",") check_symbol_list(symbol_list) check_should_not_none(callback, "callback") def price_depth_bbo_channel(symbol): channel = dict() channel["sub"] = "market." + symbol + ".bbo" channel["id"] = str(get_current_timestamp()) return json.dumps(channel) def subscription(connection): for symbol in symbol_list: connection.send(price_depth_bbo_channel(symbol)) time.sleep(0.01) self.market_service_sub.execute_subscribe_v1(subscription, callback, error_handler) def req_price_depth(self, symbols: str, depth_step: str, callback, error_handler=None): """ Subscribe price depth event. If the price depth is updated, server will send the data to client and onReceive in callback will be called. :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt". :param depth_step: The depth precision, string from step0 to step5. :param callback: The implementation is required. onReceive will be called if receive server's update. example: def callback(price_depth_event: 'PriceDepthEvent'): pass :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server example: def error_handler(exception: 'HuobiApiException') pass :return: No return """ symbol_list = symbols.split(",") check_symbol_list(symbol_list) step = MarketClient.get_valid_depth_step(value=depth_step, defalut_value=DepthStep.STEP0) check_should_not_none(callback, "callback") def request_price_depth_channel(symbol, step_type="step0"): channel = dict() channel["req"] = "market." + symbol + ".depth." + step_type channel["id"] = str(get_current_timestamp()) return json.dumps(channel) def subscription(connection): for symbol in symbol_list: connection.send(request_price_depth_channel(symbol, step)) time.sleep(0.01) self.market_service_socket.execute_subscribe_v1( subscription, callback, error_handler) def get_market_detail(self, symbol: str): """ Get trade statistics in 24 hours. :param symbol: The symbol, like "btcusdt". (mandatory) :return: Trade statistics. """ channel = "/market/detail" check_symbol(symbol) params = { "symbol": symbol, } return self.market_service.request_process(HttpMethod.GET, channel, params) def sub_market_detail(self, symbols: str, callback, error_handler=None): """ Subscribe 24 hours trade statistics event. If statistics is generated, server will send the data to client and onReceive in callback will be called. :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt". :param callback: The implementation is required. onReceive will be called if receive server's update. example: def callback(trade_statistics_event: 'TradeStatisticsEvent'): pass :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server example: def error_handler(exception: 'HuobiApiException') pass :return: No return """ symbol_list = symbols.split(",") check_symbol_list(symbol_list) check_should_not_none(callback, "callback") def market_detail_channel(_symbol): channel = dict() channel["sub"] = "market." + _symbol + ".detail" channel["id"] = str(get_current_timestamp()) return json.dumps(channel) def subscription(connection): for symbol in symbol_list: connection.send(market_detail_channel(symbol)) time.sleep(0.01) self.market_service_sub.execute_subscribe_v1(subscription, callback, error_handler) def req_market_detail(self, symbols: str, callback, error_handler=None): """ Subscribe 24 hours trade statistics event. If statistics is generated, server will send the data to client and onReceive in callback will be called. :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt". :param callback: The implementation is required. onReceive will be called if receive server's update. example: def callback(trade_statistics_event: 'TradeStatisticsEvent'): pass :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server example: def error_handler(exception: 'HuobiApiException') pass :return: No return """ symbol_list = symbols.split(",") check_symbol_list(symbol_list) check_should_not_none(callback, "callback") def request_market_detail_channel(_symbol): channel = dict() channel["req"] = "market." + _symbol + ".detail" channel["id"] = str(get_current_timestamp()) return json.dumps(channel) def subscription(connection): for symbol in symbol_list: connection.send(request_market_detail_channel(symbol)) time.sleep(0.01) self.market_service_socket.execute_subscribe_v1( subscription, callback, error_handler) def get_market_trade(self, symbol: str) -> list: """ Get the most recent trades with their price, volume and direction. :param symbol: The symbol, like "btcusdt". (mandatory) :return: The list of trade. """ channel = "/market/trade" check_symbol(symbol) params = { "symbol": symbol, } return self.market_service.request_process(HttpMethod.GET, channel, params) def get_history_trade(self, symbol: str, size: 'int' = None) -> list: """ Get the most recent trades with their price, volume and direction. :param symbol: The symbol, like "btcusdt". (mandatory) :param size: The number of historical trade requested, range [1 - 2000] (optional) :return: The list of trade. """ channel = "/market/history/trade" check_symbol(symbol) check_range(size, 1, 2000, "size") params = {"symbol": symbol, "size": size} return self.market_service.request_process(HttpMethod.GET, channel, params) def sub_trade_detail(self, symbols: str, callback, error_handler=None): """ Subscribe price depth event. If the price depth is updated, server will send the data to client and onReceive in callback will be called. :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt". :param callback: The implementation is required. onReceive will be called if receive server's update. example: def callback(trade_event: 'TradeEvent'): pass :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server example: def error_handler(exception: 'HuobiApiException') pass :return: No return """ symbol_list = symbols.split(",") check_symbol_list(symbol_list) check_should_not_none(callback, "callback") def trade_detail_channel(symbol): channel = dict() channel["sub"] = "market." + symbol + ".trade.detail" channel["id"] = str(get_current_timestamp()) return json.dumps(channel) def subscription(connection): for symbol in symbol_list: connection.send(trade_detail_channel(symbol)) time.sleep(0.01) self.market_service_sub.execute_subscribe_v1(subscription, callback, error_handler) def req_trade_detail(self, symbols: str, callback, error_handler=None): """ Subscribe price depth event. If the price depth is updated, server will send the data to client and onReceive in callback will be called. :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt". :param callback: The implementation is required. onReceive will be called if receive server's update. example: def callback(trade_event: 'TradeEvent'): pass :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server example: def error_handler(exception: 'HuobiApiException') pass :return: No return """ symbol_list = symbols.split(",") check_symbol_list(symbol_list) check_should_not_none(callback, "callback") def request_trade_detail_channel(symbol): channel = dict() channel["req"] = "market." + symbol + ".trade.detail" channel["id"] = str(get_current_timestamp()) return json.dumps(channel) def subscription(connection): for symbol in symbol_list: connection.send(request_trade_detail_channel(symbol)) time.sleep(0.01) self.market_service_socket.execute_subscribe_v1( subscription, callback, error_handler) def get_market_detail_merged(self, symbol): check_symbol(symbol) params = {"symbol": symbol} channel = "/market/detail/merged" return self.market_service.request_process(HttpMethod.GET, channel, params) def get_market_tickers(self) -> Response: """ get market tickers :return: market ticker list. """ params = {} channel = "/market/tickers" return self.market_service.request_process(HttpMethod.GET, channel, params) """ increase mbp(market by price) """ def sub_mbp_increase(self, symbols: str, levels: 'int', callback, error_handler=None): """ Subscribe mbp event. If the mbp is updated, server will send the data to client and onReceive in callback will be called. :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt". :param levels: level, 5,10,20,150. current only support 150 :param callback: The implementation is required. onReceive will be called if receive server's update. example: def callback(price_depth_event: 'PriceDepthEvent'): pass :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server example: def error_handler(exception: 'HuobiApiException') pass :return: No return """ check_should_not_none(symbols, "symbol") symbol_list = symbols.split(",") check_symbol_list(symbol_list) check_should_not_none(levels, "levels") check_should_not_none(callback, "callback") def mbp_increase_channel(symbol, level): channel = dict() channel["sub"] = "market.{symbol}.mbp.{level}".format( symbol=symbol, level=level) channel["id"] = str(get_current_timestamp()) return json.dumps(channel) def subscription(connection): for symbol in symbol_list: connection.send(mbp_increase_channel(symbol, levels)) time.sleep(0.01) return self.market_service_sub.execute_subscribe_mbp( subscription, callback, error_handler) """ subscribe full mbp(market by price) """ def sub_mbp_full(self, symbols: str, levels: 'int', callback, error_handler=None): """ Subscribe full mbp event. If the mbp is updated, server will send the data to client and onReceive in callback will be called. :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt". :param levels: level, 5,10,20 :param callback: The implementation is required. onReceive will be called if receive server's update. example: def callback(price_depth_event: 'PriceDepthEvent'): pass :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server example: def error_handler(exception: 'HuobiApiException') pass :return: No return """ check_should_not_none(symbols, "symbol") symbol_list = symbols.split(",") check_symbol_list(symbol_list) check_should_not_none(levels, "levels") check_in_list(levels, [MbpLevel.MBP5, MbpLevel.MBP10, MbpLevel.MBP20], "levels") check_should_not_none(callback, "callback") def mbp_full_channel(symbol, level): channel = dict() channel["sub"] = "market.{symbol}.mbp.refresh.{level}".format( symbol=symbol, level=level) channel["id"] = str(get_current_timestamp()) return json.dumps(channel) def subscription(connection): for symbol in symbol_list: connection.send(mbp_full_channel(symbol, levels)) time.sleep(0.01) self.market_service_sub.execute_subscribe_v1(subscription, callback, error_handler) def req_mbp(self, symbols: str, levels: 'int', callback, auto_close=True, error_handler=None): """ Subscribe mbp event. If the mbp is updated, server will send the data to client and onReceive in callback will be called. :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt". :param levels: level, 5,10,20,150. current only support 150 :param callback: The implementation is required. onReceive will be called if receive server's update. example: def callback(price_depth_event: 'PriceDepthEvent'): pass :param auto_close : close websocket connection after get data :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server example: def error_handler(exception: 'HuobiApiException') pass :return: No return """ check_should_not_none(symbols, "symbol") symbol_list = symbols.split(",") check_symbol_list(symbol_list) check_should_not_none(levels, "levels") check_should_not_none(callback, "callback") def request_mbp_channel(symbol, level): channel = dict() channel["req"] = "market.{symbol}.mbp.{level}".format( symbol=symbol, level=level) channel["id"] = str(get_current_timestamp()) return json.dumps(channel) def subscription(connection): for symbol in symbol_list: connection.send(request_mbp_channel(symbol, levels)) time.sleep(0.01) self.market_service_socket.execute_subscribe_mbp( subscription, callback, error_handler)
class AccountClient(object): def __init__(self, *args, **kwargs): """ Create the request client instance. :param kwargs: The option of request connection. api_key: The public key applied from Huobi. secret_key: The private key applied from Huobi. url: The URL name like "https://api.huobi.pro". init_log: Init logger, default is False, True will init logger handler """ self.__kwargs = kwargs self.rest_api_sync_client = RestApiSyncClient(*args, **kwargs) self.web_socket_req_client = WebSocketReqClient(*args, **kwargs) self.sub_socket_req_client = SubscribeClient(*args, **kwargs) def get_accounts(self): """ Get the account list. :return: The list of accounts data. """ channel = "/v1/account/accounts" return self.rest_api_sync_client.request_process( HttpMethod.GET_SIGN, channel, {}) def get_balance(self, account_id: int): """ Get the account list. :return: The list of accounts data. """ check_should_not_none(account_id, "account-id") params = {"account-id": account_id} def get_channel(): path = "/v1/account/accounts/{}/balance" return path.format(account_id) return self.rest_api_sync_client.request_process( HttpMethod.GET_SIGN, get_channel(), params) def get_account_by_type_and_symbol(self, account_type, symbol): accounts = self.get_accounts() if accounts and len(accounts): for account_obj in accounts: if account_obj.type == account_type: if account_type == AccountType.MARGIN: if symbol == account_obj.subtype: return account_obj else: return account_obj return None @staticmethod async def async_get_account_balance(balance_full_url, account_id, ret_map): async with aiohttp.ClientSession() as session: async with session.get(balance_full_url) as resp: _json = await resp.json() ret_map[account_id] = _json return _json """ (SDK encapsulated api) to easily use but not recommend for low performance and frequence limitation """ def get_account_balance(self) -> list: """ Get the balance of a all accounts. :return: The information of all account balance. """ server_url = get_default_server_url(self.__kwargs.get("url")) tasks = [] account_obj_map = {} accounts = self.get_accounts() account_balance_list = [] account_balance_json_map = {} for account_item in accounts: account_obj_map[account_item.id] = account_item params = {"account-id": account_item.id} def get_channel(): path = "/v1/account/accounts/{}/balance" return path.format(account_item.id) balance_request = self.rest_api_sync_client.create_request( HttpMethod.GET_SIGN, get_channel(), params) balance_url = server_url + balance_request.url tasks.append( asyncio.ensure_future( self.async_get_account_balance(balance_url, account_item.id, account_balance_json_map))) loop = asyncio.get_event_loop() try: loop.run_until_complete(asyncio.wait(tasks)) except Exception as ee: print(ee) finally: # loop.close() #for thread safe, the event loop can't be closed pass for account_id, account_balance_json in account_balance_json_map.items( ): account_balance_list.append(account_balance_json.get("data", {})) del account_balance_json_map del tasks return account_balance_list def get_account_balance_by_subuid(self, sub_uid): """ Get account balance of a sub-account. :param sub_uid: the specified sub account id to get balance for. :return: the balance of a sub-account specified by sub-account uid. """ check_should_not_none(sub_uid, "sub-uid") params = {"sub-uid": sub_uid} def get_channel(): path = "/v1/account/accounts/{}" return path.format(sub_uid) return self.rest_api_sync_client.request_process( HttpMethod.GET_SIGN, get_channel(), params) def get_aggregated_subuser_balance(self): """ Get the aggregated balance of all sub-accounts of the current user. :return: The balance of all the sub-account aggregated. """ params = {} channel = "/v1/subuser/aggregate-balance" return self.rest_api_sync_client.request_process( HttpMethod.GET_SIGN, channel, params) def transfer_between_parent_and_subuser(self, sub_uid: int, currency: str, amount: float, transfer_type: TransferMasterType): """ Transfer Asset between Parent and Sub Account. :param sub_uid: The target sub account uid to transfer to or from. (mandatory) :param currency: The crypto currency to transfer. (mandatory) :param amount: The amount of asset to transfer. (mandatory) :param transfer_type: The type of transfer, see {@link TransferMasterType} (mandatory) :return: The order id. """ check_currency(currency) check_should_not_none(sub_uid, "sub-uid") check_should_not_none(amount, "amount") check_should_not_none(transfer_type, "type") params = { "sub-uid": sub_uid, "currency": currency, "amount": amount, "type": transfer_type } channel = "/v1/subuser/transfer" return self.rest_api_sync_client.request_process( HttpMethod.POST_SIGN, channel, params) def sub_account_update(self, mode: AccountBalanceMode, callback, error_handler=None): """ Subscribe accounts update :param mode: subscribe mode "0" : for balance "1" : for available and balance :param callback: The implementation is required. onReceive will be called if receive server's update. example: def callback(price_depth_event: 'PriceDepthEvent'): pass :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server example: def error_handler(exception: 'HuobiApiException') pass :return: No return """ check_should_not_none(callback, "callback") if str(mode) == AccountBalanceMode.TOTAL: mode = AccountBalanceMode.TOTAL else: mode = AccountBalanceMode.BALANCE def accounts_update_channel(_mode=0): channel = dict() channel["action"] = "sub" if _mode is None: channel["ch"] = "accounts.update" else: channel["ch"] = "accounts.update#{mode}".format(mode=_mode) return json.dumps(channel) def subscription(connection): connection.send(accounts_update_channel(mode)) self.sub_socket_req_client.execute_subscribe_v2(subscription, callback, error_handler, is_trade=True) def req_account_balance(self, callback, client_req_id=None, error_handler=None): """ Subscribe account changing event. If the balance is updated, server will send the data to client and onReceive in callback will be called. :param client_req_id: client request ID :param callback: The implementation is required. onReceive will be called if receive server's update. example: def callback(account_event: 'AccountEvent'): pass :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server example: def error_handler(exception: 'HuobiApiException') pass :return: No return """ check_should_not_none(callback, "callback") def request_account_list_channel(_client_req_id=None): channel = dict() channel["op"] = "req" channel["topic"] = "accounts.list" channel["cid"] = str(_client_req_id) if _client_req_id else str( get_current_timestamp()) return json.dumps(channel) def subscription(connection): connection.send(request_account_list_channel(client_req_id)) self.web_socket_req_client.execute_subscribe_v1(subscription, callback, error_handler, is_trade=True) def transfer_between_futures_and_pro( self, currency: str, amount: float, transfer_type: TransferFuturesPro) -> int: """ Transfer Asset between Futures and Contract. :param currency: The crypto currency to transfer. (mandatory) :param amount: The amount of asset to transfer. (mandatory) :param transfer_type: The type of transfer, need be "futures-to-pro" or "pro-to-futures" (mandatory) :return: The order id. """ check_currency(currency) check_should_not_none(currency, "currency") check_should_not_none(amount, "amount") check_should_not_none(transfer_type, "transfer_type") params = { "currency": currency, "amount": amount, "type": transfer_type } channel = "/v1/futures/transfer" return self.rest_api_sync_client.request_process( HttpMethod.POST_SIGN, channel, params) def get_account_history(self, account_id: int, currency: str = None, transact_types: str = None, start_time: int = None, end_time: int = None, sort: str = None, size: int = None): """ get account change record :param account_id: account id (mandatory) :param currency: currency as "btc,eth" (optional) :param transact_types: see AccountTransactType, the value can be "trade" (交易),"etf"(ETF申购), "transact-fee"(交易手续费), "deduction"(手续费抵扣), "transfer"(划转), "credit"(借币), "liquidation"(清仓), "interest"(币息), "deposit"(充币), "withdraw"(提币), "withdraw-fee"(提币手续费), "exchange"(兑换), "other-types"(其他) (optional) :param start_time :param end_time: for time range to search (optional) :param sort: see SortDesc, "asc" or "desc" (optional) :param size: page size (optional) :return: account change record list. """ check_should_not_none(account_id, "account-id") params = { "account-id": account_id, "currency": currency, "transact-types": transact_types, "start-time": start_time, "end-time": end_time, "sort": sort, "size": size } channel = "/v1/account/history" return self.rest_api_sync_client.request_process( HttpMethod.GET_SIGN, channel, params) def post_sub_uid_management(self, sub_uid: int, action: str): """ use to freeze or unfreeze the sub uid :return: user and status. """ check_should_not_none(sub_uid, "subUid") check_should_not_none(action, "action") params = {"subUid": sub_uid, "action": action} channel = "/v2/sub-user/management" return self.rest_api_sync_client.request_process( HttpMethod.POST_SIGN, channel, params) def get_account_ledger(self, account_id: int, currency: str = None, transact_types: str = None, start_time: int = None, end_time: int = None, sort: str = None, limit: int = None, from_id: int = None) -> list: """ get account ledger :param from_id: from_id: :param account_id: account id (mandatory) :param currency: currency as "btc,eth" (optional) :param transact_types: see AccountTransactType, the value can be "trade" (交易),"etf"(ETF申购), "transact-fee"(交易手续费), "deduction"(手续费抵扣), "transfer"(划转), "credit"(借币), "liquidation"(清仓), "interest"(币息), "deposit"(充币),"withdraw"(提币), "withdraw-fee"(提币手续费), "exchange"(兑换), "other-types"(其他) (optional) :param start_time :param end_time: for time range to search (optional) :param sort: see SortDesc, "asc" or "desc" (optional) :param limit: page size (optional) :return: account ledger list. """ check_should_not_none(account_id, "accountId") params = { "accountId": account_id, "currency": currency, "transactTypes": transact_types, "startTime": start_time, "endTime": end_time, "sort": sort, "limit": limit, "fromId": from_id } channel = "/v2/account/ledger" return self.rest_api_sync_client.request_process( HttpMethod.GET_SIGN, channel, params) def post_account_transfer(self, from_user: int, from_account_type: str, from_account: int, to_user: int, to_account_type: str, to_account: int, currency: str, amount: str): check_should_not_none(from_user, "from-user") check_should_not_none(from_account_type, "from-account-type") check_should_not_none(from_account, "from_account") check_should_not_none(to_user, "to-user") check_should_not_none(to_account, "to-account") check_should_not_none(to_account_type, "to-account") check_should_not_none(currency, "currency") check_in_list(from_account_type, [AccountType.SPOT], "from_account_type") check_in_list(to_account_type, [AccountType.SPOT], "to_account_type") params = { "from-user": from_user, "from-account-type": from_account_type, "from-account": from_account, "to-user": to_user, "to-account-type": to_account_type, "to-account": to_account, "currency": currency, "amount": amount } channel = "/v1/account/transfer" return self.rest_api_sync_client.request_process( HttpMethod.POST_SIGN, channel, params) def get_account_asset_valuation(self, account_type, valuation_currency: str = None, sub_uid: str = None): check_should_not_none(account_type, "account-type") params = { "accountType": account_type, "valuationCurrency": valuation_currency.upper(), "subUid": sub_uid } channel = "/v2/account/asset-valuation" return self.rest_api_sync_client.request_process( HttpMethod.GET_SIGN, channel, params) def get_account_point(self, sub_uid: str = None): params = {"subUid": sub_uid} channel = "/v2/point/account" return self.rest_api_sync_client.request_process( HttpMethod.GET_SIGN, channel, params) def post_point_transfer(self, from_uid: str, to_uid: str, group_id: str, amount: str): channel = "/v2/point/transfer" params = { "fromUid": from_uid, "toUid": to_uid, "groupId": group_id, "amount": amount } return self.rest_api_sync_client.request_process( HttpMethod.POST_SIGN, channel, params)
class TradeClient(object): def __init__(self, *args, **kwargs): """ Create the request client instance. :param kwargs: The option of request connection. api_key: The public key applied from Huobi. secret_key: The private key applied from Huobi. url: The URL name like "https://api.huobi.pro". init_log: to init logger """ self.__kwargs = kwargs self.trade_client = RestApiSyncClient(*args, **kwargs) self.trade_client_socket = WebSocketReqClient(*args, **kwargs) self.trade_client_socket_sub = SubscribeClient(*args, **kwargs) def get_fee_rate(self, symbols: str) -> list: """ Get the candlestick/kline for the specified symbol. The data number is 150 as default. :param symbols: The symbol, like "btcusdt". To query hb10, put "hb10" at here. (mandatory) : interval: The candlestick/kline interval, MIN1, MIN5, DAY1 etc. (mandatory) : size: The start time of of requested candlestick/kline data. (optional) : start_time: The start time of of requested candlestick/kline data. (optional) : end_time: The end time of of requested candlestick/kline data. (optional) :return: The list of candlestick/kline data. """ channel = "/v1/fee/fee-rate/get" check_symbol(symbols) params = { "symbols": symbols } return self.trade_client.request_process(HttpMethod.GET_SIGN, channel, params) def get_transact_fee_rate(self, symbols: str) -> list: """ The request of get transact fee rate list. :param symbols: The symbol, like "btcusdt,htusdt". (mandatory) :return: The transact fee rate list. """ check_symbol(symbols) params = { "symbols": symbols } channel = "/v2/reference/transact-fee-rate" return self.trade_client.request_process(HttpMethod.GET_SIGN, channel, params) def sub_order_update(self, symbols: str, callback, error_handler=None): """ Subscribe order changing event. If a order is created, canceled etc, server will send the data to client and onReceive in callback will be called. :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt". :param callback: The implementation is required. onReceive will be called if receive server's update. example: def callback(order_update_event: 'OrderUpdateEvent'): pass :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server example: def error_handler(exception: 'HuobiApiException') pass :return: No return """ symbol_list = symbols.split(",") check_symbol_list(symbol_list) check_should_not_none(callback, "callback") def subscription(connection): for val in symbol_list: connection.send(orders_update_channel(val)) time.sleep(0.01) self.trade_client_socket_sub.execute_subscribe_v2(subscription, callback, error_handler, is_trade=True) def req_order_list(self, symbol: str, account_id: int, callback, order_states: str, order_types: str = None, start_date: str = None, end_date: str = None, from_id=None, direct=None, size=None, client_req_id: str = None, error_handler=None): """ request order list. :param client_req_id: client_req_id: :param size: size: :param direct: direct: :param from_id: from_id: :param end_date: end_date: :param start_date: start_date: :param account_id: account_id: :param order_types: order_types: :param symbol: The symbol, like "btcusdt". :param order_states: order status, can be one state or many state sepearted by comma, such as "submitted,partial-filled,partial-canceled,filled,canceled,created" :param callback: The implementation is required. onReceive will be called if receive server's update. example: def callback(candlestick_event: 'CandlestickEvent'): pass :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server example: def error_handler(exception: 'HuobiApiException') pass :return: No return """ check_should_not_none(symbol, "symbol") check_should_not_none(order_states, "states") check_should_not_none(account_id, "account-d") check_should_not_none(callback, "callback") params = { "symbol": symbol, "account-id": account_id, "states": order_states, "types": order_types, "start-date": start_date, "end-date": end_date, "from": from_id, "direct": direct, "size": size, "client-req-id": client_req_id } def subscription(connection): connection.send(request_order_list_channel(symbol=symbol, account_id=account_id, states_str=order_states, client_req_id=client_req_id, more_key=params)) self.trade_client_socket.execute_subscribe_v1(subscription, callback, error_handler, is_trade=True) def req_order_detail(self, order_id: str, callback, client_req_id: str = None, error_handler=None): """ Subscribe candlestick/kline event. If the candlestick/kline is updated, server will send the data to client and onReceive in callback will be called. :param order_id: order_id: symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt". interval: The candlestick/kline interval, MIN1, MIN5, DAY1 etc. :param callback: The implementation is required. onReceive will be called if receive server's update. example: def callback(candlestick_event: 'CandlestickEvent'): pass :param client_req_id: client request ID :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server example: def error_handler(exception: 'HuobiApiException') pass :return: No return """ check_should_not_none(order_id, "order_id") check_should_not_none(callback, "callback") def subscription(connection): connection.send(request_order_detail_channel(order_id, client_req_id)) self.trade_client_socket.execute_subscribe_v1(subscription, callback, error_handler, is_trade=True) def get_order(self, order_id: int): """ Get the details of an order. :param order_id: The order id. (mandatory) :return: The information of order. """ check_should_not_none(order_id, "order_id") params = { "order_id": order_id, } def get_channel(): path = "/v1/order/orders/{}" return path.format(order_id) return self.trade_client.request_process(HttpMethod.GET_SIGN, get_channel(), params) def get_order_by_client_order_id(self, client_order_id): check_should_not_none(client_order_id, "clientOrderId") params = { "clientOrderId": client_order_id, } channel = "/v1/order/orders/getClientOrder" return self.trade_client.request_process(HttpMethod.GET_SIGN, channel, params) def get_orders(self, symbol: str, order_state: OrderState, order_type: OrderType = None, start_date: str = None, end_date: str = None, start_id: 'int' = None, size: 'int' = None, direct=None) -> list: check_symbol(symbol) check_should_not_none(order_state, "order_state") start_date = format_date(start_date, "start_date") end_date = format_date(end_date, "end_date") params = { "symbol": symbol, "types": order_type, "start-date": start_date, "end-date": end_date, "from": start_id, "states": order_state, "size": size, "direct": direct } channel = "/v1/order/orders" return self.trade_client.request_process(HttpMethod.GET_SIGN, channel, params) def get_open_orders(self, symbol: str, account_id: 'int', side: 'OrderSide' = None, size: 'int' = None, from_id=None, direct=None) -> list: """ The request of get open orders. :param symbol: The symbol, like "btcusdt". (mandatory) :param account_id: account id (mandatory) :param side: The order side, buy or sell. If no side defined, will return all open orders of the account. (optional) :param size: The number of orders to return. Range is [1, 500]. (optional) :param direct: 1:prev order by ID asc from from_id, 2:next order by ID desc from from_id :param from_id: start ID for search :return: The orders information. """ check_symbol(symbol) check_range(size, 1, 500, "size") check_should_not_none(account_id, "account_id") params = { "symbol": symbol, "account-id": account_id, "side": side, "size": size, "from": from_id, "direct": direct } channel = "/v1/order/openOrders" return self.trade_client.request_process(HttpMethod.GET_SIGN, channel, params) def get_history_orders(self, symbol=None, start_time=None, end_time=None, size=None, direct=None) -> list: """ Transfer Asset between Futures and Contract. :param direct: :param symbol: The target sub account uid to transfer to or from. (optional) :param start_time: The crypto currency to transfer. (optional) :param end_time: The amount of asset to transfer. (optional) :param size: The type of transfer, need be "futures-to-pro" or "pro-to-futures" (optional) :return: The Order list. """ params = { "symbol": symbol, "start-time": start_time, "end-time": end_time, "size": size, "direct": direct } channel = "/v1/order/history" return self.trade_client.request_process(HttpMethod.GET_SIGN, channel, params) def get_match_result(self, symbol: str, order_type: OrderSide = None, start_date: str = None, end_date: str = None, size: 'int' = None, from_id: 'int' = None, direct: str = None): """ Search for the trade records of an account. :param direct: direct :param symbol: The symbol, like "btcusdt" (mandatory). :param order_type: The types of order to include in the search (optional). :param start_date: Search starts date in format yyyy-mm-dd. (optional). :param end_date: Search ends date in format yyyy-mm-dd. (optional). :param size: The number of orders to return, range [1-100]. (optional). :param from_id: Search order id to begin with. (optional). :return: """ check_symbol(symbol) start_date = format_date(start_date, "start_date") end_date = format_date(end_date, "end_date") check_range(size, 1, 100, "size") params = { "symbol": symbol, "start-date": start_date, "end-date": end_date, "types": order_type, "size": size, "from": from_id, "direct": direct } channel = "/v1/order/matchresults" return self.trade_client.request_process(HttpMethod.GET_SIGN, channel, params) def get_match_results_by_order_id(self, order_id: int) -> list: """ Get detail match results of an order. :param order_id: The order id. (mandatory) :return: The list of match result. """ check_should_not_none(order_id, "order_id") params = { "order_id": order_id } def get_channel(): path = "/v1/order/orders/{}/matchresults" return path.format(order_id) return self.trade_client.request_process(HttpMethod.GET_SIGN, get_channel(), params) @staticmethod def order_source_desc(account_type): default_source = "api" if account_type: if account_type == AccountType.MARGIN: return "margin-api" return default_source @staticmethod def create_order_param_check(symbol: str, account_id: int, order_type: OrderType, amount: float, price: float, source: str, client_order_id=None, stop_price=None, operator=None): check_symbol(symbol) check_should_not_none(account_id, "account_id") check_should_not_none(order_type, "order_type") check_should_not_none(amount, "amount") check_should_not_none(source, "source") if order_type == OrderType.SELL_LIMIT \ or order_type == OrderType.BUY_LIMIT \ or order_type == OrderType.BUY_LIMIT_MAKER \ or order_type == OrderType.SELL_LIMIT_MAKER: check_should_not_none(price, "price") if order_type in [OrderType.SELL_MARKET, OrderType.BUY_MARKET]: price = None params = { "account-id": account_id, "amount": amount, "price": price, "symbol": symbol, "type": order_type, "source": source, "client-order-id": client_order_id, "stop-price": stop_price, "operator": operator } return params def create_order(self, symbol: str, account_id: 'int', order_type: 'OrderType', amount: 'float', price: 'float', source: str, client_order_id=None, stop_price=None, operator=None) -> int: """ Make an order in huobi. :param symbol: The symbol, like "btcusdt". (mandatory) :param account_id: Account id. (mandatory) :param order_type: The order type. (mandatory) :param source: The order source. (mandatory) for spot, it's "api", see OrderSource.API for margin, it's "margin-api", see OrderSource.MARGIN_API for super margin, it's "super-margin-api", see OrderSource.SUPER_MARGIN_API :param amount: The amount to buy (quote currency) or to sell (base currency). (mandatory) :param price: The limit price of limit order, only needed for limit order. (mandatory for buy-limit, sell-limit, buy-limit-maker and sell-limit-maker) :param client_order_id: unique Id which is user defined and must be unique in recent 24 hours :param stop_price: Price for auto sell to get the max benefit :param operator: the condition for stop_price, value can be "gte" or "lte", gte – greater than and equal (>=), lte – less than and equal (<=) :return: The order id. """ params = self.create_order_param_check(symbol, account_id, order_type, amount, price, source, client_order_id, stop_price, operator) channel = "/v1/order/orders/place" return self.trade_client.request_process(HttpMethod.POST_SIGN, channel, params) def create_spot_order(self, symbol: str, account_id: 'int', order_type: 'OrderType', amount: 'float', price: 'float', client_order_id=None, stop_price=None, operator=None) -> int: order_source = OrderSource.API return self.create_order(symbol=symbol, account_id=account_id, order_type=order_type, amount=amount, price=price, source=order_source, client_order_id=client_order_id, stop_price=stop_price, operator=operator) def create_margin_order(self, symbol: str, account_id: 'int', order_type: 'OrderType', amount: 'float', price: 'float', client_order_id=None, stop_price=None, operator=None) -> int: order_source = OrderSource.MARGIN_API return self.create_order(symbol=symbol, account_id=account_id, order_type=order_type, amount=amount, price=price, source=order_source, client_order_id=client_order_id, stop_price=stop_price, operator=operator) def create_super_margin_order(self, symbol: str, account_id: 'int', order_type: 'OrderType', amount: 'float', price: 'float', client_order_id=None, stop_price=None, operator=None) -> int: order_source = OrderSource.SUPER_MARGIN_API return self.create_order(symbol=symbol, account_id=account_id, order_type=order_type, amount=amount, price=price, source=order_source, client_order_id=client_order_id, stop_price=stop_price, operator=operator) def cancel_order(self, symbol, order_id): check_symbol(symbol) check_should_not_none(order_id, "order_id") params = { "order_id": order_id } order_id = params["order_id"] def get_channel(): path = "/v1/order/orders/{}/submitcancel" return path.format(order_id) return self.trade_client.request_process(HttpMethod.POST_SIGN, get_channel(), params) def cancel_orders(self, symbol, order_id_list): """ Submit cancel request for cancelling multiple orders. :param symbol: The symbol, like "btcusdt". (mandatory) :param order_id_list: The list of order id. the max size is 50. (mandatory) :return: No return """ check_symbol(symbol) check_should_not_none(order_id_list, "order_id_list") check_list(order_id_list, 1, 50, "order_id_list") string_list = list() for order_id in order_id_list: string_list.append(str(order_id)) params = { "order-ids": string_list } channel = "/v1/order/orders/batchcancel" return self.trade_client.request_process(HttpMethod.POST_SIGN, channel, params) def cancel_open_orders(self, account_id, symbols: str = None, side=None, size=None): """ Request to cancel open orders. :param account_id: account_id :param symbols: The symbol, like "btcusdt". :param side: The order side, buy or sell. If no side defined, will cancel all open orders of the account. (optional) :param size: The number of orders to cancel. Range is [1, 100]. (optional) :return: Status of batch cancel result. """ check_should_not_none(account_id, "account_id") params = { "account-id": account_id, "symbol": symbols, "side": side, "size": size } channel = "/v1/order/orders/batchCancelOpenOrders" return self.trade_client.request_process(HttpMethod.POST_SIGN, channel, params) def cancel_client_order(self, client_order_id) -> int: """ Request to cancel open orders. :param client_order_id: user defined unique order id """ check_should_not_none(client_order_id, "client-order-id") params = { "client-order-id": client_order_id } channel = "/v1/order/orders/submitCancelClientOrder" return self.trade_client.request_process(HttpMethod.POST_SIGN, channel, params) def transfer_between_futures_and_pro(self, currency: str, amount: 'float', transfer_type: TransferFuturesPro) -> int: """ Transfer Asset between Futures and Contract. :sub_uid: The target sub account uid to transfer to or from. (mandatory) :param currency: The crypto currency to transfer. (mandatory) :param amount: The amount of asset to transfer. (mandatory) :param transfer_type: The type of transfer, need be "futures-to-pro" or "pro-to-futures" (mandatory) :return: The order id. """ check_currency(currency) check_should_not_none(currency, "currency") check_should_not_none(amount, "amount") check_should_not_none(transfer_type, "transfer_type") params = { "currency": currency, "amount": amount, "type": transfer_type } channel = "/v1/futures/transfer" return self.trade_client.request_process(HttpMethod.POST_SIGN, channel, params) def batch_create_order(self, order_config_list) -> int: """ Make an order in huobi. :param order_config_list: order config list, it can batch create orders, and each order config check as below : items as below : symbol: The symbol, like "btcusdt". (mandatory) : account_type: Account type. (mandatory) : order_type: The order type. (mandatory) : amount: The amount to buy (quote currency) or to sell (base currency). (mandatory) : price: The limit price of limit order, only needed for limit order. (mandatory for buy-limit, sell-limit, buy-limit-maker and sell-limit-maker) : client_order_id: unique Id which is user defined and must be unique in recent 24 hours : stop_price: Price for auto sell to get the max benefit : operator: the condition for stop_price, value can be "gte" or "lte", gte – greater than and equal (>=), lte – less than and equal (<=) :return: The order id. """ check_should_not_none(order_config_list, "order_config_list") check_list(order_config_list, 1, 10, "create order config list") new_config_list = list() for item in order_config_list: new_item = self.create_order_param_check( item.get("symbol", None), item.get("account_id", None), item.get("order_type", None), item.get("amount", None), item.get("price", None), item.get("source", None), item.get("client_order_id", None), item.get("stop-price", None), item.get("operator", None)) new_config_list.append(new_item) channel = "/v1/order/batch-orders" return self.trade_client.request_process_post_batch(HttpMethod.POST_SIGN, channel, new_config_list) def sub_trade_clearing(self, symbols: str, callback, error_handler=None): """ Subscribe trade clearing by symbol :param symbols: The symbols, like "btcusdt". Use comma to separate multi symbols, like "btcusdt,ethusdt". "*" for all symbols :param callback: The implementation is required. onReceive will be called if receive server's update. example: def callback(price_depth_event: 'PriceDepthEvent'): pass :param error_handler: The error handler will be called if subscription failed or error happen between client and Huobi server example: def error_handler(exception: 'HuobiApiException') pass :return: No return """ check_should_not_none(symbols, "symbols") symbol_list = symbols.split(",") if "*" in symbol_list: symbol_list = ["*"] else: check_symbol_list(symbol_list) check_should_not_none(callback, "callback") def subscription(connection): for symbol in symbol_list: connection.send(trade_clearing_channel(symbol)) time.sleep(0.01) return self.trade_client_socket_sub.execute_subscribe_v2(subscription, callback, error_handler, is_trade=True)