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.rest_api_sync_client = RestApiSyncClient(*args, **kwargs) self.web_socket_req_client = WebSocketReqClient(*args, **kwargs) self.sub_socket_req_client = SubscribeClient(*args, **kwargs)
class AlgoClient(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.rest_api_sync_client = RestApiSyncClient(*args, **kwargs) self.web_socket_req_client = WebSocketReqClient(*args, **kwargs) self.sub_socket_req_client = SubscribeClient(*args, **kwargs) def create_order(self, account_id: 'int', symbol: 'str', order_side: OrderSide, order_type: OrderType, client_order_id: 'str', stop_price: 'str', order_price: 'str' = None, order_size: 'str' = None, order_value: 'str' = None, time_in_force: 'str' = None, trailing_rate: 'str' = None) -> int: """ Make an algo order in huobi. :param account_id: Account id. (mandatory) :param symbol: The symbol, like "btcusdt". (mandatory) :param order_side: the Order side, possible values: buy,sell. (mandatory) :param order_type: The order type, possible values: limit, market. (mandatory) :param stop_price: The stop price. (mandatory) :param order_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 order_size: The amount of market order only :param order_value: for market buy order only :param stop_price: Price for auto sell to get the max benefit :param time_in_force: gtc(invalid for orderType=market), boc(invalid orderType=market),ioc, fok(invalid for orderType=market) :param trailing_rate: for trailing orders only :param client_order_id: unique Id which is user defined and must be unique in recent 24 hours """ params = self.create_order_param_check(symbol, account_id, order_side, order_type, stop_price, order_price, order_size, order_value, time_in_force, trailing_rate, client_order_id) channel = "/v2/algo-orders" return self.rest_api_sync_client.request_process(HttpMethod.POST_SIGN, channel, params) def cancel_orders(self, client_order_ids): check_should_not_none(client_order_ids, "clientOrderIds") channel = "/v2/algo-orders/cancellation" params = { "clientOrderIds": client_order_ids } return self.rest_api_sync_client.request_process(HttpMethod.POST_SIGN, channel, params) def get_open_orders(self, account_id: 'str' = None, symbol: 'str' = None, order_side: OrderSide = None, order_type: AlgoOrderType = None, sort: SortDesc = None, limit: 'int' = 100, from_id: 'int' = None): params = { "accountId": account_id, "symbol": symbol, "orderSide": order_side, "orderType": order_type, "sort": sort, "limit": limit, "fromId": from_id } channel = "/v2/algo-orders/cancellation" return self.rest_api_sync_client.request_process(HttpMethod.GET_SIGN, channel, params) def get_order_history(self, symbol: 'str', order_status: AlgoOrderStatus, account_id: 'str' = None, order_side: 'OrderSide' = None, order_type: 'AlgoOrderType' = None, start_time: 'int' = None, end_time: 'int' = None, sort: 'SortDesc' = SortDesc.DESC, limit: 'int' = 100, from_id: 'int' = None): params = { "symbol": symbol, "accountId": account_id, "orderSide": order_side, "orderType": order_type, "orderStatus": order_status, "startTime": start_time, "endTime": end_time, "sort": sort, "limit": limit, "fromId": from_id } channel = "/v2/algo-orders/history" return self.rest_api_sync_client.request_process(HttpMethod.GET_SIGN, channel, params) def get_order(self, client_order_id: 'str'): params = { "clientOrderId": client_order_id } channel = "/v2/algo-orders/specific" return self.rest_api_sync_client.request_process(HttpMethod.GET_SIGN, channel, params) @staticmethod def create_order_param_check(symbol, account_id, order_side, order_type, stop_price, order_price, order_size, order_value, time_in_force, trailing_rate, client_order_id): check_symbol(symbol) check_should_not_none(account_id, "accountId") check_should_not_none(order_type, "orderType") check_should_not_none(order_side, "orderSide") 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(order_price, "orderPrice") if time_in_force is not None: check_time_in_force(time_in_force) if order_type in [OrderType.SELL_MARKET, OrderType.BUY_MARKET]: order_price = None params = { "accountId": account_id, "symbol": symbol, "orderPrice": order_price, "orderSide": order_side, "orderSize": order_size, "orderValue": order_value, "timeInForce": time_in_force, "orderType": order_type, "clientOrderId": client_order_id, "stopPrice": stop_price, "trailingRate": trailing_rate } return params
class WalletClient(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.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_deposit_withdraw(self, op_type: 'str', currency: 'str' = None, from_id: 'int' = None, size: 'int' = None, direct: 'str' = None) -> list: """ Get the withdraw records of an account. :param currency: The currency, like "btc". (optional) :param from_id: The beginning withdraw record id. (optional) :param op_type: deposit or withdraw, see defination DepositWithdraw (mandatory) :param size: The size of record. (optional) :param direct: "prev" is order by asc, "next" is order by desc, default as "prev"(optional) :return: The list of withdraw records. """ check_should_not_none(op_type, "operate type") params = { "currency": currency, "type": op_type, "from": from_id, "direct": direct, "size": size } channel = "/v1/query/deposit-withdraw" return self.rest_api_sync_client.request_process( HttpMethod.GET_SIGN, channel, params) def post_create_withdraw(self, address: 'str', amount: 'float', currency: 'str', fee: 'float', chain: 'str' = None, address_tag: 'str' = None) -> int: """ Submit a request to withdraw some asset from an account. :param address: The destination address of this withdraw. (mandatory) :param amount: The amount of currency to withdraw. (mandatory) :param currency: The crypto currency to withdraw. (mandatory) :param fee: The fee to pay with this withdraw. (mandatory) :param address_tag: A tag specified for this address. (optional) :param chain: set as "usdt" to withdraw USDT to OMNI, set as "trc20usdt" to withdraw USDT to TRX. (optional) :return: Withdraw id """ check_symbol(currency) check_should_not_none(address, "address") check_should_not_none(amount, "amount") check_should_not_none(fee, "fee") params = { "currency": currency, "address": address, "amount": amount, "fee": fee, "chain": chain, "addr-tag": address_tag } channel = "/v1/dw/withdraw/api/create" return self.rest_api_sync_client.request_process( HttpMethod.POST_SIGN, channel, params) def post_cancel_withdraw(self, withdraw_id: 'int') -> int: """ Cancel an withdraw request. :param withdraw_id: withdraw id (mandatory) :return: No return. """ params = {"withdraw-id": withdraw_id} def get_channel(): path = "/v1/dw/withdraw-virtual/{}/cancel" return path.format(withdraw_id) return self.rest_api_sync_client.request_process( HttpMethod.POST_SIGN, get_channel(), params) def get_account_deposit_address(self, currency: 'str') -> list: """ Get deposit address of corresponding chain, for a specific crypto currency (except IOTA) :param currency: The currency, like "btc". (optional) :return: """ check_should_not_none(currency, "currency") params = {"currency": currency} channel = "/v2/account/deposit/address" return self.rest_api_sync_client.request_process( HttpMethod.GET_SIGN, channel, params) def get_account_withdraw_quota(self, currency: 'str') -> list: """ Get the withdraw quota for currencies :param currency: The currency, like "btc". (mandatory) :return: """ check_should_not_none(currency, "currency") params = { "currency": currency, } channel = "/v2/account/withdraw/quota" return self.rest_api_sync_client.request_process( HttpMethod.GET_SIGN, channel, params) def get_sub_user_deposit_history(self, sub_uid: 'int', currency: 'str' = None, start_time: 'int' = None, end_time: 'int' = None, sort: 'str' = None, limit: 'int' = None, from_id: 'int' = None): """ Parent get sub user depoist history. :param sub_uid: Sub user id. (mandatory) :param currency: Cryptocurrency. :param start_time: Farthest time :param end_time: Nearest time :param sort: Sorting order :param limit: Maximum number of items in one page :param from_id: First record Id in this query """ check_should_not_none(sub_uid, "sub_uid") params = { "subUid": sub_uid, "currency": currency, "startTime": start_time, "endTime": end_time, "sort": sort, "limit": limit, "fromId": from_id } channel = "/v2/sub-user/query-deposit" return self.rest_api_sync_client.request_process( HttpMethod.GET_SIGN, channel, params) def get_sub_user_deposit_address(self, sub_uid: 'int', currency: 'str') -> list: """ Parent get sub user deposit address :param sub_uid: Sub user id :param currency: Cryptocurrency, like "btc". (mandatory) :return: """ check_should_not_none(sub_uid, "subUid") check_should_not_none(currency, "currency") params = {"subUid": sub_uid, "currency": currency} channel = "/v2/sub-user/deposit-address" return self.rest_api_sync_client.request_process( HttpMethod.GET_SIGN, channel, params) def get_account_withdraw_address(self, currency: 'str', chain: 'str' = None, note: 'str' = None, limit: 'int' = 100, fromid: 'int' = None): check_should_not_none(currency, "currency") params = { "currency": currency, "chain": chain, "note": note, "limit": limit, "fromid": fromid } channel = "/v2/account/withdraw/address" return self.rest_api_sync_client.request_process( HttpMethod.GET_SIGN, channel, params)
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)
class GenericClient(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.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_exchange_timestamp(self) -> int: """ Get the timestamp from Huobi server. The timestamp is the Unix timestamp in millisecond. The count shows how many milliseconds passed from Jan 1st 1970, 00:00:00.000 at UTC. e.g. 1546300800000 is Thu, 1st Jan 2019 00:00:00.000 UTC. :return: The timestamp in UTC """ channel = "/v1/common/timestamp" params = {} return self.rest_api_sync_client.request_process( HttpMethod.GET, channel, params) def get_exchange_currencies(self): """ Get all the trading assets and currencies supported in huobi. The information of trading instrument, including base currency, quote precision, etc. :return: The information of trading currencies. """ channel = "/v1/common/currencys" params = {} return self.rest_api_sync_client.request_process( HttpMethod.GET, channel, params) def get_exchange_symbols(self): """ Get all the trading assets and currencies supported in huobi. The information of trading instrument etc. :return: The information of trading instrument. """ channel = "/v1/common/symbols" params = {} return self.rest_api_sync_client.request_process( HttpMethod.GET, channel, params) def get_exchange_info(self): """ Get all the trading assets and currencies supported in huobi. The information of trading instrument, including base currency, quote precision, etc. :return: The information of trading instrument and currencies. """ ret = { "symbol_list": self.get_exchange_symbols(), "currencies": self.get_exchange_currencies() } return ret def get_reference_currencies(self, currency: 'str' = None, is_authorized_user: '******' = None) -> list: """ Get all the trading assets and currencies supported in huobi. The information of trading instrument, including base currency, quote precision, etc. :param currency: btc, ltc, bch, eth, etc ...(available currencies in Huobi Global) :param is_authorized_user: is Authorized user? True or False :return: The information of trading instrument and currencies. """ channel = "/v2/reference/currencies" params = {"currency": currency, "authorizedUser": is_authorized_user} return self.rest_api_sync_client.request_process( HttpMethod.GET, channel, params) def get_system_status(self) -> str: """ get system status :return: system status. """ channel = "/api/v2/summary.json" temp = self.rest_api_sync_client.__server_url self.rest_api_sync_client.__server_url = "https://status.huobigroup.com" res = self.rest_api_sync_client.request_process( HttpMethod.GET, channel, {}) self.rest_api_sync_client.__server_url = temp return res def get_market_status(self): channel = "/v2/market-status" return self.rest_api_sync_client.request_process( HttpMethod.GET, channel, {})
class EtfClient(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.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_etf_swap_config(self, etf_name: 'str'): """ Get the basic information of ETF creation and redemption, as well as ETF constituents, including max amount of creation, min amount of creation, max amount of redemption, min amount of redemption, creation fee rate, redemption fee rate, eft create/redeem status. :param etf_name: The symbol, currently only support hb10. (mandatory) :return: The etf configuration information. """ check_symbol(etf_name) params = {"etf_name": etf_name} channel = "/etf/swap/config" return self.rest_api_sync_client.request_process( HttpMethod.GET, channel, params) def get_etf_swap_list(self, etf_name: 'str', offset: 'int', size: 'int') -> list: """ Get past creation and redemption.(up to 100 records) :param etf_name: The symbol, currently only support hb10. (mandatory) :param offset: The offset of the records, set to 0 for the latest records. (mandatory) :param size: The number of records to return, the range is [1, 100]. (mandatory) :return: The swap history. """ check_symbol(etf_name) params = {"etf_name": etf_name, "offset": offset, "limit": size} channel = "/etf/swap/list" return self.rest_api_sync_client.request_process( HttpMethod.GET_SIGN, channel, params) def post_etf_swap_in(self, etf_name: 'str', amount: 'int') -> None: """ Order creation or redemption of ETF. :param etf_name: The symbol, currently only support hb10. (mandatory) :param amount: The amount to create or redemption. (mandatory) :return: No return """ check_symbol(etf_name) check_should_not_none(amount, "amount") params = {"etf_name": etf_name, "amount": amount} channel = "/etf/swap/in" return self.rest_api_sync_client.request_process( HttpMethod.POST_SIGN, channel, params) def post_etf_swap_out(self, etf_name: 'str', amount: 'int') -> None: """ Order creation or redemption of ETF. :param etf_name: The symbol, currently only support hb10. (mandatory) :param amount: The amount to create or redemption. (mandatory) :return: No return """ check_symbol(etf_name) check_should_not_none(amount, "amount") params = {"etf_name": etf_name, "amount": amount} channel = "/etf/swap/out" return self.rest_api_sync_client.request_process( HttpMethod.POST_SIGN, channel, params)