Example #1
0
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)
Example #2
0
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)
Example #3
0
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)