<?php

namespace ccxt\async;

// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code

use Exception; // a common import
use ccxt\async\abstract\hashkey as Exchange;
use ccxt\ExchangeError;
use ccxt\ArgumentsRequired;
use ccxt\BadRequest;
use ccxt\NotSupported;
use ccxt\Precise;
use \React\Async;
use \React\Promise\PromiseInterface;

class hashkey extends Exchange {

    public function describe(): mixed {
        return $this->deep_extend(parent::describe(), array(
            'id' => 'hashkey',
            'name' => 'HashKey Global',
            'countries' => array( 'BM' ), // Bermuda
            'rateLimit' => 100,
            'version' => 'v1',
            'certified' => true,
            'pro' => true,
            'has' => array(
                'CORS' => null,
                'spot' => true,
                'margin' => false,
                'swap' => false,
                'future' => false,
                'option' => false,
                'addMargin' => false,
                'borrowCrossMargin' => false,
                'borrowIsolatedMargin' => false,
                'borrowMargin' => false,
                'cancelAllOrders' => true,
                'cancelAllOrdersAfter' => false,
                'cancelOrder' => true,
                'cancelOrders' => true,
                'cancelWithdraw' => false,
                'closeAllPositions' => false,
                'closePosition' => false,
                'createConvertTrade' => false,
                'createDepositAddress' => false,
                'createMarketBuyOrderWithCost' => true,
                'createMarketOrder' => true,
                'createMarketOrderWithCost' => false,
                'createMarketSellOrderWithCost' => false,
                'createOrder' => true,
                'createOrderWithTakeProfitAndStopLoss' => false,
                'createReduceOnlyOrder' => true,
                'createStopLimitOrder' => true,
                'createStopLossOrder' => false,
                'createStopMarketOrder' => true,
                'createStopOrder' => true,
                'createTakeProfitOrder' => false,
                'createTrailingAmountOrder' => false,
                'createTrailingPercentOrder' => false,
                'createTriggerOrder' => true,
                'fetchAccounts' => true,
                'fetchAllGreeks' => false,
                'fetchBalance' => true,
                'fetchBorrowInterest' => false,
                'fetchBorrowRate' => false,
                'fetchBorrowRateHistories' => false,
                'fetchBorrowRateHistory' => false,
                'fetchBorrowRates' => false,
                'fetchBorrowRatesPerSymbol' => false,
                'fetchCanceledAndClosedOrders' => true,
                'fetchCanceledOrders' => true,
                'fetchClosedOrder' => true,
                'fetchClosedOrders' => false,
                'fetchConvertCurrencies' => false,
                'fetchConvertQuote' => false,
                'fetchConvertTrade' => false,
                'fetchConvertTradeHistory' => false,
                'fetchCrossBorrowRate' => false,
                'fetchCrossBorrowRates' => false,
                'fetchCurrencies' => true,
                'fetchDepositAddress' => true,
                'fetchDepositAddresses' => false,
                'fetchDepositAddressesByNetwork' => false,
                'fetchDeposits' => true,
                'fetchDepositsWithdrawals' => false,
                'fetchFundingHistory' => false,
                'fetchFundingInterval' => false,
                'fetchFundingIntervals' => false,
                'fetchFundingRate' => true,
                'fetchFundingRateHistory' => true,
                'fetchFundingRates' => true,
                'fetchGreeks' => false,
                'fetchIndexOHLCV' => false,
                'fetchIsolatedBorrowRate' => false,
                'fetchIsolatedBorrowRates' => false,
                'fetchIsolatedPositions' => false,
                'fetchLedger' => true,
                'fetchLeverage' => true,
                'fetchLeverages' => false,
                'fetchLeverageTiers' => true,
                'fetchLiquidations' => false,
                'fetchLongShortRatio' => false,
                'fetchLongShortRatioHistory' => false,
                'fetchMarginAdjustmentHistory' => false,
                'fetchMarginMode' => false,
                'fetchMarginModes' => false,
                'fetchMarketLeverageTiers' => 'emulated',
                'fetchMarkets' => true,
                'fetchMarkOHLCV' => false,
                'fetchMarkPrice' => false,
                'fetchMarkPrices' => false,
                'fetchMyLiquidations' => false,
                'fetchMySettlementHistory' => false,
                'fetchMyTrades' => true,
                'fetchOHLCV' => true,
                'fetchOpenInterest' => false,
                'fetchOpenInterestHistory' => false,
                'fetchOpenInterests' => false,
                'fetchOpenOrder' => false,
                'fetchOpenOrders' => true,
                'fetchOption' => false,
                'fetchOptionChain' => false,
                'fetchOrder' => true,
                'fetchOrderBook' => true,
                'fetchOrders' => false,
                'fetchOrderTrades' => false,
                'fetchPosition' => false,
                'fetchPositionHistory' => false,
                'fetchPositionMode' => false,
                'fetchPositions' => true,
                'fetchPositionsForSymbol' => true,
                'fetchPositionsHistory' => false,
                'fetchPositionsRisk' => false,
                'fetchPremiumIndexOHLCV' => false,
                'fetchSettlementHistory' => false,
                'fetchStatus' => true,
                'fetchTicker' => true,
                'fetchTickers' => true,
                'fetchTime' => true,
                'fetchTrades' => true,
                'fetchTradingFee' => true, // emulated for spot markets
                'fetchTradingFees' => true, // for spot markets only
                'fetchTransactions' => false,
                'fetchTransfers' => false,
                'fetchUnderlyingAssets' => false,
                'fetchVolatilityHistory' => false,
                'fetchWithdrawals' => true,
                'reduceMargin' => false,
                'repayCrossMargin' => false,
                'repayIsolatedMargin' => false,
                'sandbox' => false,
                'setLeverage' => true,
                'setMargin' => false,
                'setMarginMode' => false,
                'setPositionMode' => false,
                'transfer' => true,
                'withdraw' => true,
            ),
            'timeframes' => array(
                '1m' => '1m',
                '3m' => '3m',
                '5m' => '5m',
                '15m' => '15m',
                '30m' => '30m',
                '1h' => '1h',
                '2h' => '2h',
                '4h' => '4h',
                '6h' => '6h',
                '8h' => '8h',
                '12h' => '12h',
                '1d' => '1d',
                '1w' => '1w',
                '1M' => '1M',
            ),
            'urls' => array(
                'logo' => 'https://github.com/user-attachments/assets/6dd6127b-cc19-4a13-9b29-a98d81f80e98',
                'api' => array(
                    'public' => 'https://api-glb.hashkey.com',
                    'private' => 'https://api-glb.hashkey.com',
                ),
                'test' => array(
                    'public' => 'https://api-glb.sim.hashkeydev.com',
                    'private' => 'https://api-glb.sim.hashkeydev.com',
                ),
                'www' => 'https://global.hashkey.com/',
                'doc' => 'https://hashkeyglobal-apidoc.readme.io/',
                'fees' => 'https://support.global.hashkey.com/hc/en-us/articles/13199900083612-HashKey-Global-Fee-Structure',
                'referral' => 'https://global.hashkey.com/en-US/register/invite?invite_code=82FQUN',
            ),
            'api' => array(
                'public' => array(
                    'get' => array(
                        'api/v1/exchangeInfo' => 5,
                        'quote/v1/depth' => 1,
                        'quote/v1/trades' => 1,
                        'quote/v1/klines' => 1,
                        'quote/v1/ticker/24hr' => 1,
                        'quote/v1/ticker/price' => 1,
                        'quote/v1/ticker/bookTicker' => 1, // not unified
                        'quote/v1/depth/merged' => 1,
                        'quote/v1/markPrice' => 1,
                        'quote/v1/index' => 1,
                        'api/v1/futures/fundingRate' => 1,
                        'api/v1/futures/historyFundingRate' => 1,
                        'api/v1/ping' => 1,
                        'api/v1/time' => 1,
                    ),
                ),
                'private' => array(
                    'get' => array(
                        'api/v1/spot/order' => 1,
                        'api/v1/spot/openOrders' => 1,
                        'api/v1/spot/tradeOrders' => 5,
                        'api/v1/futures/leverage' => 1,
                        'api/v1/futures/order' => 1,
                        'api/v1/futures/openOrders' => 1,
                        'api/v1/futures/userTrades' => 1,
                        'api/v1/futures/positions' => 1,
                        'api/v1/futures/historyOrders' => 1,
                        'api/v1/futures/balance' => 1,
                        'api/v1/futures/liquidationAssignStatus' => 1,
                        'api/v1/futures/riskLimit' => 1,
                        'api/v1/futures/commissionRate' => 1,
                        'api/v1/futures/getBestOrder' => 1,
                        'api/v1/account/vipInfo' => 1,
                        'api/v1/account' => 1,
                        'api/v1/account/trades' => 5,
                        'api/v1/account/type' => 5,
                        'api/v1/account/checkApiKey' => 1,
                        'api/v1/account/balanceFlow' => 5,
                        'api/v1/spot/subAccount/openOrders' => 1,
                        'api/v1/spot/subAccount/tradeOrders' => 1,
                        'api/v1/subAccount/trades' => 1,
                        'api/v1/futures/subAccount/openOrders' => 1,
                        'api/v1/futures/subAccount/historyOrders' => 1,
                        'api/v1/futures/subAccount/userTrades' => 1,
                        'api/v1/account/deposit/address' => 1,
                        'api/v1/account/depositOrders' => 1,
                        'api/v1/account/withdrawOrders' => 1,
                    ),
                    'post' => array(
                        'api/v1/userDataStream' => 1,
                        'api/v1/spot/orderTest' => 1,
                        'api/v1/spot/order' => 1,
                        'api/v1.1/spot/order' => 1,
                        'api/v1/spot/batchOrders' => 5,
                        'api/v1/futures/leverage' => 1,
                        'api/v1/futures/order' => 1,
                        'api/v1/futures/position/trading-stop' => 3,
                        'api/v1/futures/batchOrders' => 5,
                        'api/v1/account/assetTransfer' => 1,
                        'api/v1/account/authAddress' => 1,
                        'api/v1/account/withdraw' => 1,
                    ),
                    'put' => array(
                        'api/v1/userDataStream' => 1,
                    ),
                    'delete' => array(
                        'api/v1/spot/order' => 1,
                        'api/v1/spot/openOrders' => 5,
                        'api/v1/spot/cancelOrderByIds' => 5,
                        'api/v1/futures/order' => 1,
                        'api/v1/futures/batchOrders' => 1,
                        'api/v1/futures/cancelOrderByIds' => 1,
                        'api/v1/userDataStream' => 1,
                    ),
                ),
            ),
            'fees' => array(
                'trading' => array(
                    'spot' => array(
                        'tierBased' => true,
                        'percentage' => true,
                        'feeSide' => 'get',
                        'maker' => $this->parse_number('0.0012'),
                        'taker' => $this->parse_number('0.0012'),
                        'tiers' => array(
                            'maker' => array(
                                array( $this->parse_number('0'), $this->parse_number('0.0012') ),
                                array( $this->parse_number('1000000'), $this->parse_number('0.00080') ),
                                array( $this->parse_number('5000000'), $this->parse_number('0.00070') ),
                                array( $this->parse_number('10000000'), $this->parse_number('0.00060') ),
                                array( $this->parse_number('50000000'), $this->parse_number('0.00040') ),
                                array( $this->parse_number('200000000'), $this->parse_number('0.00030') ),
                                array( $this->parse_number('400000000'), $this->parse_number('0.00010') ),
                                array( $this->parse_number('800000000'), $this->parse_number('0.00') ),
                            ),
                            'taker' => array(
                                array( $this->parse_number('0'), $this->parse_number('0.0012') ),
                                array( $this->parse_number('1000000'), $this->parse_number('0.00090') ),
                                array( $this->parse_number('5000000'), $this->parse_number('0.00085') ),
                                array( $this->parse_number('10000000'), $this->parse_number('0.00075') ),
                                array( $this->parse_number('50000000'), $this->parse_number('0.00065') ),
                                array( $this->parse_number('200000000'), $this->parse_number('0.00045') ),
                                array( $this->parse_number('400000000'), $this->parse_number('0.00040') ),
                                array( $this->parse_number('800000000'), $this->parse_number('0.00035') ),
                            ),
                        ),
                    ),
                    'swap' => array(
                        'tierBased' => true,
                        'percentage' => true,
                        'feeSide' => 'get',
                        'maker' => $this->parse_number('0.00025'),
                        'taker' => $this->parse_number('0.00060'),
                        'tiers' => array(
                            'maker' => array(
                                array( $this->parse_number('0'), $this->parse_number('0.00025') ),
                                array( $this->parse_number('1000000'), $this->parse_number('0.00016') ),
                                array( $this->parse_number('5000000'), $this->parse_number('0.00014') ),
                                array( $this->parse_number('10000000'), $this->parse_number('0.00012') ),
                                array( $this->parse_number('50000000'), $this->parse_number('0.000080') ),
                                array( $this->parse_number('200000000'), $this->parse_number('0.000060') ),
                                array( $this->parse_number('400000000'), $this->parse_number('0.000020') ),
                                array( $this->parse_number('800000000'), $this->parse_number('0.00') ),
                            ),
                            'taker' => array(
                                array( $this->parse_number('0'), $this->parse_number('0.00060') ),
                                array( $this->parse_number('1000000'), $this->parse_number('0.00050') ),
                                array( $this->parse_number('5000000'), $this->parse_number('0.00045') ),
                                array( $this->parse_number('10000000'), $this->parse_number('0.00040') ),
                                array( $this->parse_number('50000000'), $this->parse_number('0.00035') ),
                                array( $this->parse_number('200000000'), $this->parse_number('0.00030') ),
                                array( $this->parse_number('400000000'), $this->parse_number('0.00025') ),
                                array( $this->parse_number('800000000'), $this->parse_number('0.00020') ),
                            ),
                        ),
                    ),
                ),
            ),
            'options' => array(
                'broker' => '10000700011',
                'recvWindow' => null,
                'sandboxMode' => false,
                'networks' => array(
                    'BTC' => 'BTC',
                    'ERC20' => 'ETH',
                    'AVAX' => 'AvalancheC',
                    'SOL' => 'Solana',
                    'MATIC' => 'Polygon',
                    'ATOM' => 'Cosmos',
                    'DOT' => 'Polkadot',
                    'LTC' => 'LTC',
                    'OPTIMISM' => 'Optimism',
                    'ARB' => 'Arbitrum',
                    'DOGE' => 'Dogecoin',
                    'TRC20' => 'Tron',
                    'ZKSYNC' => 'zkSync',
                    'TON' => 'TON',
                    'KLAYTN' => 'Klaytn',
                    'MERLINCHAIN' => 'Merlin Chain',
                ),
                'networksById' => array(
                    'BTC' => 'BTC',
                    'Bitcoin' => 'BTC',
                    'ETH' => 'ERC20',
                    'ERC20' => 'ERC20',
                    'AvalancheC' => 'AVAX',
                    'AVAX C-Chain' => 'AVAX',
                    'Solana' => 'SOL',
                    'Cosmos' => 'ATOM',
                    'Arbitrum' => 'ARB',
                    'Polygon' => 'MATIC',
                    'Optimism' => 'OPTIMISM',
                    'Polkadot' => 'DOT',
                    'LTC' => 'LTC',
                    'Litecoin' => 'LTC',
                    'Dogecoin' => 'DOGE',
                    'Merlin Chain' => 'MERLINCHAIN',
                    'zkSync' => 'ZKSYNC',
                    'TRC20' => 'TRC20',
                    'Tron' => 'TRC20',
                    'TON' => 'TON',
                    'BSC(BEP20)' => 'BSC',
                    'Klaytn' => 'KLAYTN',
                ),
                'defaultNetwork' => 'ERC20',
            ),
            'features' => array(
                'default' => array(
                    'sandbox' => true,
                    'createOrder' => array(
                        'marginMode' => false,
                        'triggerPrice' => false,
                        'triggerPriceType' => null,
                        'triggerDirection' => false,
                        'stopLossPrice' => false,
                        'takeProfitPrice' => false,
                        'attachedStopLossTakeProfit' => null,
                        'timeInForce' => array(
                            'IOC' => true,
                            'FOK' => true,
                            'PO' => true,
                            'GTD' => false,
                        ),
                        'hedged' => false,
                        'trailing' => false,
                        'leverage' => false,
                        'marketBuyByCost' => true,
                        'marketBuyRequiresPrice' => true, // todo fix
                        'selfTradePrevention' => true, // todo implement
                        'iceberg' => false,
                    ),
                    'createOrders' => array(
                        'max' => 20,
                    ),
                    'fetchMyTrades' => array(
                        'marginMode' => false,
                        'limit' => 1000,
                        'daysBack' => 30,
                        'untilDays' => 30,
                        'symbolRequired' => false,
                    ),
                    'fetchOrder' => array(
                        'marginMode' => false,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => false,
                    ),
                    'fetchOpenOrders' => array(
                        'marginMode' => false,
                        'limit' => 1000,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => false,
                    ),
                    'fetchOrders' => null,
                    'fetchClosedOrders' => null, // todo
                    'fetchOHLCV' => array(
                        'limit' => 1000,
                    ),
                ),
                'spot' => array(
                    'extends' => 'default',
                ),
                'forDerivatives' => array(
                    'extends' => 'default',
                    'createOrder' => array(
                        'triggerPrice' => true,
                        'selfTradePrevention' => true,
                    ),
                    'fetchOpenOrders' => array(
                        'trigger' => true,
                        'limit' => 500,
                    ),
                ),
                'swap' => array(
                    'linear' => array(
                        'extends' => 'forDerivatives',
                    ),
                    'inverse' => null,
                ),
                'future' => array(
                    'linear' => null,
                    'inverse' => null,
                ),
            ),
            'commonCurrencies' => array(),
            'exceptions' => array(
                'exact' => array(
                    '0001' => '\\ccxt\\BadRequest', // Required field '%s' missing or invalid.
                    '0002' => '\\ccxt\\AuthenticationError', // Incorrect signature
                    '0003' => '\\ccxt\\RateLimitExceeded', // Rate limit exceeded
                    '0102' => '\\ccxt\\AuthenticationError', // Invalid APIKey
                    '0103' => '\\ccxt\\AuthenticationError', // APIKey expired
                    '0104' => '\\ccxt\\PermissionDenied',  // The accountId defined is not permissible
                    '0201' => '\\ccxt\\ExchangeError', // Instrument not found
                    '0202' => '\\ccxt\\PermissionDenied', // Invalid IP
                    '0206' => '\\ccxt\\BadRequest', // Unsupported order type
                    '0207' => '\\ccxt\\BadRequest', // Invalid price
                    '0209' => '\\ccxt\\BadRequest', // Invalid price precision
                    '0210' => '\\ccxt\\BadRequest', // Price outside of allowed range
                    '0211' => '\\ccxt\\OrderNotFound', // Order not found
                    '0401' => '\\ccxt\\InsufficientFunds', // Insufficient asset
                    '0402' => '\\ccxt\\BadRequest', // Invalid asset
                    '-1000' => '\\ccxt\\ExchangeError', // An unknown error occurred while processing the request
                    '-1001' => '\\ccxt\\ExchangeError', // Internal error
                    '-100010' => '\\ccxt\\BadSymbol', // Invalid Symbols!
                    '-100012' => '\\ccxt\\BadSymbol', // Parameter symbol ['strval'] missing!
                    '-1002' => '\\ccxt\\AuthenticationError', // Unauthorized operation
                    '-1004' => '\\ccxt\\BadRequest', // Bad request
                    '-1005' => '\\ccxt\\PermissionDenied', // No permission
                    '-1006' => '\\ccxt\\ExchangeError', // Execution status unknown
                    '-1007' => '\\ccxt\\RequestTimeout', // Timeout waiting for response from server
                    '-1014' => '\\ccxt\\InvalidOrder', // Unsupported order combination
                    '-1015' => '\\ccxt\\InvalidOrder', // Too many new orders
                    '-1020' => '\\ccxt\\OperationRejected', // Unsupported operation
                    '-1021' => '\\ccxt\\InvalidNonce', // Timestamp for this request is outside of the recvWindow
                    '-1024' => '\\ccxt\\BadRequest', // Duplicate request
                    '-1101' => '\\ccxt\\ExchangeNotAvailable', // Feature has been offline
                    '-1115' => '\\ccxt\\InvalidOrder', // Invalid timeInForce
                    '-1117' => '\\ccxt\\InvalidOrder', // Invalid order side
                    '-1123' => '\\ccxt\\InvalidOrder', // Invalid client order id
                    '-1124' => '\\ccxt\\InvalidOrder', // Invalid price
                    '-1126' => '\\ccxt\\InvalidOrder', // Invalid quantity
                    '-1129' => '\\ccxt\\BadRequest', // Invalid parameters, quantity and amount are not allowed to be sent at the same time.
                    '-1130' => '\\ccxt\\BadRequest', // Illegal parameter '%s'
                    '-1132' => '\\ccxt\\BadRequest', // Order price greater than the maximum
                    '-1133' => '\\ccxt\\BadRequest', // Order price lower than the minimum
                    '-1135' => '\\ccxt\\BadRequest', // Order quantity greater than the maximum
                    '-1136' => '\\ccxt\\BadRequest', // Order quantity lower than the minimum
                    '-1138' => '\\ccxt\\InvalidOrder', // Order has been partially cancelled
                    '-1137' => '\\ccxt\\InvalidOrder', // Order quantity precision too large
                    '-1139' => '\\ccxt\\OrderImmediatelyFillable', // Order has been filled
                    '-1140' => '\\ccxt\\InvalidOrder', // Order amount lower than the minimum
                    '-1141' => '\\ccxt\\DuplicateOrderId', // Duplicate order
                    '-1142' => '\\ccxt\\OrderNotFillable', // Order has been cancelled
                    '-1143' => '\\ccxt\\OrderNotFound', // Order not found on order book
                    '-1144' => '\\ccxt\\OperationRejected', // Order has been locked
                    '-1145' => '\\ccxt\\NotSupported', // Cancellation on this order type not supported
                    '-1146' => '\\ccxt\\RequestTimeout', // Order creation timeout
                    '-1147' => '\\ccxt\\RequestTimeout', // Order cancellation timeout
                    '-1148' => '\\ccxt\\InvalidOrder', // Order amount precision too large
                    '-1149' => '\\ccxt\\OperationRejected', // Order creation failed
                    '-1150' => '\\ccxt\\OperationFailed', // Order cancellation failed
                    '-1151' => '\\ccxt\\OperationRejected', // The trading pair is not open yet
                    '-1152' => '\\ccxt\\AccountNotEnabled', // User does not exist
                    '-1153' => '\\ccxt\\InvalidOrder', // Invalid price type
                    '-1154' => '\\ccxt\\InvalidOrder', // Invalid position side
                    '-1155' => '\\ccxt\\OperationRejected', // The trading pair is not available for api trading
                    '-1156' => '\\ccxt\\OperationFailed', // Limit maker order creation failed
                    '-1157' => '\\ccxt\\OperationFailed', // Modify futures margin failed
                    '-1158' => '\\ccxt\\OperationFailed', // Reduce margin is forbidden
                    '-1159' => '\\ccxt\\AccountNotEnabled', // Finance account already exists
                    '-1160' => '\\ccxt\\AccountNotEnabled', // Account does not exist
                    '-1161' => '\\ccxt\\OperationFailed', // Balance transfer failed
                    '-1162' => '\\ccxt\\ContractUnavailable', // Unsupport contract address
                    '-1163' => '\\ccxt\\InvalidAddress', // Illegal withdrawal address
                    '-1164' => '\\ccxt\\OperationFailed', // Withdraw failed
                    '-1165' => '\\ccxt\\ArgumentsRequired', // Withdrawal amount cannot be null
                    '-1166' => '\\ccxt\\OperationRejected', // Withdrawal amount exceeds the daily limit
                    '-1167' => '\\ccxt\\BadRequest', // Withdrawal amount less than the minimum
                    '-1168' => '\\ccxt\\BadRequest', // Illegal withdrawal amount
                    '-1169' => '\\ccxt\\PermissionDenied', // Withdraw not allowed
                    '-1170' => '\\ccxt\\PermissionDenied', // Deposit not allowed
                    '-1171' => '\\ccxt\\PermissionDenied', // Withdrawal address not in whitelist
                    '-1172' => '\\ccxt\\BadRequest', // Invalid from account id
                    '-1173' => '\\ccxt\\BadRequest', // Invalid to account i
                    '-1174' => '\\ccxt\\PermissionDenied', // Transfer not allowed between the same account
                    '-1175' => '\\ccxt\\BadRequest', // Invalid fiat deposit status
                    '-1176' => '\\ccxt\\BadRequest', // Invalid fiat withdrawal status
                    '-1177' => '\\ccxt\\InvalidOrder', // Invalid fiat order type
                    '-1178' => '\\ccxt\\AccountNotEnabled', // Brokerage account does not exist
                    '-1179' => '\\ccxt\\AccountSuspended', // Address owner is not true
                    '-1181' => '\\ccxt\\ExchangeError', // System error
                    '-1193' => '\\ccxt\\OperationRejected', // Order creation count exceeds the limit
                    '-1194' => '\\ccxt\\OperationRejected', // Market order creation forbidden
                    '-1195' => '\\ccxt\\BadRequest', // Market order long position cannot exceed %s above the market price
                    '-1196' => '\\ccxt\\BadRequest', // Market order short position cannot be below %s of the market price
                    '-1200' => '\\ccxt\\BadRequest', // Order buy quantity too small
                    '-1201' => '\\ccxt\\BadRequest', // Order buy quantity too large
                    '-1202' => '\\ccxt\\BadRequest', // Order sell quantity too small
                    '-1203' => '\\ccxt\\BadRequest', // Order sell quantity too large
                    '-1204' => '\\ccxt\\BadRequest', // From account must be a main account
                    '-1205' => '\\ccxt\\AccountNotEnabled', // Account not authorized
                    '-1206' => '\\ccxt\\BadRequest', // Order amount greater than the maximum
                    '-1207' => '\\ccxt\\BadRequest', // The status of deposit is invalid
                    '-1208' => '\\ccxt\\BadRequest', // The orderType of fiat is invalid
                    '-1209' => '\\ccxt\\BadRequest', // The status of withdraw is invalid
                    '-2001' => '\\ccxt\\ExchangeNotAvailable', // Platform is yet to open trading
                    '-2002' => '\\ccxt\\OperationFailed', // The number of open orders exceeds the limit 300
                    '-2003' => '\\ccxt\\OperationFailed', // Position size cannot meet target leverage
                    '-2004' => '\\ccxt\\OperationFailed', // Adjust leverage fail
                    '-2005' => '\\ccxt\\RequestTimeout', // Adjust leverage timeout
                    '-2010' => '\\ccxt\\OperationRejected', // New order rejected
                    '-2011' => '\\ccxt\\OperationRejected', // Order cancellation rejected
                    '-2016' => '\\ccxt\\OperationRejected', // API key creation exceeds the limit
                    '-2017' => '\\ccxt\\OperationRejected', // Open orders exceeds the limit of the trading pair
                    '-2018' => '\\ccxt\\OperationRejected', // Trade user creation exceeds the limit
                    '-2019' => '\\ccxt\\PermissionDenied', // Trader and omnibus user not allowed to login app
                    '-2020' => '\\ccxt\\PermissionDenied', // Not allowed to trade this trading pair
                    '-2021' => '\\ccxt\\PermissionDenied', // Not allowed to trade this trading pair
                    '-2022' => '\\ccxt\\OperationRejected', // Order batch size exceeds the limit
                    '-2023' => '\\ccxt\\AuthenticationError', // Need to pass KYC verification
                    '-2024' => '\\ccxt\\AccountNotEnabled', // Fiat account does not exist
                    '-2025' => '\\ccxt\\AccountNotEnabled', // Custody account not exist
                    '-2026' => '\\ccxt\\BadRequest', // Invalid type
                    '-2027' => '\\ccxt\\OperationRejected', // Exceed maximum time range of 30 days
                    '-2028' => '\\ccxt\\OperationRejected', // The search is limited to data within the last one month
                    '-2029' => '\\ccxt\\OperationRejected', // The search is limited to data within the last three months
                    '-2030' => '\\ccxt\\InsufficientFunds', // Insufficient margin
                    '-2031' => '\\ccxt\\NotSupported', // Leverage reduction is not supported in Isolated Margin Mode with open positions
                    '-2032' => '\\ccxt\\OperationRejected', // After the transaction, your %s position will account for %s of the total position, which poses concentration risk. Do you want to continue with the transaction?
                    '-2033' => '\\ccxt\\OperationFailed', // Order creation failed. Please verify if the order parameters comply with the trading rules
                    '-2034' => '\\ccxt\\InsufficientFunds', // Trade account holding limit is zero
                    '-2035' => '\\ccxt\\OperationRejected', // The sub account has been frozen and cannot transfer
                    '-2036' => '\\ccxt\\NotSupported', // We do not support queries for records exceeding 30 days
                    '-2037' => '\\ccxt\\ExchangeError', // Position and order data error
                    '-2038' => '\\ccxt\\InsufficientFunds', // Insufficient margin
                    '-2039' => '\\ccxt\\NotSupported', // Leverage reduction is not supported in Isolated Margin Mode with open positions
                    '-2040' => '\\ccxt\\ExchangeNotAvailable', // There is a request being processed. Please try again later
                    '-2041' => '\\ccxt\\BadRequest', // Token does not exist
                    '-2042' => '\\ccxt\\OperationRejected', // You have passed the trade limit, please pay attention to the risks
                    '-2043' => '\\ccxt\\OperationRejected', // Maximum allowed leverage reached, please lower your leverage
                    '-2044' => '\\ccxt\\BadRequest', // This order price is unreasonable to exceed (or be lower than) the liquidation price
                    '-2045' => '\\ccxt\\BadRequest', // Price too low, please order again!
                    '-2046' => '\\ccxt\\BadRequest', // Price too high, please order again!
                    '-2048' => '\\ccxt\\BadRequest', // Exceed the maximum number of conditional orders of %s
                    '-2049' => '\\ccxt\\BadRequest', // Create stop order buy price too big
                    '-2050' => '\\ccxt\\BadRequest', // Create stop order sell price too small
                    '-2051' => '\\ccxt\\OperationRejected', // Create order rejected
                    '-2052' => '\\ccxt\\OperationRejected', // Create stop profit-loss plan order reject
                    '-2053' => '\\ccxt\\OperationRejected', // Position not enough
                    '-2054' => '\\ccxt\\BadRequest', // Invalid long stop profit price
                    '-2055' => '\\ccxt\\BadRequest', // Invalid long stop loss price
                    '-2056' => '\\ccxt\\BadRequest', // Invalid short stop profit price
                    '-2057' => '\\ccxt\\BadRequest', // Invalid short stop loss price
                    '-3117' => '\\ccxt\\PermissionDenied', // Invalid permission
                    '-3143' => '\\ccxt\\PermissionDenied', // According to KYC and risk assessment, your trading account has exceeded the limit.
                    '-3144' => '\\ccxt\\PermissionDenied', // Currently, your trading account has exceeded its limit and is temporarily unable to perform transfers
                    '-3145' => '\\ccxt\\DDoSProtection', // Please DO NOT submit request too frequently
                    '-4001' => '\\ccxt\\BadRequest', // Invalid asset
                    '-4002' => '\\ccxt\\BadRequest', // Withdrawal amount less than Minimum Withdrawal Amount
                    '-4003' => '\\ccxt\\InsufficientFunds', // Insufficient Balance
                    '-4004' => '\\ccxt\\BadRequest', // Invalid bank account number
                    '-4005' => '\\ccxt\\BadRequest', // Assets are not listed
                    '-4006' => '\\ccxt\\AccountNotEnabled', // KYC is not certified
                    '-4007' => '\\ccxt\\NotSupported', // Withdrawal channels are not supported
                    '-4008' => '\\ccxt\\AccountNotEnabled', // This currency does not support this customer type
                    '-4009' => '\\ccxt\\PermissionDenied', // No withdrawal permission
                    '-4010' => '\\ccxt\\PermissionDenied', // Withdrawals on the same day exceed the maximum limit for a single day
                    '-4011' => '\\ccxt\\ExchangeError', // System error
                    '-4012' => '\\ccxt\\ExchangeError', // Parameter error
                    '-4013' => '\\ccxt\\OperationFailed', // Withdraw repeatly
                ),
                'broad' => array(),
            ),
            'precisionMode' => TICK_SIZE,
        ));
    }

    public function fetch_time($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * fetches the current integer timestamp in milliseconds from the exchange server
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/check-server-time
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {int} the current integer timestamp in milliseconds from the exchange server
             */
            $response = Async\await($this->publicGetApiV1Time ($params));
            //
            //     {
            //         "serverTime" => 1721661553214
            //     }
            //
            return $this->safe_integer($response, 'serverTime');
        }) ();
    }

    public function fetch_status($params = array ()) {
        return Async\async(function () use ($params) {
            /**
             * the latest known information on the availability of the exchange API
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/test-connectivity
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=exchange-status-structure status structure~
             */
            $response = Async\await($this->publicGetApiV1Ping ($params));
            //
            // array()
            //
            return array(
                'status' => 'ok',
                'updated' => null,
                'eta' => null,
                'url' => null,
                'info' => $response,
            );
        }) ();
    }

    public function fetch_markets($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * retrieves data on all $markets for the exchange
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/exchangeinfo
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->symbol] the id of the market to fetch
             * @return {array[]} an array of objects representing market data
             */
            $request = array();
            $response = Async\await($this->publicGetApiV1ExchangeInfo ($this->extend($request, $params)));
            //
            //     {
            //         "timezone" => "UTC",
            //         "serverTime" => "1721661653952",
            //         "brokerFilters" => array(),
            //         "symbols" => array(
            //             {
            //                 "symbol" => "BTCUSDT",
            //                 "symbolName" => "BTCUSDT",
            //                 "status" => "TRADING",
            //                 "baseAsset" => "BTC",
            //                 "baseAssetName" => "BTC",
            //                 "baseAssetPrecision" => "0.00001",
            //                 "quoteAsset" => "USDT",
            //                 "quoteAssetName" => "USDT",
            //                 "quotePrecision" => "0.0000001",
            //                 "retailAllowed" => true,
            //                 "piAllowed" => true,
            //                 "corporateAllowed" => true,
            //                 "omnibusAllowed" => true,
            //                 "icebergAllowed" => false,
            //                 "isAggregate" => false,
            //                 "allowMargin" => false,
            //                 "filters" => array(
            //                     array(
            //                         "minPrice" => "0.01",
            //                         "maxPrice" => "100000.00000000",
            //                         "tickSize" => "0.01",
            //                         "filterType" => "PRICE_FILTER"
            //                     ),
            //                     array(
            //                         "minQty" => "0.00001",
            //                         "maxQty" => "8",
            //                         "stepSize" => "0.00001",
            //                         "marketOrderMinQty" => "0.00001",
            //                         "marketOrderMaxQty" => "4",
            //                         "filterType" => "LOT_SIZE"
            //                     ),
            //                     array(
            //                         "minNotional" => "1",
            //                         "filterType" => "MIN_NOTIONAL"
            //                     ),
            //                     array(
            //                         "minAmount" => "1",
            //                         "maxAmount" => "400000",
            //                         "minBuyPrice" => "0",
            //                         "marketOrderMinAmount" => "1",
            //                         "marketOrderMaxAmount" => "200000",
            //                         "filterType" => "TRADE_AMOUNT"
            //                     ),
            //                     array(
            //                         "maxSellPrice" => "0",
            //                         "buyPriceUpRate" => "0.1",
            //                         "sellPriceDownRate" => "0.1",
            //                         "filterType" => "LIMIT_TRADING"
            //                     ),
            //                     array(
            //                         "buyPriceUpRate" => "0.1",
            //                         "sellPriceDownRate" => "0.1",
            //                         "filterType" => "MARKET_TRADING"
            //                     ),
            //                     {
            //                         "noAllowMarketStartTime" => "1710485700000",
            //                         "noAllowMarketEndTime" => "1710486000000",
            //                         "limitOrderStartTime" => "0",
            //                         "limitOrderEndTime" => "0",
            //                         "limitMinPrice" => "0",
            //                         "limitMaxPrice" => "0",
            //                         "filterType" => "OPEN_QUOTE"
            //                     }
            //                 )
            //             }
            //         ),
            //         "options" => [ ],
            //         "contracts" => array(
            //             {
            //                 "filters" => array(
            //                     array(
            //                         "minPrice" => "0.1",
            //                         "maxPrice" => "100000.00000000",
            //                         "tickSize" => "0.1",
            //                         "filterType" => "PRICE_FILTER"
            //                     ),
            //                     array(
            //                         "minQty" => "0.001",
            //                         "maxQty" => "10",
            //                         "stepSize" => "0.001",
            //                         "marketOrderMinQty" => "0",
            //                         "marketOrderMaxQty" => "0",
            //                         "filterType" => "LOT_SIZE"
            //                     ),
            //                     array(
            //                         "minNotional" => "0",
            //                         "filterType" => "MIN_NOTIONAL"
            //                     ),
            //                     array(
            //                         "maxSellPrice" => "999999",
            //                         "buyPriceUpRate" => "0.05",
            //                         "sellPriceDownRate" => "0.05",
            //                         "maxEntrustNum" => 200,
            //                         "maxConditionNum" => 200,
            //                         "filterType" => "LIMIT_TRADING"
            //                     ),
            //                     array(
            //                         "buyPriceUpRate" => "0.05",
            //                         "sellPriceDownRate" => "0.05",
            //                         "filterType" => "MARKET_TRADING"
            //                     ),
            //                     {
            //                         "noAllowMarketStartTime" => "0",
            //                         "noAllowMarketEndTime" => "0",
            //                         "limitOrderStartTime" => "0",
            //                         "limitOrderEndTime" => "0",
            //                         "limitMinPrice" => "0",
            //                         "limitMaxPrice" => "0",
            //                         "filterType" => "OPEN_QUOTE"
            //                     }
            //                 ),
            //                 "exchangeId" => "301",
            //                 "symbol" => "BTCUSDT-PERPETUAL",
            //                 "symbolName" => "BTCUSDT-PERPETUAL",
            //                 "status" => "TRADING",
            //                 "baseAsset" => "BTCUSDT-PERPETUAL",
            //                 "baseAssetPrecision" => "0.001",
            //                 "quoteAsset" => "USDT",
            //                 "quoteAssetPrecision" => "0.1",
            //                 "icebergAllowed" => false,
            //                 "inverse" => false,
            //                 "index" => "USDT",
            //                 "marginToken" => "USDT",
            //                 "marginPrecision" => "0.0001",
            //                 "contractMultiplier" => "0.001",
            //                 "underlying" => "BTC",
            //                 "riskLimits" => array(
            //                     array(
            //                         "riskLimitId" => "200000722",
            //                         "quantity" => "1000.00",
            //                         "initialMargin" => "0.10",
            //                         "maintMargin" => "0.005",
            //                         "isWhite" => false
            //                     ),
            //                     {
            //                         "riskLimitId" => "200000723",
            //                         "quantity" => "2000.00",
            //                         "initialMargin" => "0.10",
            //                         "maintMargin" => "0.01",
            //                         "isWhite" => false
            //                     }
            //                 )
            //             }
            //         ),
            //         "coins" => array(
            //            {
            //                 "orgId" => "9001",
            //                 "coinId" => "BTC",
            //                 "coinName" => "BTC",
            //                 "coinFullName" => "Bitcoin",
            //                 "allowWithdraw" => true,
            //                 "allowDeposit" => true,
            //                 "tokenType" => "CHAIN_TOKEN",
            //                 "chainTypes" => array(
            //                     {
            //                         "chainType" => "Bitcoin",
            //                         "withdrawFee" => "0",
            //                         "minWithdrawQuantity" => "0.002",
            //                         "maxWithdrawQuantity" => "0",
            //                         "minDepositQuantity" => "0.0005",
            //                         "allowDeposit" => true,
            //                         "allowWithdraw" => true
            //                     }
            //                 )
            //             }
            //         )
            //     }
            //
            $spotMarkets = $this->safe_list($response, 'symbols', array());
            $swapMarkets = $this->safe_list($response, 'contracts', array());
            $markets = $this->array_concat($spotMarkets, $swapMarkets);
            if ($this->is_empty($markets)) {
                $markets = array( $response ); // if user provides $params->symbol the exchange returns a single object insted of list of objects
            }
            return $this->parse_markets($markets);
        }) ();
    }

    public function parse_market(array $market): array {
        // spot
        //     {
        //         "symbol" => "BTCUSDT",
        //         "symbolName" => "BTCUSDT",
        //         "status" => "TRADING",
        //         "baseAsset" => "BTC",
        //         "baseAssetName" => "BTC",
        //         "baseAssetPrecision" => "0.00001",
        //         "quoteAsset" => "USDT",
        //         "quoteAssetName" => "USDT",
        //         "quotePrecision" => "0.0000001",
        //         "retailAllowed" => true,
        //         "piAllowed" => true,
        //         "corporateAllowed" => true,
        //         "omnibusAllowed" => true,
        //         "icebergAllowed" => false,
        //         "isAggregate" => false,
        //         "allowMargin" => false,
        //         "filters" => array(
        //             array(
        //                 "minPrice" => "0.01",
        //                 "maxPrice" => "100000.00000000",
        //                 "tickSize" => "0.01",
        //                 "filterType" => "PRICE_FILTER"
        //             ),
        //             array(
        //                 "minQty" => "0.00001",
        //                 "maxQty" => "8",
        //                 "stepSize" => "0.00001",
        //                 "marketOrderMinQty" => "0.00001",
        //                 "marketOrderMaxQty" => "4",
        //                 "filterType" => "LOT_SIZE"
        //             ),
        //             array(
        //                 "minNotional" => "1",
        //                 "filterType" => "MIN_NOTIONAL"
        //             ),
        //             array(
        //                 "minAmount" => "1",
        //                 "maxAmount" => "400000",
        //                 "minBuyPrice" => "0",
        //                 "marketOrderMinAmount" => "1",
        //                 "marketOrderMaxAmount" => "200000",
        //                 "filterType" => "TRADE_AMOUNT"
        //             ),
        //             array(
        //                 "maxSellPrice" => "0",
        //                 "buyPriceUpRate" => "0.1",
        //                 "sellPriceDownRate" => "0.1",
        //                 "filterType" => "LIMIT_TRADING"
        //             ),
        //             array(
        //                 "buyPriceUpRate" => "0.1",
        //                 "sellPriceDownRate" => "0.1",
        //                 "filterType" => "MARKET_TRADING"
        //             ),
        //             {
        //                 "noAllowMarketStartTime" => "1710485700000",
        //                 "noAllowMarketEndTime" => "1710486000000",
        //                 "limitOrderStartTime" => "0",
        //                 "limitOrderEndTime" => "0",
        //                 "limitMinPrice" => "0",
        //                 "limitMaxPrice" => "0",
        //                 "filterType" => "OPEN_QUOTE"
        //             }
        //         )
        //     }
        //
        // swap
        //     {
        //         "filters" => array(
        //             array(
        //                 "minPrice" => "0.1",
        //                 "maxPrice" => "100000.00000000",
        //                 "tickSize" => "0.1",
        //                 "filterType" => "PRICE_FILTER"
        //             ),
        //             array(
        //                 "minQty" => "0.001",
        //                 "maxQty" => "10",
        //                 "stepSize" => "0.001",
        //                 "marketOrderMinQty" => "0",
        //                 "marketOrderMaxQty" => "0",
        //                 "filterType" => "LOT_SIZE"
        //             ),
        //             array(
        //                 "minNotional" => "0",
        //                 "filterType" => "MIN_NOTIONAL"
        //             ),
        //             array(
        //                 "maxSellPrice" => "999999",
        //                 "buyPriceUpRate" => "0.05",
        //                 "sellPriceDownRate" => "0.05",
        //                 "maxEntrustNum" => 200,
        //                 "maxConditionNum" => 200,
        //                 "filterType" => "LIMIT_TRADING"
        //             ),
        //             array(
        //                 "buyPriceUpRate" => "0.05",
        //                 "sellPriceDownRate" => "0.05",
        //                 "filterType" => "MARKET_TRADING"
        //             ),
        //             {
        //                 "noAllowMarketStartTime" => "0",
        //                 "noAllowMarketEndTime" => "0",
        //                 "limitOrderStartTime" => "0",
        //                 "limitOrderEndTime" => "0",
        //                 "limitMinPrice" => "0",
        //                 "limitMaxPrice" => "0",
        //                 "filterType" => "OPEN_QUOTE"
        //             }
        //         ),
        //         "exchangeId" => "301",
        //         "symbol" => "BTCUSDT-PERPETUAL",
        //         "symbolName" => "BTCUSDT-PERPETUAL",
        //         "status" => "TRADING",
        //         "baseAsset" => "BTCUSDT-PERPETUAL",
        //         "baseAssetPrecision" => "0.001",
        //         "quoteAsset" => "USDT",
        //         "quoteAssetPrecision" => "0.1",
        //         "icebergAllowed" => false,
        //         "inverse" => false,
        //         "index" => "USDT",
        //         "marginToken" => "USDT",
        //         "marginPrecision" => "0.0001",
        //         "contractMultiplier" => "0.001",
        //         "underlying" => "BTC",
        //         "riskLimits" => array(
        //             array(
        //                 "riskLimitId" => "200000722",
        //                 "quantity" => "1000.00",
        //                 "initialMargin" => "0.10",
        //                 "maintMargin" => "0.005",
        //                 "isWhite" => false
        //             ),
        //             {
        //                 "riskLimitId" => "200000723",
        //                 "quantity" => "2000.00",
        //                 "initialMargin" => "0.10",
        //                 "maintMargin" => "0.01",
        //                 "isWhite" => false
        //             }
        //         )
        //     }
        //
        $marketId = $this->safe_string($market, 'symbol');
        $quoteId = $this->safe_string($market, 'quoteAsset');
        $quote = $this->safe_currency_code($quoteId);
        $settleId = $this->safe_string($market, 'marginToken');
        $settle = $this->safe_currency_code($settleId);
        $baseId = $this->safe_string($market, 'baseAsset');
        $marketType = 'spot';
        $isSpot = true;
        $isSwap = false;
        $suffix = '';
        $parts = explode('-', $marketId);
        $secondPart = $this->safe_string($parts, 1);
        if ($secondPart === 'PERPETUAL') {
            $marketType = 'swap';
            $isSpot = false;
            $isSwap = true;
            $baseId = $this->safe_string($market, 'underlying');
            $suffix .= ':' . $settleId;
        }
        $base = $this->safe_currency_code($baseId);
        $symbol = $base . '/' . $quote . $suffix;
        $status = $this->safe_string($market, 'status');
        $active = $status === 'TRADING';
        $isLinear = null;
        $subType = null;
        $isInverse = $this->safe_bool($market, 'inverse');
        if ($isInverse !== null) {
            if ($isInverse) {
                $isLinear = false;
                $subType = 'inverse';
            } else {
                $isLinear = true;
                $subType = 'linear';
            }
        }
        $filtersList = $this->safe_list($market, 'filters', array());
        $filters = $this->index_by($filtersList, 'filterType');
        $priceFilter = $this->safe_dict($filters, 'PRICE_FILTER', array());
        $amountFilter = $this->safe_dict($filters, 'LOT_SIZE', array());
        $costFilter = $this->safe_dict($filters, 'MIN_NOTIONAL', array());
        $minCostString = $this->omit_zero($this->safe_string($costFilter, 'min_notional'));
        $contractSizeString = $this->safe_string($market, 'contractMultiplier');
        $amountPrecisionString = $this->safe_string($amountFilter, 'stepSize');
        $amountMinLimitString = $this->safe_string($amountFilter, 'minQty');
        $amountMaxLimitString = $this->safe_string($amountFilter, 'maxQty');
        $minLeverage = null;
        $maxLeverage = null;
        if ($isSwap) {
            $amountPrecisionString = Precise::string_div($amountPrecisionString, $contractSizeString);
            $amountMinLimitString = Precise::string_div($amountMinLimitString, $contractSizeString);
            $amountMaxLimitString = Precise::string_div($amountMaxLimitString, $contractSizeString);
            $riskLimits = $this->safe_list($market, 'riskLimits');
            if ($riskLimits !== null) {
                $first = $this->safe_dict($riskLimits, 0);
                $arrayLength = count($riskLimits);
                $last = $this->safe_dict($riskLimits, $arrayLength - 1);
                $minInitialMargin = $this->safe_string($first, 'initialMargin');
                $maxInitialMargin = $this->safe_string($last, 'initialMargin');
                if (Precise::string_gt($minInitialMargin, $maxInitialMargin)) {
                    list($minInitialMargin, $maxInitialMargin) = array( $maxInitialMargin, $minInitialMargin );
                }
                $minLeverage = $this->parse_to_int(Precise::string_div('1', $maxInitialMargin));
                $maxLeverage = $this->parse_to_int(Precise::string_div('1', $minInitialMargin));
            }
        }
        $tradingFees = $this->safe_dict($this->fees, 'trading');
        $fees = $isSpot ? $this->safe_dict($tradingFees, 'spot') : $this->safe_dict($tradingFees, 'swap');
        return $this->safe_market_structure(array(
            'id' => $marketId,
            'symbol' => $symbol,
            'base' => $base,
            'quote' => $quote,
            'baseId' => $baseId,
            'quoteId' => $quoteId,
            'active' => $active,
            'type' => $marketType,
            'subType' => $subType,
            'spot' => $isSpot,
            'margin' => $this->safe_bool($market, 'allowMargin'),
            'swap' => $isSwap,
            'future' => false,
            'option' => false,
            'contract' => $isSwap,
            'settle' => $settle,
            'settleId' => $settleId,
            'contractSize' => $this->parse_number($contractSizeString),
            'linear' => $isLinear,
            'inverse' => $isInverse,
            'taker' => $this->safe_number($fees, 'taker'),
            'maker' => $this->safe_number($fees, 'maker'),
            'percentage' => $this->safe_bool($fees, 'percentage'),
            'tierBased' => $this->safe_bool($fees, 'tierBased'),
            'feeSide' => $this->safe_string($fees, 'feeSide'),
            'expiry' => null,
            'expiryDatetime' => null,
            'strike' => null,
            'optionType' => null,
            'precision' => array(
                'amount' => $this->parse_number($amountPrecisionString),
                'price' => $this->safe_number($priceFilter, 'tickSize'),
            ),
            'limits' => array(
                'amount' => array(
                    'min' => $this->parse_number($amountMinLimitString),
                    'max' => $this->parse_number($amountMaxLimitString),
                ),
                'price' => array(
                    'min' => $this->safe_number($priceFilter, 'minPrice'),
                    'max' => $this->safe_number($priceFilter, 'maxPrice'),
                ),
                'leverage' => array(
                    'min' => $minLeverage,
                    'max' => $maxLeverage,
                ),
                'cost' => array(
                    'min' => $this->parse_number($minCostString),
                    'max' => null,
                ),
            ),
            'created' => null,
            'info' => $market,
        ));
    }

    public function fetch_currencies($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * fetches all available currencies on an exchange
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/exchangeinfo
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an associative dictionary of currencies
             */
            $response = Async\await($this->publicGetApiV1ExchangeInfo ($params));
            $coins = $this->safe_list($response, 'coins');
            //
            //     {
            //         ...
            //         "coins" => array(
            //             {
            //                 "orgId" => "9001",
            //                 "coinId" => "BTC",
            //                 "coinName" => "BTC",
            //                 "coinFullName" => "Bitcoin",
            //                 "allowWithdraw" => true,
            //                 "allowDeposit" => true,
            //                 "tokenType" => "CHAIN_TOKEN",
            //                 "chainTypes" => array(
            //                     {
            //                         "chainType" => "Bitcoin",
            //                         "withdrawFee" => "0",
            //                         "minWithdrawQuantity" => "0.002",
            //                         "maxWithdrawQuantity" => "0",
            //                         "minDepositQuantity" => "0.0005",
            //                         "allowDeposit" => true,
            //                         "allowWithdraw" => true
            //                     }
            //                 )
            //             }
            //         )
            //     }
            //
            $result = array();
            for ($i = 0; $i < count($coins); $i++) {
                $currecy = $coins[$i];
                $currencyId = $this->safe_string($currecy, 'coinId');
                $code = $this->safe_currency_code($currencyId);
                $networks = $this->safe_list($currecy, 'chainTypes');
                $parsedNetworks = array();
                for ($j = 0; $j < count($networks); $j++) {
                    $network = $networks[$j];
                    $networkId = $this->safe_string($network, 'chainType');
                    $networkCode = $this->network_code_to_id($networkId);
                    $parsedNetworks[$networkCode] = array(
                        'id' => $networkId,
                        'network' => $networkCode,
                        'limits' => array(
                            'withdraw' => array(
                                'min' => $this->safe_number($network, 'minWithdrawQuantity'),
                                'max' => $this->parse_number($this->omit_zero($this->safe_string($network, 'maxWithdrawQuantity'))),
                            ),
                            'deposit' => array(
                                'min' => $this->safe_number($network, 'minDepositQuantity'),
                                'max' => null,
                            ),
                        ),
                        'active' => null,
                        'deposit' => $this->safe_bool($network, 'allowDeposit'),
                        'withdraw' => $this->safe_bool($network, 'allowWithdraw'),
                        'fee' => $this->safe_number($network, 'withdrawFee'),
                        'precision' => null,
                        'info' => $network,
                    );
                }
                $rawType = $this->safe_string($currecy, 'tokenType');
                $type = ($rawType === 'REAL_MONEY') ? 'fiat' : 'crypto';
                $result[$code] = $this->safe_currency_structure(array(
                    'id' => $currencyId,
                    'code' => $code,
                    'precision' => null,
                    'type' => $type,
                    'name' => $this->safe_string($currecy, 'coinFullName'),
                    'active' => null,
                    'deposit' => $this->safe_bool($currecy, 'allowDeposit'),
                    'withdraw' => $this->safe_bool($currecy, 'allowWithdraw'),
                    'fee' => null,
                    'limits' => array(
                        'deposit' => array(
                            'min' => null,
                            'max' => null,
                        ),
                        'withdraw' => array(
                            'min' => null,
                            'max' => null,
                        ),
                    ),
                    'networks' => $parsedNetworks,
                    'info' => $currecy,
                ));
            }
            return $result;
        }) ();
    }

    public function fetch_order_book(string $symbol, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $limit, $params) {
            /**
             * fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-order-book
             *
             * @param {string} $symbol unified $symbol of the $market to fetch the order book for
             * @param {int} [$limit] the maximum amount of order book entries to return (maximum value is 200)
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} A dictionary of ~@link https://docs.ccxt.com/#/?id=order-book-structure order book structures~ indexed by $market symbols
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = Async\await($this->publicGetQuoteV1Depth ($this->extend($request, $params)));
            //
            //     {
            //         "t" => 1721681436393,
            //         "b" => [
            //             ["67902.49", "0.00112"],
            //             ["67901.08", "0.01014"]
            //             ...
            //         ],
            //         "a" => [
            //             ["67905.99", "0.87134"],
            //             ["67906", "0.57361"]
            //             ...
            //         ]
            //     }
            //
            $timestamp = $this->safe_integer($response, 't');
            return $this->parse_order_book($response, $symbol, $timestamp, 'b', 'a');
        }) ();
    }

    public function fetch_trades(string $symbol, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * get the list of most recent trades for a particular $symbol
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-recent-trade-list
             *
             * @param {string} $symbol unified $symbol of the $market to fetch trades for
             * @param {int} [$since] timestamp in ms of the earliest trade to fetch
             * @param {int} [$limit] the maximum amount of trades to fetch (maximum value is 100)
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=public-trades trade structures~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = Async\await($this->publicGetQuoteV1Trades ($this->extend($request, $params)));
            //
            //     array(
            //         array(
            //             "t" => 1721682745779,
            //             "p" => "67835.99",
            //             "q" => "0.00017",
            //             "ibm" => true
            //         ),
            //         ...
            //     )
            //
            return $this->parse_trades($response, $market, $since, $limit);
        }) ();
    }

    public function fetch_my_trades(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetch all trades made by the user
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-account-trade-list
             * @see https://hashkeyglobal-apidoc.readme.io/reference/query-futures-trades
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-sub-account-user
             *
             * @param {string} $symbol *is mandatory for swap markets* unified $market $symbol
             * @param {int} [$since] the earliest time in ms to fetch trades for
             * @param {int} [$limit] the maximum amount of trades to fetch (default 200, max 500)
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->type] 'spot' or 'swap' - the type of the $market to fetch trades for (default 'spot')
             * @param {int} [$params->until] the latest time in ms to fetch trades for, only supports the last 30 days timeframe
             * @param {string} [$params->fromId] srarting trade id
             * @param {string} [$params->toId] ending trade id
             * @param {string} [$params->clientOrderId] *spot markets only* filter trades by orderId
             * @param {string} [$params->accountId] account id to fetch the orders from
             * @return {Trade[]} a list of {@link https://github.com/ccxt/ccxt/wiki/Manual#trade-structure trade structures}
             */
            $methodName = 'fetchMyTrades';
            Async\await($this->load_markets());
            $request = array();
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            $marketType = 'spot';
            list($marketType, $params) = $this->handle_market_type_and_params($methodName, $market, $params);
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $until = null;
            list($until, $params) = $this->handle_option_and_params($params, $methodName, 'until');
            if ($until !== null) {
                $request['endTime'] = $until;
            }
            $accountId = null;
            list($accountId, $params) = $this->handle_option_and_params($params, $methodName, 'accountId');
            $response = null;
            if ($marketType === 'spot') {
                if ($market !== null) {
                    $request['symbol'] = $market['id'];
                }
                if ($accountId !== null) {
                    $request['accountId'] = $accountId;
                }
                $response = Async\await($this->privateGetApiV1AccountTrades ($this->extend($request, $params)));
                //
                //     array(
                //         array(
                //             "id" => "1739352552862964736",
                //             "clientOrderId" => "1722082982086472",
                //             "ticketId" => "1739352552795029504",
                //             "symbol" => "ETHUSDT",
                //             "symbolName" => "ETHUSDT",
                //             "orderId" => "1739352552762301440",
                //             "matchOrderId" => "0",
                //             "price" => "3289.96",
                //             "qty" => "0.001",
                //             "commission" => "0.0000012",
                //             "commissionAsset" => "ETH",
                //             "time" => "1722082982097",
                //             "isBuyer" => true,
                //             "isMaker" => false,
                //             "fee" => array(
                //                 "feeCoinId" => "ETH",
                //                 "feeCoinName" => "ETH",
                //                 "fee" => "0.0000012"
                //             ),
                //             "feeCoinId" => "ETH",
                //             "feeAmount" => "0.0000012",
                //             "makerRebate" => "0"
                //         ),
                //         ...
                //     )
                //
            } elseif ($marketType === 'swap') {
                if ($symbol === null) {
                    throw new ArgumentsRequired($this->id . ' ' . $methodName . '() requires a $symbol argument for swap markets');
                }
                $request['symbol'] = $market['id'];
                if ($accountId !== null) {
                    $request['subAccountId'] = $accountId;
                    $response = Async\await($this->privateGetApiV1FuturesSubAccountUserTrades ($this->extend($request, $params)));
                } else {
                    $response = Async\await($this->privateGetApiV1FuturesUserTrades ($this->extend($request, $params)));
                    //
                    //     array(
                    //         {
                    //             "time" => "1722429951648",
                    //             "tradeId" => "1742263144691139328",
                    //             "orderId" => "1742263144028363776",
                    //             "symbol" => "ETHUSDT-PERPETUAL",
                    //             "price" => "3327.54",
                    //             "quantity" => "4",
                    //             "commissionAsset" => "USDT",
                    //             "commission" => "0.00798609",
                    //             "makerRebate" => "0",
                    //             "type" => "LIMIT",
                    //             "side" => "BUY_OPEN",
                    //             "realizedPnl" => "0",
                    //             "isMarker" => false
                    //         }
                    //     )
                    //
                }
            } else {
                throw new NotSupported($this->id . ' ' . $methodName . '() is not supported for ' . $marketType . ' type of markets');
            }
            return $this->parse_trades($response, $market, $since, $limit);
        }) ();
    }

    public function parse_trade(array $trade, ?array $market = null): array {
        //
        // fetchTrades
        //
        //     {
        //         "t" => 1721682745779,
        //         "p" => "67835.99",
        //         "q" => "0.00017",
        //         "ibm" => true
        //     }
        //
        // fetchMyTrades spot
        //
        //     {
        //         "id" => "1739352552862964736",
        //         "clientOrderId" => "1722082982086472",
        //         "ticketId" => "1739352552795029504",
        //         "symbol" => "ETHUSDT",
        //         "symbolName" => "ETHUSDT",
        //         "orderId" => "1739352552762301440",
        //         "matchOrderId" => "0",
        //         "price" => "3289.96",
        //         "qty" => "0.001",
        //         "commission" => "0.0000012",
        //         "commissionAsset" => "ETH",
        //         "time" => "1722082982097",
        //         "isBuyer" => true,
        //         "isMaker" => false,
        //         "fee" => array(
        //             "feeCoinId" => "ETH",
        //             "feeCoinName" => "ETH",
        //             "fee" => "0.0000012"
        //         ),
        //         "feeCoinId" => "ETH",
        //         "feeAmount" => "0.0000012",
        //         "makerRebate" => "0"
        //     }
        //
        // fetchMyTrades swap
        //     {
        //         "time" => "1722429951648",
        //         "tradeId" => "1742263144691139328",
        //         "orderId" => "1742263144028363776",
        //         "symbol" => "ETHUSDT-PERPETUAL",
        //         "price" => "3327.54",
        //         "quantity" => "4",
        //         "commissionAsset" => "USDT",
        //         "commission" => "0.00798609",
        //         "makerRebate" => "0",
        //         "type" => "LIMIT",
        //         "side" => "BUY_OPEN",
        //         "realizedPnl" => "0",
        //         "isMarker" => false
        //     }
        $timestamp = $this->safe_integer_2($trade, 't', 'time');
        $marketId = $this->safe_string($trade, 'symbol');
        $market = $this->safe_market($marketId, $market);
        $side = $this->safe_string_lower($trade, 'side'); // swap trades have $side param
        if ($side !== null) {
            $side = $this->safe_string(explode('_', $side), 0);
        }
        $isBuyer = $this->safe_bool($trade, 'isBuyer');
        if ($isBuyer !== null) {
            $side = $isBuyer ? 'buy' : 'sell';
        }
        $takerOrMaker = null;
        $isMaker = $this->safe_bool_n($trade, array( 'isMaker', 'isMarker' ));
        if ($isMaker !== null) {
            $takerOrMaker = $isMaker ? 'maker' : 'taker';
        }
        $isBuyerMaker = $this->safe_bool($trade, 'ibm');
        // if public $trade
        if ($isBuyerMaker !== null) {
            $takerOrMaker = 'taker';
            $side = $isBuyerMaker ? 'sell' : 'buy';
        }
        $feeCost = $this->safe_string($trade, 'commission');
        $feeCurrncyId = $this->safe_string($trade, 'commissionAsset');
        $feeInfo = $this->safe_dict($trade, 'fee');
        $fee = null;
        if ($feeInfo !== null) {
            $feeCost = $this->safe_string($feeInfo, 'fee');
            $feeCurrncyId = $this->safe_string($feeInfo, 'feeCoinId');
        }
        if ($feeCost !== null) {
            $fee = array(
                'cost' => $this->parse_number($feeCost),
                'currency' => $this->safe_currency_code($feeCurrncyId),
            );
        }
        return $this->safe_trade(array(
            'id' => $this->safe_string_2($trade, 'id', 'tradeId'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'symbol' => $market['symbol'],
            'side' => $side,
            'price' => $this->safe_string_2($trade, 'p', 'price'),
            'amount' => $this->safe_string_n($trade, array( 'q', 'qty', 'quantity' )),
            'cost' => null,
            'takerOrMaker' => $takerOrMaker,
            'type' => null,
            'order' => $this->safe_string($trade, 'orderId'),
            'fee' => $fee,
            'info' => $trade,
        ), $market);
    }

    public function fetch_ohlcv(string $symbol, $timeframe = '1m', ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $timeframe, $since, $limit, $params) {
            /**
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-kline
             *
             * fetches historical candlestick data containing the open, high, low, and close price, and the volume of a $market
             * @param {string} $symbol unified $symbol of the $market to fetch OHLCV data for
             * @param {string} $timeframe the length of time each candle represents
             * @param {int} [$since] timestamp in ms of the earliest candle to fetch
             * @param {int} [$limit] the maximum amount of candles to fetch
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {int} [$params->until] timestamp in ms of the latest candle to fetch
             * @param {boolean} [$params->paginate] default false, when true will automatically $paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-$params)
             * @return {int[][]} A list of candles ordered, open, high, low, close, volume
             */
            $methodName = 'fetchOHLCV';
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, $methodName, 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_deterministic('fetchOHLCV', $symbol, $since, $limit, $timeframe, $params, 1000));
            }
            $market = $this->market($symbol);
            $timeframe = $this->safe_string($this->timeframes, $timeframe, $timeframe);
            $request = array(
                'symbol' => $market['id'],
                'interval' => $timeframe,
            );
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $until = null;
            list($until, $params) = $this->handle_option_and_params($params, $methodName, 'until');
            if ($until !== null) {
                $request['endTime'] = $until;
            }
            $response = Async\await($this->publicGetQuoteV1Klines ($this->extend($request, $params)));
            //
            //     array(
            //         array(
            //             1721684280000,
            //             "67832.49",
            //             "67862.5",
            //             "67832.49",
            //             "67861.44",
            //             "0.01122",0,
            //             "761.2763533",68,
            //             "0.00561",
            //             "380.640643"
            //         ),
            //         ...
            //     )
            //
            return $this->parse_ohlcvs($response, $market, $timeframe, $since, $limit);
        }) ();
    }

    public function parse_ohlcv($ohlcv, ?array $market = null): array {
        //
        //     array(
        //         1721684280000,
        //         "67832.49",
        //         "67862.5",
        //         "67832.49",
        //         "67861.44",
        //         "0.01122",0,
        //         "761.2763533",68,
        //         "0.00561",
        //         "380.640643"
        //     )
        //
        return array(
            $this->safe_integer($ohlcv, 0),
            $this->safe_number($ohlcv, 1),
            $this->safe_number($ohlcv, 2),
            $this->safe_number($ohlcv, 3),
            $this->safe_number($ohlcv, 4),
            $this->safe_number($ohlcv, 5),
        );
    }

    public function fetch_ticker(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetches a price $ticker, a statistical calculation with the information calculated over the past 24 hours for a specific $market
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-24hr-$ticker-price-change
             *
             * @param {string} $symbol unified $symbol of the $market to fetch the $ticker for
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=$ticker-structure $ticker structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $response = Async\await($this->publicGetQuoteV1Ticker24hr ($this->extend($request, $params)));
            //
            //     array(
            //         {
            //             "t" => 1721685896846,
            //             "s" => "BTCUSDT-PERPETUAL",
            //             "c" => "67756.7",
            //             "h" => "68479.9",
            //             "l" => "66594.3",
            //             "o" => "68279.7",
            //             "b" => "67756.6",
            //             "a" => "67756.7",
            //             "v" => "1604722",
            //             "qv" => "108827258.7761"
            //         }
            //     )
            //
            $ticker = $this->safe_dict($response, 0, array());
            return $this->parse_ticker($ticker, $market);
        }) ();
    }

    public function fetch_tickers(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-24hr-ticker-price-change
             *
             * @param {string[]} [$symbols] unified $symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structures~
             */
            Async\await($this->load_markets());
            $symbols = $this->market_symbols($symbols);
            $response = Async\await($this->publicGetQuoteV1Ticker24hr ($params));
            return $this->parse_tickers($response, $symbols);
        }) ();
    }

    public function parse_ticker($ticker, ?array $market = null): array {
        //
        //     {
        //         "t" => 1721685896846,
        //         "s" => "BTCUSDT-PERPETUAL",
        //         "c" => "67756.7",
        //         "h" => "68479.9",
        //         "l" => "66594.3",
        //         "o" => "68279.7",
        //         "b" => "67756.6",
        //         "a" => "67756.7",
        //         "v" => "1604722",
        //         "qv" => "108827258.7761"
        //     }
        //
        $timestamp = $this->safe_integer($ticker, 't');
        $marketId = $this->safe_string($ticker, 's');
        $market = $this->safe_market($marketId, $market);
        $symbol = $market['symbol'];
        $last = $this->safe_string($ticker, 'c');
        return $this->safe_ticker(array(
            'symbol' => $symbol,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'high' => $this->safe_string($ticker, 'h'),
            'low' => $this->safe_string($ticker, 'l'),
            'bid' => $this->safe_string($ticker, 'b'),
            'bidVolume' => null,
            'ask' => $this->safe_string($ticker, 'a'),
            'askVolume' => null,
            'vwap' => null,
            'open' => $this->safe_string($ticker, 'o'),
            'close' => $last,
            'last' => $last,
            'previousClose' => null,
            'change' => null,
            'percentage' => null,
            'average' => null,
            'baseVolume' => $this->safe_string($ticker, 'v'),
            'quoteVolume' => $this->safe_string($ticker, 'qv'),
            'info' => $ticker,
        ), $market);
    }

    public function fetch_last_prices(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * fetches the last price for multiple markets
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-symbol-price-ticker
             *
             * @param {string[]} [$symbols] unified $symbols of the markets to fetch the last prices
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->symbol] the id of the market to fetch last price for
             * @return {array} a dictionary of lastprices structures
             */
            Async\await($this->load_markets());
            $symbols = $this->market_symbols($symbols);
            $request = array();
            $response = Async\await($this->publicGetQuoteV1TickerPrice ($this->extend($request, $params)));
            //
            //     array(
            //         array(
            //             "s" => "BTCUSDT-PERPETUAL",
            //             "p" => "64871"
            //         ),
            //         ...
            //     )
            //
            return $this->parse_last_prices($response, $symbols);
        }) ();
    }

    public function parse_last_price($entry, ?array $market = null): array {
        $marketId = $this->safe_string($entry, 's');
        $market = $this->safe_market($marketId, $market);
        return array(
            'symbol' => $market['symbol'],
            'timestamp' => null,
            'datetime' => null,
            'price' => $this->safe_number($entry, 'p'),
            'side' => null,
            'info' => $entry,
        );
    }

    public function fetch_balance($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * query for $balance and get the amount of funds available for trading or funds locked in orders
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-account-information
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->accountId] account ID, for Master Key only
             * @param {string} [$params->type] 'spot' or 'swap' - the type of the market to fetch $balance for (default 'spot')
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=$balance-structure $balance structure~
             */
            Async\await($this->load_markets());
            $request = array();
            $methodName = 'fetchBalance';
            $marketType = 'spot';
            list($marketType, $params) = $this->handle_market_type_and_params($methodName, null, $params, $marketType);
            if ($marketType === 'swap') {
                $response = Async\await($this->privateGetApiV1FuturesBalance ($params));
                //
                //     array(
                //         {
                //             "balance" => "30.63364672",
                //             "availableBalance" => "28.85635534",
                //             "positionMargin" => "4.3421",
                //             "orderMargin" => "0",
                //             "asset" => "USDT",
                //             "crossUnRealizedPnl" => "2.5649"
                //         }
                //     )
                //
                $balance = $this->safe_dict($response, 0, array());
                return $this->parse_swap_balance($balance);
            } elseif ($marketType === 'spot') {
                $response = Async\await($this->privateGetApiV1Account ($this->extend($request, $params)));
                //
                //     {
                //         "balances" => array(
                //             array(
                //                 "asset":"USDT",
                //                 "assetId":"USDT",
                //                 "assetName":"USDT",
                //                 "total":"40",
                //                 "free":"40",
                //                 "locked":"0"
                //             ),
                //             ...
                //         ),
                //         "userId" => "1732885739572845312"
                //     }
                //
                return $this->parse_balance($response);
            } else {
                throw new NotSupported($this->id . ' ' . $methodName . '() is not supported for ' . $marketType . ' type of markets');
            }
        }) ();
    }

    public function parse_balance($balance): array {
        //
        //     {
        //         "balances" => array(
        //             array(
        //                 "asset":"USDT",
        //                 "assetId":"USDT",
        //                 "assetName":"USDT",
        //                 "total":"40",
        //                 "free":"40",
        //                 "locked":"0"
        //             ),
        //             ...
        //         ),
        //         "userId" => "1732885739572845312"
        //     }
        //
        $result = array(
            'info' => $balance,
        );
        $balances = $this->safe_list($balance, 'balances', array());
        for ($i = 0; $i < count($balances); $i++) {
            $balanceEntry = $balances[$i];
            $currencyId = $this->safe_string($balanceEntry, 'asset');
            $code = $this->safe_currency_code($currencyId);
            $account = $this->account();
            $account['total'] = $this->safe_string($balanceEntry, 'total');
            $account['free'] = $this->safe_string($balanceEntry, 'free');
            $account['used'] = $this->safe_string($balanceEntry, 'locked');
            $result[$code] = $account;
        }
        return $this->safe_balance($result);
    }

    public function parse_swap_balance($balance): array {
        //
        //     {
        //         "balance" => "30.63364672",
        //         "availableBalance" => "28.85635534",
        //         "positionMargin" => "4.3421",
        //         "orderMargin" => "0",
        //         "asset" => "USDT",
        //         "crossUnRealizedPnl" => "2.5649"
        //     }
        //
        $currencyId = $this->safe_string($balance, 'asset');
        $code = $this->safe_currency_code($currencyId);
        $account = $this->account();
        $account['total'] = $this->safe_string($balance, 'balance');
        $positionMargin = $this->safe_string($balance, 'positionMargin');
        $orderMargin = $this->safe_string($balance, 'orderMargin');
        $account['used'] = Precise::string_add($positionMargin, $orderMargin);
        $result = array(
            'info' => $balance,
        );
        $result[$code] = $account;
        return $this->safe_balance($result);
    }

    public function fetch_deposit_address(string $code, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $params) {
            /**
             * fetch the deposit address for a $currency associated with this account
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-deposit-address
             *
             * @param {string} $code unified $currency $code (default is 'USDT')
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->network] network for fetch deposit address (default is 'ETH')
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=address-structure address structure~
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $request = array(
                'coin' => $currency['id'],
            );
            $networkCode = null;
            list($networkCode, $params) = $this->handle_network_code_and_params($params);
            if ($networkCode === null) {
                $networkCode = $this->default_network_code($code);
            }
            $request['chainType'] = $this->network_code_to_id($networkCode, $code);
            $response = Async\await($this->privateGetApiV1AccountDepositAddress ($this->extend($request, $params)));
            //
            //     {
            //         "canDeposit" => true,
            //         "address" => "0x61AAd7F763e2C7fF1CC996918740F67f9dC8BF4e",
            //         "addressExt" => "",
            //         "minQuantity" => "1",
            //         "needAddressTag" => false,
            //         "requiredConfirmTimes" => 64,
            //         "canWithdrawConfirmTimes" => 64,
            //         "coinType" => "ERC20_TOKEN"
            //     }
            //
            $depositAddress = $this->parse_deposit_address($response, $currency);
            $depositAddress['network'] = $networkCode;
            return $depositAddress;
        }) ();
    }

    public function parse_deposit_address($depositAddress, ?array $currency = null): array {
        //
        //     {
        //         "canDeposit" => true,
        //         "address" => "0x61AAd7F763e2C7fF1CC996918740F67f9dC8BF4e",
        //         "addressExt" => "",
        //         "minQuantity" => "1",
        //         "needAddressTag" => false,
        //         "requiredConfirmTimes" => 64,
        //         "canWithdrawConfirmTimes" => 64,
        //         "coinType" => "ERC20_TOKEN"
        //     }
        //
        $address = $this->safe_string($depositAddress, 'address');
        $this->check_address($address);
        $tag = $this->safe_string($depositAddress, 'addressExt');
        if ($tag === '') {
            $tag = null;
        }
        return array(
            'info' => $depositAddress,
            'currency' => $currency['code'],
            'network' => null,
            'address' => $address,
            'tag' => $tag,
        );
    }

    public function fetch_deposits(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch all deposits made to an account
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-deposit-history
             *
             * @param {string} $code unified $currency $code of the $currency transferred
             * @param {int} [$since] the earliest time in ms to fetch transfers for (default 24 hours ago)
             * @param {int} [$limit] the maximum number of transfer structures to retrieve (default 50, max 200)
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {int} [$params->until] the latest time in ms to fetch transfers for (default time now)
             * @param {int} [$params->fromId] starting ID (To be released)
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transfer-structure transfer structures~
             */
            $methodName = 'fetchDeposits';
            Async\await($this->load_markets());
            $request = array();
            $currency = null;
            if ($code !== null) {
                $currency = $this->currency($code);
                $request['coin'] = $currency['id'];
            }
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $until = null;
            list($until, $params) = $this->handle_option_and_params($params, $methodName, 'until');
            if ($until !== null) {
                $request['endTime'] = $until;
            }
            $response = Async\await($this->privateGetApiV1AccountDepositOrders ($this->extend($request, $params)));
            //
            //     array(
            //         {
            //             "time" => "1721641082163",
            //             "coin" => "TRXUSDT",
            //             "coinName" => "TRXUSDT",
            //             "address" => "TBA6CypYJizwA9XdC7Ubgc5F1bxrQ7SqPt",
            //             "quantity" => "86.00000000000000000000",
            //             "status" => 4,
            //             "statusCode" => "4",
            //             "txId" => "0970c14da4d7412295fa7b21c03a08da319e746a0d59ef14462a74183d118da4"
            //         }
            //     )
            //
            return $this->parse_transactions($response, $currency, $since, $limit, array( 'type' => 'deposit' ));
        }) ();
    }

    public function fetch_withdrawals(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch all withdrawals made from an account
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/withdrawal-records
             *
             * @param {string} $code unified $currency $code of the $currency transferred
             * @param {int} [$since] the earliest time in ms to fetch transfers for (default 24 hours ago)
             * @param {int} [$limit] the maximum number of transfer structures to retrieve (default 50, max 200)
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {int} [$params->until] the latest time in ms to fetch transfers for (default time now)
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structures~
             */
            $methodName = 'fetchWithdrawals';
            Async\await($this->load_markets());
            $request = array();
            $currency = null;
            if ($code !== null) {
                $currency = $this->currency($code);
                $request['coin'] = $currency['id'];
            }
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $until = null;
            list($until, $params) = $this->handle_option_and_params($params, $methodName, 'until');
            if ($until !== null) {
                $request['endTime'] = $until;
            }
            $response = Async\await($this->privateGetApiV1AccountWithdrawOrders ($this->extend($request, $params)));
            //
            //     array(
            //         {
            //             "time" => "1723545505366",
            //             "id" => "W611267400947572736",
            //             "coin" => "USDT",
            //             "coinId" => "USDT",
            //             "coinName" => "USDT",
            //             "address" => "TQbkBMnWnJNGTAUpFS4kvv4NRLzUAnGAes",
            //             "quantity" => "2.00000000",
            //             "arriveQuantity" => "2.00000000",
            //             "txId" => "f83f94e7d2e81fbec98c66c25d6615872cc2d426145629b6cf22e5e0a0753715",
            //             "addressUrl" => "TQbkBMnWnJNGTAUpFS4kvv4NRLzUAnGAes",
            //             "feeCoinId" => "USDT",
            //             "feeCoinName" => "USDT",
            //             "fee" => "1.00000000",
            //             "remark" => "",
            //             "platform" => ""
            //         }
            //     )
            //
            return $this->parse_transactions($response, $currency, $since, $limit, array( 'type' => 'withdrawal' ));
        }) ();
    }

    public function withdraw(string $code, float $amount, string $address, ?string $tag = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $amount, $address, $tag, $params) {
            /**
             * make a withdrawal
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/withdraw
             *
             * @param {string} $code unified $currency $code
             * @param {float} $amount the $amount to withdraw
             * @param {string} $address the $address to withdraw to
             * @param {string} $tag
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->network] network for withdraw
             * @param {string} [$params->clientOrderId] client order id
             * @param {string} [$params->platform] the platform to withdraw to (hashkey, HashKey HK)
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structure~
             */
            list($tag, $params) = $this->handle_withdraw_tag_and_params($tag, $params);
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $request = array(
                'coin' => $currency['id'],
                'address' => $address,
                'quantity' => $amount,
            );
            if ($tag !== null) {
                $request['addressExt'] = $tag;
            }
            $networkCode = null;
            list($networkCode, $params) = $this->handle_network_code_and_params($params);
            if ($networkCode !== null) {
                $request['chainType'] = $this->network_code_to_id($networkCode);
            }
            $response = Async\await($this->privatePostApiV1AccountWithdraw ($this->extend($request, $params)));
            //
            //     {
            //         "success" => true,
            //         "id" => "0",
            //         "orderId" => "W611267400947572736",
            //         "accountId" => "1732885739589466115"
            //     }
            //
            return $this->parse_transaction($response, $currency);
        }) ();
    }

    public function parse_transaction($transaction, ?array $currency = null): array {
        //
        //  fetchDeposits
        //     {
        //         "time" => "1721641082163",
        //         "coin" => "TRXUSDT", // todo how to parse it?
        //         "coinName" => "TRXUSDT",
        //         "address" => "TBA6CypYJizwA9XdC7Ubgc5F1bxrQ7SqPt",
        //         "quantity" => "86.00000000000000000000",
        //         "status" => 4,
        //         "statusCode" => "4",
        //         "txId" => "0970c14da4d7412295fa7b21c03a08da319e746a0d59ef14462a74183d118da4"
        //     }
        //
        // fetchWithdrawals
        //     {
        //         "time" => "1723545505366",
        //         "id" => "W611267400947572736",
        //         "coin" => "USDT",
        //         "coinId" => "USDT",
        //         "coinName" => "USDT",
        //         "address" => "TQbkBMnWnJNGTAUpFS4kvv4NRLzUAnGAes",
        //         "quantity" => "2.00000000",
        //         "arriveQuantity" => "2.00000000",
        //         "txId" => "f83f94e7d2e81fbec98c66c25d6615872cc2d426145629b6cf22e5e0a0753715",
        //         "addressUrl" => "TQbkBMnWnJNGTAUpFS4kvv4NRLzUAnGAes",
        //         "feeCoinId" => "USDT",
        //         "feeCoinName" => "USDT",
        //         "fee" => "1.00000000",
        //         "remark" => "",
        //         "platform" => ""
        //     }
        //
        // withdraw
        //     {
        //         "success" => true,
        //         "id" => "0",
        //         "orderId" => "W611267400947572736",
        //         "accountId" => "1732885739589466115"
        //     }
        //
        $id = $this->safe_string_2($transaction, 'id', 'orderId');
        $address = $this->safe_string($transaction, 'address');
        $status = $this->safe_string($transaction, 'status'); // for fetchDeposits
        if ($status === null) {
            $success = $this->safe_bool($transaction, 'success', false); // for withdraw
            if ($success) {
                $status = 'ok';
            } else {
                $addressUrl = $this->safe_string($transaction, 'addressUrl'); // for fetchWithdrawals
                if ($addressUrl !== null) {
                    $status = 'ok';
                }
            }
        }
        $txid = $this->safe_string($transaction, 'txId');
        $coin = $this->safe_string($transaction, 'coin');
        $code = $this->safe_currency_code($coin, $currency);
        $timestamp = $this->safe_integer($transaction, 'time');
        $amount = $this->safe_number($transaction, 'quantity');
        $feeCost = $this->safe_number($transaction, 'fee');
        $fee = null;
        if ($feeCost !== null) {
            $fee = array(
                'cost' => $feeCost,
                'currency' => $code,
            );
        }
        return array(
            'info' => $transaction,
            'id' => $id,
            'txid' => $txid,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'network' => null,
            'address' => $address,
            'addressTo' => null,
            'addressFrom' => null,
            'tag' => null,
            'tagTo' => null,
            'tagFrom' => null,
            'type' => null,
            'amount' => $amount,
            'currency' => $code,
            'status' => $this->parse_transaction_status($status),
            'updated' => null,
            'internal' => null,
            'comment' => null,
            'fee' => $fee,
        );
    }

    public function parse_transaction_status($status) {
        $statuses = array(
            '1' => 'pending',
            '2' => 'pending',
            '3' => 'failed',
            '4' => 'ok',
            '5' => 'pending',
            '6' => 'ok',
            '7' => 'failed',
            '8' => 'cancelled',
            '9' => 'failed',
            '10' => 'failed',
            'successful' => 'ok',
            'success' => 'ok',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function transfer(string $code, float $amount, string $fromAccount, string $toAccount, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $amount, $fromAccount, $toAccount, $params) {
            /**
             * transfer $currency internally between wallets on the same account
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/new-account-transfer
             *
             * @param {string} $code unified $currency $code
             * @param {float} $amount amount to transfer
             * @param {string} $fromAccount account id to transfer from
             * @param {string} $toAccount account id to transfer to
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->clientOrderId] a unique id for the transfer
             * @param {string} [$params->remark] a note for the transfer
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=transfer-structure transfer structure~
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $request = array(
                'coin' => $currency['id'],
                'quantity' => $this->currency_to_precision($code, $amount),
                'fromAccountId' => $fromAccount,
                'toAccountId' => $toAccount,
            );
            $response = Async\await($this->privatePostApiV1AccountAssetTransfer ($this->extend($request, $params)));
            //
            //     {
            //         "success" => true,
            //         "timestamp" => 1722260230773,
            //         "clientOrderId" => "",
            //         "orderId" => "1740839420695806720"
            //     }
            //
            return $this->parse_transfer($response, $currency);
        }) ();
    }

    public function parse_transfer($transfer, ?array $currency = null) {
        $timestamp = $this->safe_integer($transfer, 'timestamp');
        $currencyId = $this->safe_string($currency, 'id');
        $status = null;
        $success = $this->safe_bool($transfer, 'success', false);
        if ($success) {
            $status = 'ok';
        }
        return array(
            'id' => $this->safe_string($transfer, 'orderId'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'currency' => $this->safe_currency_code($currencyId, $currency),
            'amount' => null,
            'fromAccount' => null,
            'toAccount' => null,
            'status' => $status,
            'info' => $transfer,
        );
    }

    public function fetch_accounts($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * fetch all the accounts associated with a profile
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/query-sub-account
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=account-structure account structures~ indexed by the account type
             */
            Async\await($this->load_markets());
            $response = Async\await($this->privateGetApiV1AccountType ($params));
            //
            //     array(
            //         array(
            //             "accountId" => "1732885739589466112",
            //             "accountLabel" => "Main Trading Account",
            //             "accountType" => 1,
            //             "accountIndex" => 0
            //         ),
            //         ...
            //     )
            //
            return $this->parse_accounts($response, $params);
        }) ();
    }

    public function parse_account($account) {
        $accountLabel = $this->safe_string($account, 'accountLabel');
        $label = '';
        if ($accountLabel === 'Main Trading Account' || $accountLabel === 'Main Future Account') {
            $label = 'main';
        } elseif ($accountLabel === 'Sub Main Trading Account' || $accountLabel === 'Sub Main Future Account') {
            $label = 'sub';
        }
        $accountType = $this->parse_account_type($this->safe_string($account, 'accountType'));
        $type = $label . ' ' . $accountType;
        return array(
            'id' => $this->safe_string($account, 'accountId'),
            'type' => $type,
            'code' => null,
            'info' => $account,
        );
    }

    public function parse_account_type($type) {
        $types = array(
            '1' => 'spot account',
            '3' => 'swap account',
            '5' => 'custody account',
            '6' => 'fiat account',
        );
        return $this->safe_string($types, $type, $type);
    }

    public function encode_account_type($type) {
        $types = array(
            'spot' => '1',
            'swap' => '3',
            'custody' => '5',
        );
        return $this->safe_integer($types, $type, $type);
    }

    public function encode_flow_type($type) {
        $types = array(
            'trade' => '1',
            'fee' => '3',
            'transfer' => '51',
            'deposit' => '900',
            'withdraw' => '904',
        );
        return $this->safe_integer($types, $type, $type);
    }

    public function fetch_ledger(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch the history of changes, actions done by the user or operations that altered the balance of the user
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-account-transaction-list
             *
             * @param {string} [$code] unified $currency $code, default is null (not used)
             * @param {int} [$since] timestamp in ms of the earliest ledger entry, default is null
             * @param {int} [$limit] max number of ledger entries to return, default is null
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {int} [$params->until] the latest time in ms to fetch entries for
             * @param {int} [$params->flowType] trade, fee, transfer, deposit, withdrawal
             * @param {int} [$params->accountType] spot, swap, custody
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=ledger ledger structure~
             */
            $methodName = 'fetchLedger';
            if ($since === null) {
                throw new ArgumentsRequired($this->id . ' ' . $methodName . '() requires a $since argument');
            }
            $until = null;
            list($until, $params) = $this->handle_option_and_params($params, $methodName, 'until');
            if ($until === null) {
                throw new ArgumentsRequired($this->id . ' ' . $methodName . '() requires an $until argument');
            }
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $request = array();
            $request['startTime'] = $since;
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $request['endTime'] = $until;
            $flowType = null;
            list($flowType, $params) = $this->handle_option_and_params($params, $methodName, 'flowType');
            if ($flowType !== null) {
                $request['flowType'] = $this->encode_flow_type($flowType);
            }
            $accountType = null;
            list($accountType, $params) = $this->handle_option_and_params($params, $methodName, 'accountType');
            if ($accountType !== null) {
                $request['accountType'] = $this->encode_account_type($accountType);
            }
            $response = Async\await($this->privateGetApiV1AccountBalanceFlow ($this->extend($request, $params)));
            //
            //     array(
            //         array(
            //             "id" => "1740844413612065537",
            //             "accountId" => "1732885739589466112",
            //             "coin" => "USDT",
            //             "coinId" => "USDT",
            //             "coinName" => "USDT",
            //             "flowTypeValue" => 51,
            //             "flowType" => "USER_ACCOUNT_TRANSFER",
            //             "flowName" => "",
            //             "change" => "-1",
            //             "total" => "8.015680088",
            //             "created" => "1722260825765"
            //         ),
            //         ...
            //     )
            //
            return $this->parse_ledger($response, $currency, $since, $limit);
        }) ();
    }

    public function parse_ledger_entry_type($type) {
        $types = array(
            '1' => 'trade', // transfer
            '2' => 'fee', // trade
            '51' => 'transfer',
            '900' => 'deposit',
            '904' => 'withdraw',
        );
        return $this->safe_string($types, $type, $type);
    }

    public function parse_ledger_entry(array $item, ?array $currency = null): array {
        //
        //     {
        //         "id" => "1740844413612065537",
        //         "accountId" => "1732885739589466112",
        //         "coin" => "USDT",
        //         "coinId" => "USDT",
        //         "coinName" => "USDT",
        //         "flowTypeValue" => 51,
        //         "flowType" => "USER_ACCOUNT_TRANSFER",
        //         "flowName" => "",
        //         "change" => "-1",
        //         "total" => "8.015680088",
        //         "created" => "1722260825765"
        //     }
        //
        $id = $this->safe_string($item, 'id');
        $account = $this->safe_string($item, 'accountId');
        $timestamp = $this->safe_integer($item, 'created');
        $type = $this->parse_ledger_entry_type($this->safe_string($item, 'flowTypeValue'));
        $currencyId = $this->safe_string($item, 'coin');
        $code = $this->safe_currency_code($currencyId, $currency);
        $currency = $this->safe_currency($currencyId, $currency);
        $amountString = $this->safe_string($item, 'change');
        $amount = $this->parse_number($amountString);
        $direction = 'in';
        if (mb_strpos($amountString, '-') !== false) {
            $direction = 'out';
        }
        $afterString = $this->safe_string($item, 'total');
        $after = $this->parse_number($afterString);
        $status = 'ok';
        return $this->safe_ledger_entry(array(
            'info' => $item,
            'id' => $id,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'account' => $account,
            'direction' => $direction,
            'referenceId' => null,
            'referenceAccount' => null,
            'type' => $type,
            'currency' => $code,
            'symbol' => null,
            'amount' => $amount,
            'before' => null,
            'after' => $after,
            'status' => $status,
            'fee' => null,
        ), $currency);
    }

    public function create_order(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $type, $side, $amount, $price, $params) {
            /**
             * create a trade order
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/test-new-order
             * @see https://hashkeyglobal-apidoc.readme.io/reference/create-order
             * @see https://hashkeyglobal-apidoc.readme.io/reference/create-new-futures-order
             *
             * @param {string} $symbol unified $symbol of the $market to create an order in
             * @param {string} $type 'market' or 'limit' or 'LIMIT_MAKER' for spot, 'market' or 'limit' or 'STOP' for swap
             * @param {string} $side 'buy' or 'sell'
             * @param {float} $amount how much of you want to trade in units of the base currency
             * @param {float} [$price] the $price that the order is to be fulfilled, in units of the quote currency, ignored in $market orders
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {float} [$params->cost] *spot $market buy only* the quote quantity that can be used alternative for the $amount
             * @param {boolean} [$params->test] *spot markets only* whether to use the test endpoint or not, default is false
             * @param {bool} [$params->postOnly] if true, the order will only be posted to the order book and not executed immediately
             * @param {string} [$params->timeInForce] "GTC" or "IOC" or "PO" for spot, 'GTC' or 'FOK' or 'IOC' or 'LIMIT_MAKER' or 'PO' for swap
             * @param {string} [$params->clientOrderId] a unique id for the order - is mandatory for swap
             * @param {float} [$params->triggerPrice] *swap markets only* The $price at which a trigger order is triggered at
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            if ($market['spot']) {
                return Async\await($this->create_spot_order($symbol, $type, $side, $amount, $price, $params));
            } elseif ($market['swap']) {
                return Async\await($this->create_swap_order($symbol, $type, $side, $amount, $price, $params));
            } else {
                throw new NotSupported($this->id . ' createOrder() is not supported for ' . $market['type'] . ' $type of markets');
            }
        }) ();
    }

    public function create_market_buy_order_with_cost(string $symbol, float $cost, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $cost, $params) {
            /**
             * create a $market buy order by providing the $symbol and $cost
             * @param {string} $symbol unified $symbol of the $market to create an order in
             * @param {float} $cost how much you want to trade in units of the quote currency
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            if (!$market['spot']) {
                throw new NotSupported($this->id . ' createMarketBuyOrderWithCost() is supported for spot markets only');
            }
            $req = array(
                'cost' => $cost,
            );
            return Async\await($this->create_order($symbol, 'market', 'buy', $cost, null, $this->extend($req, $params)));
        }) ();
    }

    public function create_spot_order(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $type, $side, $amount, $price, $params) {
            /**
             * create a trade order on spot $market
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/test-new-order
             * @see https://hashkeyglobal-apidoc.readme.io/reference/create-order
             *
             * @param {string} $symbol unified $symbol of the $market to create an order in
             * @param {string} $type 'market' or 'limit' or 'LIMIT_MAKER'
             * @param {string} $side 'buy' or 'sell'
             * @param {float} $amount how much of you want to trade in units of the base currency
             * @param {float} [$price] the $price that the order is to be fulfilled, in units of the quote currency, ignored in $market orders
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {float} [$params->cost] *$market buy only* the quote quantity that can be used alternative for the $amount
             * @param {bool} [$params->test] whether to use the $test endpoint or not, default is false
             * @param {bool} [$params->postOnly] if true, the order will only be posted to the order book and not executed immediately
             * @param {string} [$params->timeInForce] 'GTC', 'IOC', or 'PO'
             * @param {string} [$params->clientOrderId] a unique id for the order
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
             */
            $triggerPrice = $this->safe_string_2($params, 'stopPrice', 'triggerPrice');
            if ($triggerPrice !== null) {
                throw new NotSupported($this->id . ' trigger orders are not supported for spot markets');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $isMarketBuy = ($type === 'market') && ($side === 'buy');
            $cost = $this->safe_string($params, 'cost');
            if ((!$isMarketBuy) && ($cost !== null)) {
                throw new NotSupported($this->id . ' createOrder() supports $cost parameter for spot $market buy orders only');
            }
            $request = $this->create_spot_order_request($symbol, $type, $side, $amount, $price, $params);
            $response = array();
            $test = $this->safe_bool($params, 'test');
            if ($test) {
                $params = $this->omit($params, 'test');
                $response = Async\await($this->privatePostApiV1SpotOrderTest ($request));
            } elseif ($isMarketBuy && ($cost === null)) {
                $response = Async\await($this->privatePostApiV11SpotOrder ($request)); // the endpoint for $market buy orders by $amount
                //
                //     {
                //         "accountId" => "1732885739589466112",
                //         "symbol" => "ETHUSDT",
                //         "symbolName" => "ETHUSDT",
                //         "clientOrderId" => "1722005792096557",
                //         "orderId" => "1738705036219839744",
                //         "transactTime" => "1722005792106",
                //         "price" => "0",
                //         "origQty" => "0.006",
                //         "executedQty" => "0.0059",
                //         "status" => "FILLED",
                //         "timeInForce" => "IOC",
                //         "type" => "MARKET",
                //         "side" => "BUY",
                //         "reqAmount" => "0",
                //         "concentration" => ""
                //     }
                //
            } else {
                $response = Async\await($this->privatePostApiV1SpotOrder ($request)); // the endpoint for $market buy orders by $cost and other orders
                //
                // $market buy
                //     {
                //         "accountId" => "1732885739589466112",
                //         "symbol" => "ETHUSDT",
                //         "symbolName" => "ETHUSDT",
                //         "clientOrderId" => "1722004623170558",
                //         "orderId" => "1738695230608169984",
                //         "transactTime" => "1722004623186",
                //         "price" => "0",
                //         "origQty" => "0",
                //         "executedQty" => "0.0061",
                //         "status" => "FILLED",
                //         "timeInForce" => "IOC",
                //         "type" => "MARKET",
                //         "side" => "BUY",
                //         "reqAmount" => "20",
                //         "concentration" => ""
                //     }
                //
                // $market sell
                //     {
                //         "accountId" => "1732885739589466112",
                //         "symbol" => "ETHUSDT",
                //         "symbolName" => "ETHUSDT",
                //         "clientOrderId" => "1722005654516362",
                //         "orderId" => "1738703882140316928",
                //         "transactTime" => "1722005654529",
                //         "price" => "0",
                //         "origQty" => "0.006",
                //         "executedQty" => "0.006",
                //         "status" => "FILLED",
                //         "timeInForce" => "IOC",
                //         "type" => "MARKET",
                //         "side" => "SELL",
                //         "reqAmount" => "0",
                //         "concentration" => ""
                //     }
                //
                // limit
                //     {
                //         "accountId" => "1732885739589466112",
                //         "symbol" => "ETHUSDT",
                //         "symbolName" => "ETHUSDT",
                //         "clientOrderId" => "1722006209978370",
                //         "orderId" => "1738708541676585728",
                //         "transactTime" => "1722006209989",
                //         "price" => "5000",
                //         "origQty" => "0.005",
                //         "executedQty" => "0",
                //         "status" => "NEW",
                //         "timeInForce" => "GTC",
                //         "type" => "LIMIT_MAKER",
                //         "side" => "SELL",
                //         "reqAmount" => "0",
                //         "concentration" => ""
                //     }
                //
            }
            return $this->parse_order($response, $market);
        }) ();
    }

    public function create_order_request(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()): array {
        $market = $this->market($symbol);
        if ($market['spot']) {
            return $this->create_spot_order_request($symbol, $type, $side, $amount, $price, $params);
        } elseif ($market['swap']) {
            return $this->create_swap_order_request($symbol, $type, $side, $amount, $price, $params);
        } else {
            throw new NotSupported($this->id . ' ' . 'createOrderRequest() is not supported for ' . $market['type'] . ' $type of markets');
        }
    }

    public function create_spot_order_request(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()): array {
        /**
         * @ignore
         * helper function to build $request
         * @param {string} $symbol unified $symbol of the $market to create an order in
         * @param {string} $type 'market' or 'limit' or 'LIMIT_MAKER'
         * @param {string} $side 'buy' or 'sell'
         * @param {float} $amount how much of you want to trade in units of the base currency
         * @param {float} [$price] the $price that the order is to be fulfilled, in units of the quote currency, ignored in $market orders
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {float} [$params->cost] *$market buy only* the quote quantity that can be used alternative for the $amount
         * @param {bool} [$params->postOnly] if true, the order will only be posted to the order book and not executed immediately
         * @param {string} [$params->timeInForce] "GTC", "IOC", or "PO"
         * @param {string} [$params->clientOrderId] a unique id for the order
         * @return {array} $request to be sent to the exchange
         */
        $market = $this->market($symbol);
        $type = strtoupper($type);
        $request = array(
            'symbol' => $market['id'],
            'side' => strtoupper($side),
            'type' => $type,
        );
        if ($amount !== null) {
            $request['quantity'] = $this->amount_to_precision($symbol, $amount);
        }
        $cost = null;
        list($cost, $params) = $this->handle_param_string($params, 'cost');
        if ($cost !== null) {
            $request['quantity'] = $this->cost_to_precision($symbol, $cost);
        }
        if ($price !== null) {
            $request['price'] = $this->price_to_precision($symbol, $price);
        }
        $isMarketOrder = $type === 'MARKET';
        $postOnly = false;
        list($postOnly, $params) = $this->handle_post_only($isMarketOrder, $type === 'LIMIT_MAKER', $params);
        if ($postOnly && ($type === 'LIMIT')) {
            $request['type'] = 'LIMIT_MAKER';
        }
        $clientOrderId = null;
        list($clientOrderId, $params) = $this->handle_param_string($params, 'clientOrderId');
        if ($clientOrderId !== null) {
            $params['newClientOrderId'] = $clientOrderId;
        }
        return $this->extend($request, $params);
    }

    public function create_swap_order_request(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()): array {
        /**
         * @ignore
         * helper function to build $request
         * @param {string} $symbol unified $symbol of the $market to create an order in
         * @param {string} $type 'market' or 'limit' or 'STOP'
         * @param {string} $side 'buy' or 'sell'
         * @param {float} $amount how much of you want to trade in units of the base currency
         * @param {float} [$price] the $price that the order is to be fulfilled, in units of the quote currency, ignored in $market orders
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {bool} [$params->postOnly] if true, the order will only be posted to the order book and not executed immediately
         * @param {bool} [$params->reduceOnly] true or false whether the order is reduce only
         * @param {float} [$params->triggerPrice] The $price at which a trigger order is triggered at
         * @param {string} [$params->timeInForce] 'GTC', 'FOK', 'IOC', 'LIMIT_MAKER' or 'PO'
         * @param {string} [$params->clientOrderId] a unique id for the order
         * @return {array} $request to be sent to the exchange
         */
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
            'type' => 'LIMIT',
            'quantity' => $this->amount_to_precision($symbol, $amount),
        );
        $isMarketOrder = $type === 'market';
        if ($isMarketOrder) {
            $request['priceType'] = 'MARKET';
        }
        if ($price !== null) {
            $request['price'] = $this->price_to_precision($symbol, $price);
            $request['priceType'] = 'INPUT';
        }
        $reduceOnly = false;
        list($reduceOnly, $params) = $this->handle_param_bool($params, 'reduceOnly', $reduceOnly);
        $suffix = '_OPEN';
        if ($reduceOnly) {
            $suffix = '_CLOSE';
        }
        $request['side'] = strtoupper($side) . $suffix;
        $timeInForce = null;
        list($timeInForce, $params) = $this->handle_param_string($params, 'timeInForce');
        $postOnly = false;
        list($postOnly, $params) = $this->handle_post_only($isMarketOrder, $timeInForce === 'LIMIT_MAKER', $params);
        if ($postOnly) {
            $timeInForce = 'LIMIT_MAKER';
        }
        if ($timeInForce !== null) {
            $request['timeInForce'] = $timeInForce;
        }
        $clientOrderId = $this->safe_string($params, 'clientOrderId');
        if ($clientOrderId === null) {
            $request['clientOrderId'] = $this->uuid();
        }
        $triggerPrice = $this->safe_string($params, 'triggerPrice');
        if ($triggerPrice !== null) {
            $request['stopPrice'] = $this->price_to_precision($symbol, $triggerPrice);
            $request['type'] = 'STOP';
            $params = $this->omit($params, 'triggerPrice');
        }
        return $this->extend($request, $params);
    }

    public function create_swap_order(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $type, $side, $amount, $price, $params) {
            /**
             * create a trade order on swap $market
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/create-new-futures-order
             *
             * @param {string} $symbol unified $symbol of the $market to create an order in
             * @param {string} $type 'market' or 'limit' or 'STOP'
             * @param {string} $side 'buy' or 'sell'
             * @param {float} $amount how much of you want to trade in units of the base currency
             * @param {float} [$price] the $price that the order is to be fulfilled, in units of the quote currency, ignored in $market orders
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {bool} [$params->postOnly] if true, the order will only be posted to the order book and not executed immediately
             * @param {bool} [$params->reduceOnly] true or false whether the order is reduce only
             * @param {float} [$params->triggerPrice] The $price at which a trigger order is triggered at
             * @param {string} [$params->timeInForce] 'GTC', 'FOK', 'IOC', 'LIMIT_MAKER' or 'PO'
             * @param {string} [$params->clientOrderId] a unique id for the order
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = $this->create_swap_order_request($symbol, $type, $side, $amount, $price, $params);
            $response = Async\await($this->privatePostApiV1FuturesOrder ($this->extend($request, $params)));
            //
            //     {
            //         "time" => "1722429951611",
            //         "updateTime" => "1722429951648",
            //         "orderId" => "1742263144028363776",
            //         "clientOrderId" => "1722429950315",
            //         "symbol" => "ETHUSDT-PERPETUAL",
            //         "price" => "3460.62",
            //         "leverage" => "5",
            //         "origQty" => "10",
            //         "executedQty" => "10",
            //         "avgPrice" => "0",
            //         "marginLocked" => "6.9212",
            //         "type" => "LIMIT",
            //         "side" => "BUY_OPEN",
            //         "timeInForce" => "IOC",
            //         "status" => "FILLED",
            //         "priceType" => "MARKET",
            //         "contractMultiplier" => "0.00100000"
            //     }
            //
            return $this->parse_order($response, $market);
        }) ();
    }

    public function create_orders(array $orders, $params = array ()) {
        return Async\async(function () use ($orders, $params) {
            /**
             * create a list of trade $orders (all $orders should be of the same $symbol)
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/create-multiple-$orders
             * @see https://hashkeyglobal-apidoc.readme.io/reference/batch-create-new-futures-order
             *
             * @param {Array} $orders list of $orders to create, each object should contain the parameters required by createOrder, namely $symbol, $type, $side, $amount, $price and $params
             * @param {array} [$params] extra parameters specific to the api endpoint
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $ordersRequests = array();
            for ($i = 0; $i < count($orders); $i++) {
                $rawOrder = $orders[$i];
                $symbol = $this->safe_string($rawOrder, 'symbol');
                $type = $this->safe_string($rawOrder, 'type');
                $side = $this->safe_string($rawOrder, 'side');
                $amount = $this->safe_number($rawOrder, 'amount');
                $price = $this->safe_number($rawOrder, 'price');
                $orderParams = $this->safe_dict($rawOrder, 'params', array());
                $orderRequest = $this->create_order_request($symbol, $type, $side, $amount, $price, $orderParams);
                $clientOrderId = $this->safe_string($orderRequest, 'clientOrderId');
                if ($clientOrderId === null) {
                    $orderRequest['clientOrderId'] = $this->uuid(); // both spot and swap endpoints require $clientOrderId
                }
                $ordersRequests[] = $orderRequest;
            }
            $firstOrder = $ordersRequests[0];
            $firstSymbol = $this->safe_string($firstOrder, 'symbol');
            $market = $this->market($firstSymbol);
            $request = array(
                'orders' => $ordersRequests,
            );
            $response = null;
            if ($market['spot']) {
                $response = Async\await($this->privatePostApiV1SpotBatchOrders ($this->extend($request, $params)));
                //
                //     {
                //         "code" => 0,
                //         "result" => array(
                //             {
                //                 "code" => "0000",
                //                 "order" => {
                //                     "accountId" => "1732885739589466112",
                //                     "symbol" => "ETHUSDT",
                //                     "symbolName" => "ETHUSDT",
                //                     "clientOrderId" => "1722701490163000",
                //                     "orderId" => "1744540984757258752",
                //                     "transactTime" => "1722701491385",
                //                     "price" => "1500",
                //                     "origQty" => "0.001",
                //                     "executedQty" => "0",
                //                     "status" => "NEW",
                //                     "timeInForce" => "GTC",
                //                     "type" => "LIMIT",
                //                     "side" => "BUY",
                //                     "reqAmount" => "0"
                //                 }
                //             }
                //         ),
                //         "concentration" => ""
                //     }
                //
            } elseif ($market['swap']) {
                $response = Async\await($this->privatePostApiV1FuturesBatchOrders ($this->extend($request, $params)));
                //
                //     {
                //         "code" => "0000",
                //         "result" => array(
                //             {
                //                 "code" => "0000",
                //                 "order" => array(
                //                     "time" => "1722704251911",
                //                     "updateTime" => "1722704251918",
                //                     "orderId" => "1744564141727808768",
                //                     "clientOrderId" => "1722704250648000",
                //                     "symbol" => "ETHUSDT-PERPETUAL",
                //                     "price" => "1500",
                //                     "leverage" => "4",
                //                     "origQty" => "1",
                //                     "executedQty" => "0",
                //                     "avgPrice" => "0",
                //                     "marginLocked" => "0.375",
                //                     "type" => "LIMIT",
                //                     "side" => "BUY_OPEN",
                //                     "timeInForce" => "GTC",
                //                     "status" => "NEW",
                //                     "priceType" => "INPUT",
                //                     "isLiquidationOrder" => false,
                //                     "indexPrice" => "0",
                //                     "liquidationType" => ""
                //                 }
                //             ),
                //             {
                //                 "code" => "0207",
                //                 "msg" => "Create limit order sell $price too low"
                //             }
                //         )
                //     }
                //
            } else {
                throw new NotSupported($this->id . ' ' . 'createOrderRequest() is not supported for ' . $market['type'] . ' $type of markets');
            }
            $result = $this->safe_list($response, 'result', array());
            $responseOrders = array();
            for ($i = 0; $i < count($result); $i++) {
                $responseEntry = $this->safe_dict($result, $i, array());
                $responseOrder = $this->safe_dict($responseEntry, 'order', array());
                $responseOrders[] = $responseOrder;
            }
            return $this->parse_orders($responseOrders);
        }) ();
    }

    public function cancel_order(string $id, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($id, $symbol, $params) {
            /**
             * cancels an open order
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/cancel-order
             * @see https://hashkeyglobal-apidoc.readme.io/reference/cancel-futures-order
             *
             * @param {string} $id order $id
             * @param {string} $symbol unified $symbol of the $market the order was made in
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->type] 'spot' or 'swap' - the type of the $market to fetch entry for (default 'spot')
             * @param {string} [$params->clientOrderId] a unique $id for the order that can be used alternative for the $id
             * @param {bool} [$params->trigger] *swap markets only* true for canceling a trigger order (default false)
             * @param {bool} [$params->stop] *swap markets only* an alternative for trigger param
             * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
             */
            $methodName = 'cancelOrder';
            $this->check_type_param($methodName, $params);
            Async\await($this->load_markets());
            $request = array();
            $clientOrderId = $this->safe_string($params, 'clientOrderId');
            if ($clientOrderId === null) {
                $request['orderId'] = $id;
            }
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            $marketType = 'spot';
            list($marketType, $params) = $this->handle_market_type_and_params($methodName, $market, $params, $marketType);
            $response = null;
            if ($marketType === 'spot') {
                $response = Async\await($this->privateDeleteApiV1SpotOrder ($this->extend($request, $params)));
                //
                //     {
                //         "accountId" => "1732885739589466112",
                //         "symbol" => "ETHUSDT",
                //         "clientOrderId" => "1722006209978370",
                //         "orderId" => "1738708541676585728",
                //         "transactTime" => "1722006209989",
                //         "price" => "5000",
                //         "origQty" => "0.005",
                //         "executedQty" => "0",
                //         "status" => "NEW",
                //         "timeInForce" => "GTC",
                //         "type" => "LIMIT_MAKER",
                //         "side" => "SELL"
                //     }
                //
            } elseif ($marketType === 'swap') {
                $isTrigger = false;
                list($isTrigger, $params) = $this->handle_trigger_option_and_params($params, $methodName, $isTrigger);
                if ($isTrigger) {
                    $request['type'] = 'STOP';
                } else {
                    $request['type'] = 'LIMIT';
                }
                if ($market !== null) {
                    $request['symbol'] = $market['id'];
                }
                $response = Async\await($this->privateDeleteApiV1FuturesOrder ($this->extend($request, $params)));
                //
                //     {
                //         "time" => "1722432302919",
                //         "updateTime" => "1722432302925",
                //         "orderId" => "1742282868229463040",
                //         "clientOrderId" => "1722432301670",
                //         "symbol" => "ETHUSDT-PERPETUAL",
                //         "price" => "4000",
                //         "leverage" => "5",
                //         "origQty" => "10",
                //         "executedQty" => "0",
                //         "avgPrice" => "0",
                //         "marginLocked" => "0",
                //         "type" => "LIMIT_MAKER",
                //         "side" => "SELL_CLOSE",
                //         "timeInForce" => "GTC",
                //         "status" => "NEW",
                //         "priceType" => "INPUT",
                //         "isLiquidationOrder" => false,
                //         "indexPrice" => "0",
                //         "liquidationType" => ""
                //     }
                //
            } else {
                throw new NotSupported($this->id . ' ' . $methodName . '() is not supported for ' . $marketType . ' type of markets');
            }
            return $this->parse_order($response);
        }) ();
    }

    public function cancel_all_orders(?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($symbol, $params) {
            /**
             * cancel all open orders
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/cancel-all-open-orders
             * @see https://hashkeyglobal-apidoc.readme.io/reference/batch-cancel-futures-$order
             *
             * @param {string} $symbol unified $market $symbol, only orders in the $market of this $symbol are cancelled when $symbol is not null
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->side] 'buy' or 'sell'
             * @return {array} $response from exchange
             */
            // Does not cancel trigger orders. For canceling trigger $order use cancelOrder() or cancelOrders()
            $methodName = 'cancelAllOrders';
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' ' . $methodName . '() requires a $symbol argument');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $side = $this->safe_string($params, 'side');
            if ($side !== null) {
                $request['side'] = $side;
            }
            $response = null;
            if ($market['spot']) {
                $response = Async\await($this->privateDeleteApiV1SpotOpenOrders ($this->extend($request, $params)));
                //
                //     array( "success" => true )
                //
            } elseif ($market['swap']) {
                $response = Async\await($this->privateDeleteApiV1FuturesBatchOrders ($this->extend($request, $params)));
                //
                //     array( "message" => "success", "timestamp" => "1723127222198", "code" => "0000" )
                //
            } else {
                throw new NotSupported($this->id . ' ' . $methodName . '() is not supported for ' . $market['type'] . ' type of markets');
            }
            $order = $this->safe_order($response);
            $order['info'] = $response;
            return array( $order );
        }) ();
    }

    public function cancel_orders(array $ids, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($ids, $symbol, $params) {
            /**
             * cancel multiple orders
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/cancel-multiple-orders
             * @see https://hashkeyglobal-apidoc.readme.io/reference/batch-cancel-futures-$order-by-$order-id
             *
             * @param {string[]} $ids $order $ids
             * @param {string} [$symbol] unified $market $symbol (not used by hashkey)
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->type] 'spot' or 'swap' - the type of the $market to fetch entry for (default 'spot')
             * @return {array} an list of ~@link https://docs.ccxt.com/#/?id=$order-structure $order structures~
             */
            $methodName = 'cancelOrders';
            Async\await($this->load_markets());
            $request = array();
            $orderIds = implode(',', $ids);
            $request['ids'] = $orderIds;
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            $marketType = 'spot';
            list($marketType, $params) = $this->handle_market_type_and_params($methodName, $market, $params, $marketType);
            $response = null;
            if ($marketType === 'spot') {
                $response = Async\await($this->privateDeleteApiV1SpotCancelOrderByIds ($this->extend($request)));
                //
                //     {
                //         "code" => "0000",
                //         "result" => array()
                //     }
                //
            } elseif ($marketType === 'swap') {
                $response = $this->privateDeleteApiV1FuturesCancelOrderByIds ($this->extend($request));
            } else {
                throw new NotSupported($this->id . ' ' . $methodName . '() is not supported for ' . $marketType . ' type of markets');
            }
            $order = $this->safe_order($response);
            $order['info'] = $response;
            return array( $order );
        }) ();
    }

    public function fetch_order(string $id, ?string $symbol = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($id, $symbol, $params) {
            /**
             * fetches information on an order made by the user
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/query-order
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-futures-order
             *
             * @param {string} $id the order $id
             * @param {string} $symbol unified $symbol of the $market the order was made in
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->type] 'spot' or 'swap' - the type of the $market to fetch entry for (default 'spot')
             * @param {string} [$params->clientOrderId] a unique $id for the order that can be used alternative for the $id
             * @param {string} [$params->accountId] *spot markets only* account $id to fetch the order from
             * @param {bool} [$params->trigger] *swap markets only* true for fetching a trigger order (default false)
             * @param {bool} [$params->stop] *swap markets only* an alternative for trigger param
             * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
             */
            $methodName = 'fetchOrder';
            $this->check_type_param($methodName, $params);
            Async\await($this->load_markets());
            $request = array();
            $clientOrderId = null;
            list($clientOrderId, $params) = $this->handle_param_string($params, 'clientOrderId');
            if ($clientOrderId === null) {
                $request['orderId'] = $id;
            }
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            $marketType = 'spot';
            list($marketType, $params) = $this->handle_market_type_and_params($methodName, $market, $params, $marketType);
            $response = null;
            if ($marketType === 'spot') {
                if ($clientOrderId !== null) {
                    $request['origClientOrderId'] = $clientOrderId;
                }
                $response = Async\await($this->privateGetApiV1SpotOrder ($this->extend($request, $params)));
                //
                //     {
                //         "accountId" => "1732885739589466112",
                //         "exchangeId" => "301",
                //         "symbol" => "ETHUSDT",
                //         "symbolName" => "ETHUSDT",
                //         "clientOrderId" => "1722004623170558",
                //         "orderId" => "1738695230608169984",
                //         "price" => "0",
                //         "origQty" => "0",
                //         "executedQty" => "0.0061",
                //         "cummulativeQuoteQty" => "19.736489",
                //         "cumulativeQuoteQty" => "19.736489",
                //         "avgPrice" => "3235.49",
                //         "status" => "FILLED",
                //         "timeInForce" => "IOC",
                //         "type" => "MARKET",
                //         "side" => "BUY",
                //         "stopPrice" => "0.0",
                //         "icebergQty" => "0.0",
                //         "time" => "1722004623186",
                //         "updateTime" => "1722004623406",
                //         "isWorking" => true,
                //         "reqAmount" => "20",
                //         "feeCoin" => "",
                //         "feeAmount" => "0",
                //         "sumFeeAmount" => "0"
                //     }
                //
            } elseif ($marketType === 'swap') {
                $isTrigger = false;
                list($isTrigger, $params) = $this->handle_trigger_option_and_params($params, $methodName, $isTrigger);
                if ($isTrigger) {
                    $request['type'] = 'STOP';
                }
                $response = Async\await($this->privateGetApiV1FuturesOrder ($this->extend($request, $params)));
                //
                //     {
                //         "time" => "1722429951611",
                //         "updateTime" => "1722429951700",
                //         "orderId" => "1742263144028363776",
                //         "clientOrderId" => "1722429950315",
                //         "symbol" => "ETHUSDT-PERPETUAL",
                //         "price" => "3460.62",
                //         "leverage" => "5",
                //         "origQty" => "10",
                //         "executedQty" => "10",
                //         "avgPrice" => "3327.52",
                //         "marginLocked" => "0",
                //         "type" => "LIMIT",
                //         "side" => "BUY_OPEN",
                //         "timeInForce" => "IOC",
                //         "status" => "FILLED",
                //         "priceType" => "MARKET",
                //         "isLiquidationOrder" => false,
                //         "indexPrice" => "0",
                //         "liquidationType" => ""
                //     }
                //
            } else {
                throw new NotSupported($this->id . ' ' . $methodName . '() is not supported for ' . $marketType . ' type of markets');
            }
            return $this->parse_order($response);
        }) ();
    }

    public function fetch_open_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetch all unfilled currently open orders
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-current-open-orders
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-sub-account-open-orders
             * @see https://hashkeyglobal-apidoc.readme.io/reference/sub
             * @see https://hashkeyglobal-apidoc.readme.io/reference/query-open-futures-orders
             *
             * @param {string} [$symbol] unified $market $symbol of the $market orders were made in - is mandatory for swap markets
             * @param {int} [$since] the earliest time in ms to fetch orders for
             * @param {int} [$limit] the maximum number of order structures to retrieve - default 500, maximum 1000
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->type] 'spot' or 'swap' - the type of the $market to fetch entries for (default 'spot')
             * @param {string} [$params->orderId] *spot markets only* the id of the order to fetch
             * @param {string} [$params->side] *spot markets only* 'buy' or 'sell' - the side of the orders to fetch
             * @param {string} [$params->fromOrderId] *swap markets only* the id of the order to start from
             * @param {bool} [$params->trigger] *swap markets only* true for fetching trigger orders (default false)
             * @param {bool} [$params->stop] *swap markets only* an alternative for trigger param
             * @param {string} [$params->accountId] account id to fetch the orders from
             * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            $methodName = 'fetchOpenOrders';
            $this->check_type_param($methodName, $params);
            Async\await($this->load_markets());
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            $marketType = 'spot';
            list($marketType, $params) = $this->handle_market_type_and_params($methodName, $market, $params, $marketType);
            $params = $this->extend(array( 'methodName' => $methodName ), $params);
            if ($marketType === 'spot') {
                return Async\await($this->fetch_open_spot_orders($symbol, $since, $limit, $params));
            } elseif ($marketType === 'swap') {
                return Async\await($this->fetch_open_swap_orders($symbol, $since, $limit, $params));
            } else {
                throw new NotSupported($this->id . ' ' . $methodName . '() is not supported for ' . $marketType . ' type of markets');
            }
        }) ();
    }

    public function fetch_open_spot_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * @ignore
             * fetch all unfilled currently open orders for spot markets
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-current-open-orders
             * @see https://hashkeyglobal-apidoc.readme.io/reference/sub
             *
             * @param {string} [$symbol] unified $market $symbol of the $market orders were made in
             * @param {int} [$since] the earliest time in ms to fetch orders for
             * @param {int} [$limit] the maximum number of order structures to retrieve - default 500, maximum 1000
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->orderId] the id of the order to fetch
             * @param {string} [$params->side] 'buy' or 'sell' - the side of the orders to fetch
             * @param {string} [$params->accountId] account id to fetch the orders from
             * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $methodName = 'fetchOpenSpotOrders';
            list($methodName, $params) = $this->handle_param_string($params, 'methodName', $methodName);
            $market = null;
            $request = array();
            $response = null;
            $accountId = null;
            list($accountId, $params) = $this->handle_option_and_params($params, $methodName, 'accountId');
            if ($accountId !== null) {
                $request['subAccountId'] = $accountId;
                $response = Async\await($this->privateGetApiV1SpotSubAccountOpenOrders ($this->extend($request, $params)));
            } else {
                if ($symbol !== null) {
                    $market = $this->market($symbol);
                    $request['symbol'] = $market['id'];
                }
                if ($limit !== null) {
                    $request['limit'] = $limit;
                }
                $response = Async\await($this->privateGetApiV1SpotOpenOrders ($this->extend($request, $params)));
                //
                //     array(
                //         {
                //             "accountId" => "1732885739589466112",
                //             "exchangeId" => "301",
                //             "symbol" => "ETHUSDT",
                //             "symbolName" => "ETHUSDT",
                //             "clientOrderId" => "1",
                //             "orderId" => "1739491435386897152",
                //             "price" => "2000",
                //             "origQty" => "0.001",
                //             "executedQty" => "0",
                //             "cummulativeQuoteQty" => "0",
                //             "cumulativeQuoteQty" => "0",
                //             "avgPrice" => "0",
                //             "status" => "NEW",
                //             "timeInForce" => "GTC",
                //             "type" => "LIMIT",
                //             "side" => "BUY",
                //             "stopPrice" => "0.0",
                //             "icebergQty" => "0.0",
                //             "time" => "1722099538193",
                //             "updateTime" => "1722099538197",
                //             "isWorking" => true,
                //             "reqAmount" => "0"
                //         }
                //     )
                //
            }
            return $this->parse_orders($response, $market, $since, $limit);
        }) ();
    }

    public function fetch_open_swap_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * @ignore
             * fetch all unfilled currently open orders for swap markets
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/query-open-futures-orders
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-sub-account-open-orders
             *
             * @param {string} $symbol *is mandatory* unified $market $symbol of the $market orders were made in
             * @param {int} [$since] the earliest time in ms to fetch orders for
             * @param {int} [$limit] the maximum number of order structures to retrieve - maximum 500
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->fromOrderId] the id of the order to start from
             * @param {bool} [$params->trigger] true for fetching trigger orders (default false)
             * @param {bool} [$params->stop] an alternative for trigger param
             * @param {string} [$params->accountId] account id to fetch the orders from
             * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            $methodName = 'fetchOpenSwapOrders';
            list($methodName, $params) = $this->handle_param_string($params, 'methodName', $methodName);
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' ' . $methodName . '() requires a $symbol argument for swap $market orders');
            }
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $isTrigger = false;
            list($isTrigger, $params) = $this->handle_trigger_option_and_params($params, $methodName, $isTrigger);
            if ($isTrigger) {
                $request['type'] = 'STOP';
            } else {
                $request['type'] = 'LIMIT';
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = null;
            $accountId = null;
            list($accountId, $params) = $this->handle_option_and_params($params, $methodName, 'accountId');
            if ($accountId !== null) {
                $request['subAccountId'] = $accountId;
                $response = Async\await($this->privateGetApiV1FuturesSubAccountOpenOrders ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->privateGetApiV1FuturesOpenOrders ($this->extend($request, $params)));
                // 'LIMIT'
                //     array(
                //         {
                //             "time" => "1722432302919",
                //             "updateTime" => "1722432302925",
                //             "orderId" => "1742282868229463040",
                //             "clientOrderId" => "1722432301670",
                //             "symbol" => "ETHUSDT-PERPETUAL",
                //             "price" => "4000",
                //             "leverage" => "5",
                //             "origQty" => "10",
                //             "executedQty" => "0",
                //             "avgPrice" => "0",
                //             "marginLocked" => "0",
                //             "type" => "LIMIT_MAKER",
                //             "side" => "SELL_CLOSE",
                //             "timeInForce" => "GTC",
                //             "status" => "NEW",
                //             "priceType" => "INPUT",
                //             "isLiquidationOrder" => false,
                //             "indexPrice" => "0",
                //             "liquidationType" => ""
                //         }
                //     )
                //
                // 'STOP'
                //     array(
                //         {
                //             "time" => "1722433095688",
                //             "updateTime" => "1722433095688",
                //             "orderId" => "1742289518466225664",
                //             "accountId" => "1735619524953226496",
                //             "clientOrderId" => "1722433094438",
                //             "symbol" => "ETHUSDT-PERPETUAL",
                //             "price" => "3700",
                //             "leverage" => "0",
                //             "origQty" => "10",
                //             "type" => "STOP",
                //             "side" => "SELL_CLOSE",
                //             "status" => "ORDER_NEW",
                //             "stopPrice" => "3600"
                //         }
                //     )
            }
            return $this->parse_orders($response, $market, $since, $limit);
        }) ();
    }

    public function fetch_canceled_and_closed_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches information on multiple canceled and closed orders made by the user
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-all-orders
             * @see https://hashkeyglobal-apidoc.readme.io/reference/query-futures-history-orders
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-sub-account-history-orders
             *
             * @param {string} $symbol *is mandatory for swap markets* unified $market $symbol of the $market orders were made in
             * @param {int} [$since] the earliest time in ms to fetch orders for
             * @param {int} [$limit] the maximum number of order structures to retrieve - default 500, maximum 1000
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {int} [$params->until] the latest time in ms to fetch entries for - only supports the last 90 days timeframe
             * @param {string} [$params->type] 'spot' or 'swap' - the type of the $market to fetch entries for (default 'spot')
             * @param {string} [$params->orderId] *spot markets only* the id of the order to fetch
             * @param {string} [$params->side] *spot markets only* 'buy' or 'sell' - the side of the orders to fetch
             * @param {string} [$params->fromOrderId] *swap markets only* the id of the order to start from
             * @param {bool} [$params->trigger] *swap markets only* the id of the order to start from true for fetching trigger orders (default false)
             * @param {bool} [$params->stop] *swap markets only* the id of the order to start from an alternative for trigger param
             * @param {string} [$params->accountId] account id to fetch the orders from
             * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            $methodName = 'fetchCanceledAndClosedOrders';
            $this->check_type_param($methodName, $params);
            Async\await($this->load_markets());
            $request = array();
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            $until = null;
            list($until, $params) = $this->handle_option_and_params($params, $methodName, 'until');
            if ($until !== null) {
                $request['endTime'] = $until;
            }
            $accountId = null;
            list($accountId, $params) = $this->handle_option_and_params($params, $methodName, 'accountId');
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            $marketType = 'spot';
            list($marketType, $params) = $this->handle_market_type_and_params($methodName, $market, $params, $marketType);
            $response = null;
            if ($marketType === 'spot') {
                if ($market !== null) {
                    $request['symbol'] = $market['id'];
                }
                if ($accountId !== null) {
                    $request['accountId'] = $accountId;
                }
                $response = Async\await($this->privateGetApiV1SpotTradeOrders ($this->extend($request, $params)));
                //
                //     array(
                //         array(
                //             "accountId" => "1732885739589466112",
                //             "exchangeId" => "301",
                //             "symbol" => "ETHUSDT",
                //             "symbolName" => "ETHUSDT",
                //             "clientOrderId" => "1722082982086472",
                //             "orderId" => "1739352552762301440",
                //             "price" => "0",
                //             "origQty" => "0.001",
                //             "executedQty" => "0.001",
                //             "cummulativeQuoteQty" => "3.28996",
                //             "cumulativeQuoteQty" => "3.28996",
                //             "avgPrice" => "3289.96",
                //             "status" => "FILLED",
                //             "timeInForce" => "IOC",
                //             "type" => "MARKET",
                //             "side" => "BUY",
                //             "stopPrice" => "0.0",
                //             "icebergQty" => "0.0",
                //             "time" => "1722082982093",
                //             "updateTime" => "1722082982097",
                //             "isWorking" => true,
                //             "reqAmount" => "0"
                //         ),
                //         ...
                //     )
                //
            } elseif ($marketType === 'swap') {
                if ($symbol === null) {
                    throw new ArgumentsRequired($this->id . ' ' . $methodName . '() requires a $symbol argument for swap markets');
                }
                $request['symbol'] = $market['id'];
                $isTrigger = false;
                list($isTrigger, $params) = $this->handle_trigger_option_and_params($params, $methodName, $isTrigger);
                if ($isTrigger) {
                    $request['type'] = 'STOP';
                } else {
                    $request['type'] = 'LIMIT';
                }
                if ($accountId !== null) {
                    $request['subAccountId'] = $accountId;
                    $response = Async\await($this->privateGetApiV1FuturesSubAccountHistoryOrders ($this->extend($request, $params)));
                } else {
                    $response = Async\await($this->privateGetApiV1FuturesHistoryOrders ($this->extend($request, $params)));
                    //
                    //     array(
                    //         {
                    //             "time" => "1722429951611",
                    //             "updateTime" => "1722429951700",
                    //             "orderId" => "1742263144028363776",
                    //             "clientOrderId" => "1722429950315",
                    //             "symbol" => "ETHUSDT-PERPETUAL",
                    //             "price" => "3460.62",
                    //             "leverage" => "5",
                    //             "origQty" => "10",
                    //             "executedQty" => "10",
                    //             "avgPrice" => "3327.52",
                    //             "marginLocked" => "0",
                    //             "type" => "LIMIT",
                    //             "side" => "BUY_OPEN",
                    //             "timeInForce" => "IOC",
                    //             "status" => "FILLED",
                    //             "priceType" => "MARKET",
                    //             "isLiquidationOrder" => false,
                    //             "indexPrice" => "0",
                    //             "liquidationType" => ""
                    //         }
                    //     )
                    //
                }
            } else {
                throw new NotSupported($this->id . ' ' . $methodName . '() is not supported for ' . $marketType . ' type of markets');
            }
            return $this->parse_orders($response, $market, $since, $limit);
        }) ();
    }

    public function check_type_param($methodName, $params) {
        // some hashkey endpoints have a type param for swap markets that defines the type of an order
        // type param is reserved in ccxt for defining the type of the market
        // current method warns user if he provides the exchange specific value in type parameter
        $paramsType = $this->safe_string($params, 'type');
        if (($paramsType !== null) && ($paramsType !== 'spot') && ($paramsType !== 'swap')) {
            throw new BadRequest($this->id . ' ' . $methodName . ' () type parameter can not be "' . $paramsType . '". It should define the type of the market ("spot" or "swap"). To define the type of an order use the trigger parameter (true for trigger orders)');
        }
    }

    public function handle_trigger_option_and_params(array $params, string $methodName, $defaultValue = null) {
        $isTrigger = $defaultValue;
        list($isTrigger, $params) = $this->handle_option_and_params_2($params, $methodName, 'stop', 'trigger', $isTrigger);
        return array( $isTrigger, $params );
    }

    public function parse_order(array $order, ?array $market = null): array {
        //
        // createOrder spot
        //     {
        //         "accountId" => "1732885739589466112",
        //         "symbol" => "ETHUSDT",
        //         "symbolName" => "ETHUSDT",
        //         "clientOrderId" => "1722004623170558",
        //         "orderId" => "1738695230608169984",
        //         "transactTime" => "1722004623186",
        //         "price" => "0",
        //         "origQty" => "0",
        //         "executedQty" => "0.0061",
        //         "status" => "FILLED",
        //         "timeInForce" => "IOC",
        //         "type" => "MARKET",
        //         "side" => "BUY",
        //         "reqAmount" => "20",
        //         "concentration" => ""
        //     }
        //
        // fetchOrder spot
        //     {
        //         "accountId" => "1732885739589466112",
        //         "exchangeId" => "301",
        //         "symbol" => "ETHUSDT",
        //         "symbolName" => "ETHUSDT",
        //         "clientOrderId" => "1722004623170558",
        //         "orderId" => "1738695230608169984",
        //         "price" => "0",
        //         "origQty" => "0",
        //         "executedQty" => "0.0061",
        //         "cummulativeQuoteQty" => "19.736489",
        //         "cumulativeQuoteQty" => "19.736489",
        //         "avgPrice" => "3235.49",
        //         "status" => "FILLED",
        //         "timeInForce" => "IOC",
        //         "type" => "MARKET",
        //         "side" => "BUY",
        //         "stopPrice" => "0.0",
        //         "icebergQty" => "0.0",
        //         "time" => "1722004623186",
        //         "updateTime" => "1722004623406",
        //         "isWorking" => true,
        //         "reqAmount" => "20",
        //         "feeCoin" => "",
        //         "feeAmount" => "0",
        //         "sumFeeAmount" => "0"
        //     }
        //
        // cancelOrder
        //     {
        //         "accountId" => "1732885739589466112",
        //         "symbol" => "ETHUSDT",
        //         "clientOrderId" => "1722006209978370",
        //         "orderId" => "1738708541676585728",
        //         "transactTime" => "1722006209989",
        //         "price" => "5000",
        //         "origQty" => "0.005",
        //         "executedQty" => "0",
        //         "status" => "NEW",
        //         "timeInForce" => "GTC",
        //         "type" => "LIMIT_MAKER",
        //         "side" => "SELL"
        //     }
        //
        // createOrder swap
        //     {
        //         "time" => "1722429951611",
        //         "updateTime" => "1722429951648",
        //         "orderId" => "1742263144028363776",
        //         "clientOrderId" => "1722429950315",
        //         "symbol" => "ETHUSDT-PERPETUAL",
        //         "price" => "3460.62",
        //         "leverage" => "5",
        //         "origQty" => "10",
        //         "executedQty" => "10",
        //         "avgPrice" => "0",
        //         "marginLocked" => "6.9212",
        //         "type" => "LIMIT",
        //         "side" => "BUY_OPEN",
        //         "timeInForce" => "IOC",
        //         "status" => "FILLED",
        //         "priceType" => "MARKET",
        //         "contractMultiplier" => "0.00100000"
        //     }
        //
        // fetchOrder swap
        //     {
        //         "time" => "1722429951611",
        //         "updateTime" => "1722429951700",
        //         "orderId" => "1742263144028363776",
        //         "clientOrderId" => "1722429950315",
        //         "symbol" => "ETHUSDT-PERPETUAL",
        //         "price" => "3460.62",
        //         "leverage" => "5",
        //         "origQty" => "10",
        //         "executedQty" => "10",
        //         "avgPrice" => "3327.52",
        //         "marginLocked" => "0",
        //         "type" => "LIMIT",
        //         "side" => "BUY_OPEN",
        //         "timeInForce" => "IOC",
        //         "status" => "FILLED",
        //         "priceType" => "MARKET",
        //         "isLiquidationOrder" => false,
        //         "indexPrice" => "0",
        //         "liquidationType" => ""
        //     }
        //
        $marketId = $this->safe_string($order, 'symbol');
        $market = $this->safe_market($marketId, $market);
        $timestamp = $this->safe_integer_2($order, 'transactTime', 'time');
        $status = $this->safe_string($order, 'status');
        $type = $this->safe_string($order, 'type');
        $priceType = $this->safe_string($order, 'priceType');
        if ($priceType === 'MARKET') {
            $type = 'market';
        }
        $price = $this->omit_zero($this->safe_string($order, 'price'));
        if ($type === 'STOP') {
            if ($price === null) {
                $type = 'market';
            } else {
                $type = 'limit';
            }
        }
        $timeInForce = $this->safe_string($order, 'timeInForce');
        $postOnly = null;
        list($type, $timeInForce, $postOnly) = $this->parse_order_type_time_in_force_and_post_only($type, $timeInForce);
        $average = $this->omit_zero($this->safe_string($order, 'avgPrice'));
        if ($price === null) {
            $price = $average;
        }
        $side = $this->safe_string_lower($order, 'side');
        $reduceOnly = null;
        list($side, $reduceOnly) = $this->parse_order_side_and_reduce_only($side);
        $feeCurrncyId = $this->safe_string($order, 'feeCoin');
        if ($feeCurrncyId === '') {
            $feeCurrncyId = null;
        }
        return $this->safe_order(array(
            'id' => $this->safe_string($order, 'orderId'),
            'clientOrderId' => $this->safe_string($order, 'clientOrderId'),
            'datetime' => $this->iso8601($timestamp),
            'timestamp' => $timestamp,
            'lastTradeTimestamp' => null,
            'lastUpdateTimestamp' => $this->safe_integer($order, 'updateTime'),
            'status' => $this->parse_order_status($status),
            'symbol' => $market['symbol'],
            'type' => $type,
            'timeInForce' => $timeInForce,
            'side' => $side,
            'price' => $price,
            'average' => $average,
            'amount' => $this->omit_zero($this->safe_string($order, 'origQty')),
            'filled' => $this->safe_string($order, 'executedQty'),
            'remaining' => null,
            'triggerPrice' => $this->omit_zero($this->safe_string($order, 'stopPrice')),
            'takeProfitPrice' => null,
            'stopLossPrice' => null,
            'cost' => $this->omit_zero($this->safe_string_2($order, 'cumulativeQuoteQty', 'cummulativeQuoteQty')),
            'trades' => null,
            'fee' => array(
                'currency' => $this->safe_currency_code($feeCurrncyId),
                'amount' => $this->omit_zero($this->safe_string($order, 'feeAmount')),
            ),
            'reduceOnly' => $reduceOnly,
            'postOnly' => $postOnly,
            'info' => $order,
        ), $market);
    }

    public function parse_order_side_and_reduce_only($unparsed) {
        $parts = explode('_', $unparsed);
        $side = $parts[0];
        $reduceOnly = null;
        $secondPart = $this->safe_string($parts, 1);
        if ($secondPart !== null) {
            if ($secondPart === 'open') {
                $reduceOnly = false;
            } elseif (($secondPart === 'close')) {
                $reduceOnly = true;
            }
        }
        return array( $side, $reduceOnly );
    }

    public function parse_order_status($status) {
        $statuses = array(
            'NEW' => 'open',
            'PARTIALLY_FILLED' => 'open',
            'PARTIALLY_CANCELED' => 'canceled',
            'FILLED' => 'closed',
            'CANCELED' => 'canceled',
            'ORDER_CANCELED' => 'canceled',
            'PENDING_CANCEL' => 'canceled',
            'REJECTED' => 'rejected',
            'ORDER_NEW' => 'open',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function parse_order_type_time_in_force_and_post_only($type, $timeInForce) {
        $postOnly = null;
        if ($type === 'LIMIT_MAKER') {
            $postOnly = true;
        } elseif (($timeInForce === 'LIMIT_MAKER') || ($timeInForce === 'MAKER')) {
            $postOnly = true;
            $timeInForce = 'PO';
        }
        $type = $this->parse_order_type($type);
        return array( $type, $timeInForce, $postOnly );
    }

    public function parse_order_type($type) {
        $types = array(
            'MARKET' => 'market',
            'LIMIT' => 'limit',
            'LIMIT_MAKER' => 'limit',
            'MARKET_OF_BASE' => 'market',
        );
        return $this->safe_string($types, $type, $type);
    }

    public function fetch_funding_rate(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetch the current funding $rate
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-futures-funding-$rate
             *
             * @param {string} $symbol unified $market $symbol
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=funding-$rate-structure funding $rate structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
                'timestamp' => $this->milliseconds(),
            );
            $response = Async\await($this->publicGetApiV1FuturesFundingRate ($this->extend($request, $params)));
            //
            //     array(
            //         array( "symbol" => "ETHUSDT-PERPETUAL", "rate" => "0.0001", "nextSettleTime" => "1722297600000" )
            //     )
            //
            $rate = $this->safe_dict($response, 0, array());
            return $this->parse_funding_rate($rate, $market);
        }) ();
    }

    public function fetch_funding_rates(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * fetch the funding rate for multiple markets
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-futures-funding-rate
             *
             * @param {string[]|null} $symbols list of unified market $symbols
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=funding-rates-structure funding rate structures~, indexed by market $symbols
             */
            Async\await($this->load_markets());
            $symbols = $this->market_symbols($symbols);
            $request = array(
                'timestamp' => $this->milliseconds(),
            );
            $response = Async\await($this->publicGetApiV1FuturesFundingRate ($this->extend($request, $params)));
            //
            //     array(
            //         array( "symbol" => "BTCUSDT-PERPETUAL", "rate" => "0.0001", "nextSettleTime" => "1722297600000" ),
            //         array( "symbol" => "ETHUSDT-PERPETUAL", "rate" => "0.0001", "nextSettleTime" => "1722297600000" )
            //     )
            //
            return $this->parse_funding_rates($response, $symbols);
        }) ();
    }

    public function parse_funding_rate($contract, ?array $market = null): array {
        //
        //     {
        //         "symbol" => "ETHUSDT-PERPETUAL",
        //         "rate" => "0.0001",
        //         "nextSettleTime" => "1722297600000"
        //     }
        //
        $marketId = $this->safe_string($contract, 'symbol');
        $market = $this->safe_market($marketId, $market, null, 'swap');
        $fundingRate = $this->safe_number($contract, 'rate');
        $fundingTimestamp = $this->safe_integer($contract, 'nextSettleTime');
        return array(
            'info' => $contract,
            'symbol' => $market['symbol'],
            'markPrice' => null,
            'indexPrice' => null,
            'interestRate' => null,
            'estimatedSettlePrice' => null,
            'timestamp' => null,
            'datetime' => null,
            'fundingRate' => $fundingRate,
            'fundingTimestamp' => null,
            'fundingDatetime' => null,
            'nextFundingRate' => null,
            'nextFundingTimestamp' => $fundingTimestamp,
            'nextFundingDatetime' => $this->iso8601($fundingTimestamp),
            'previousFundingRate' => null,
            'previousFundingTimestamp' => null,
            'previousFundingDatetime' => null,
            'interval' => null,
        );
    }

    public function fetch_funding_rate_history(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches historical funding rate prices
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-futures-history-funding-rate
             *
             * @param {string} $symbol unified $symbol of the $market to fetch the funding rate history for
             * @param {int} [$since] $timestamp in ms of the earliest funding rate to fetch
             * @param {int} [$limit] the maximum amount of ~@link https://docs.ccxt.com/#/?id=funding-rate-history-structure funding rate structures~ to fetch
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {int} [$params->fromId] the id of the $entry to start from
             * @param {int} [$params->endId] the id of the $entry to end with
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=funding-rate-history-structure funding rate structures~
             */
            Async\await($this->load_markets());
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' fetchFundingRateHistory() requires a $symbol argument');
            }
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = Async\await($this->publicGetApiV1FuturesHistoryFundingRate ($this->extend($request, $params)));
            //
            //     array(
            //         array(
            //             "id" => "10698",
            //             "symbol" => "ETHUSDT-PERPETUAL",
            //             "settleTime" => "1722268800000",
            //             "settleRate" => "0.0001"
            //         ),
            //         ...
            //     )
            //
            $rates = array();
            for ($i = 0; $i < count($response); $i++) {
                $entry = $response[$i];
                $timestamp = $this->safe_integer($entry, 'settleTime');
                $rates[] = array(
                    'info' => $entry,
                    'symbol' => $this->safe_symbol($this->safe_string($entry, 'symbol'), $market, null, 'swap'),
                    'fundingRate' => $this->safe_number($entry, 'settleRate'),
                    'timestamp' => $timestamp,
                    'datetime' => $this->iso8601($timestamp),
                );
            }
            $sorted = $this->sort_by($rates, 'timestamp');
            return $this->filter_by_since_limit($sorted, $since, $limit);
        }) ();
    }

    public function fetch_positions(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * fetch open positions for a market
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-futures-positions
             *
             * fetch all open positions
             * @param {string[]|null} $symbols list of unified market $symbols
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->side] 'LONG' or 'SHORT' - the direction of the position (if not provided, positions for both sides will be returned)
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=position-structure position structure~
             */
            $methodName = 'fetchPositions';
            if (($symbols === null)) {
                throw new ArgumentsRequired($this->id . ' ' . $methodName . '() requires a symbol argument with one single market symbol');
            } else {
                $symbolsLength = count($symbols);
                if ($symbolsLength !== 1) {
                    throw new NotSupported($this->id . ' ' . $methodName . '() is supported for a symbol argument with one single market symbol only');
                }
            }
            Async\await($this->load_markets());
            return Async\await($this->fetch_positions_for_symbol($symbols[0], $this->extend(array( 'methodName' => 'fetchPositions' ), $params)));
        }) ();
    }

    public function fetch_positions_for_symbol(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetch open positions for a single $market
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-futures-positions
             *
             * fetch all open positions for specific $symbol
             * @param {string} $symbol unified $market $symbol
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->side] 'LONG' or 'SHORT' - the direction of the position (if not provided, positions for both sides will be returned)
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=position-structure position structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $methodName = 'fetchPosition';
            list($methodName, $params) = $this->handle_param_string($params, 'methodName', $methodName);
            if (!$market['swap']) {
                throw new NotSupported($this->id . ' ' . $methodName . '() supports swap markets only');
            }
            $request = array(
                'symbol' => $market['id'],
            );
            $response = Async\await($this->privateGetApiV1FuturesPositions ($this->extend($request, $params)));
            //
            //     array(
            //         {
            //             "symbol" => "ETHUSDT-PERPETUAL",
            //             "side" => "LONG",
            //             "avgPrice" => "3327.52",
            //             "position" => "10",
            //             "available" => "0",
            //             "leverage" => "5",
            //             "lastPrice" => "3324.44",
            //             "positionValue" => "33.2752",
            //             "liquidationPrice" => "-953.83",
            //             "margin" => "6.9012",
            //             "marginRate" => "",
            //             "unrealizedPnL" => "-0.0288",
            //             "profitRate" => "-0.0041",
            //             "realizedPnL" => "-0.0199",
            //             "minMargin" => "0.2173"
            //         }
            //     )
            //
            return $this->parse_positions($response, array( $symbol ));
        }) ();
    }

    public function parse_position(array $position, ?array $market = null) {
        $marketId = $this->safe_string($position, 'symbol');
        $market = $this->safe_market($marketId, $market);
        $symbol = $market['symbol'];
        return $this->safe_position(array(
            'symbol' => $symbol,
            'id' => null,
            'timestamp' => null,
            'datetime' => null,
            'contracts' => $this->safe_number($position, 'position'),
            'contractSize' => null,
            'side' => $this->safe_string_lower($position, 'side'),
            'notional' => $this->safe_number($position, 'positionValue'),
            'leverage' => $this->safe_integer($position, 'leverage'),
            'unrealizedPnl' => $this->safe_number($position, 'unrealizedPnL'),
            'realizedPnl' => $this->safe_number($position, 'realizedPnL'),
            'collateral' => null,
            'entryPrice' => $this->safe_number($position, 'avgPrice'),
            'markPrice' => null,
            'liquidationPrice' => $this->safe_number($position, 'liquidationPrice'),
            'marginMode' => 'cross',
            'hedged' => true,
            'maintenanceMargin' => $this->safe_number($position, 'minMargin'),
            'maintenanceMarginPercentage' => null,
            'initialMargin' => $this->safe_number($position, 'margin'),
            'initialMarginPercentage' => null,
            'marginRatio' => null,
            'lastUpdateTimestamp' => null,
            'lastPrice' => $this->safe_number($position, 'lastPrice'),
            'stopLossPrice' => null,
            'takeProfitPrice' => null,
            'percentage' => null,
            'info' => $position,
        ));
    }

    public function fetch_leverage(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetch the set $leverage for a $market
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/query-futures-$leverage-trade
             *
             * @param {string} $symbol unified $market $symbol
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=$leverage-structure $leverage structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $response = Async\await($this->privateGetApiV1FuturesLeverage ($this->extend($request, $params)));
            //
            //     array(
            //         {
            //             "symbolId" => "ETHUSDT-PERPETUAL",
            //             "leverage" => "5",
            //             "marginType" => "CROSS"
            //         }
            //     )
            //
            $leverage = $this->safe_dict($response, 0, array());
            return $this->parse_leverage($leverage, $market);
        }) ();
    }

    public function parse_leverage(array $leverage, ?array $market = null): array {
        $marginMode = $this->safe_string_lower($leverage, 'marginType');
        $leverageValue = $this->safe_number($leverage, 'leverage');
        return array(
            'info' => $leverage,
            'symbol' => $market['symbol'],
            'marginMode' => $marginMode,
            'longLeverage' => $leverageValue,
            'shortLeverage' => $leverageValue,
        );
    }

    public function set_leverage(int $leverage, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($leverage, $symbol, $params) {
            /**
             * set the level of $leverage for a $market
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/change-futures-$leverage-trade
             *
             * @param {float} $leverage the rate of $leverage
             * @param {string} $symbol unified $market $symbol
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} $response from the exchange
             */
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' setLeverage() requires a $symbol argument');
            }
            Async\await($this->load_markets());
            $request = array(
                'leverage' => $leverage,
            );
            $market = $this->market($symbol);
            $request['symbol'] = $market['id'];
            $response = Async\await($this->privatePostApiV1FuturesLeverage ($this->extend($request, $params)));
            //
            //     {
            //         "code" => "0000",
            //         "symbolId" => "ETHUSDT-PERPETUAL",
            //         "leverage" => "3"
            //     }
            //
            return $this->parse_leverage($response, $market);
        }) ();
    }

    public function fetch_leverage_tiers(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes
             *
             * @see https://hashkeyglobal-apidoc.readme.io/reference/exchangeinfo
             *
             * @param {string[]|null} $symbols list of unified market $symbols
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=leverage-tiers-structure leverage tiers structures~, indexed by market $symbols
             */
            Async\await($this->load_markets());
            $response = Async\await($this->publicGetApiV1ExchangeInfo ($params));
            // $response is the same fetchMarkets()
            $data = $this->safe_list($response, 'contracts', array());
            $symbols = $this->market_symbols($symbols);
            return $this->parse_leverage_tiers($data, $symbols, 'symbol');
        }) ();
    }

    public function parse_market_leverage_tiers($info, ?array $market = null): array {
        //
        //     {
        //         "filters" => array(
        //             array(
        //                 "minPrice" => "0.1",
        //                 "maxPrice" => "100000.00000000",
        //                 "tickSize" => "0.1",
        //                 "filterType" => "PRICE_FILTER"
        //             ),
        //             array(
        //                 "minQty" => "0.001",
        //                 "maxQty" => "10",
        //                 "stepSize" => "0.001",
        //                 "marketOrderMinQty" => "0",
        //                 "marketOrderMaxQty" => "0",
        //                 "filterType" => "LOT_SIZE"
        //             ),
        //             array(
        //                 "minNotional" => "0",
        //                 "filterType" => "MIN_NOTIONAL"
        //             ),
        //             array(
        //                 "maxSellPrice" => "999999",
        //                 "buyPriceUpRate" => "0.05",
        //                 "sellPriceDownRate" => "0.05",
        //                 "maxEntrustNum" => 200,
        //                 "maxConditionNum" => 200,
        //                 "filterType" => "LIMIT_TRADING"
        //             ),
        //             array(
        //                 "buyPriceUpRate" => "0.05",
        //                 "sellPriceDownRate" => "0.05",
        //                 "filterType" => "MARKET_TRADING"
        //             ),
        //             {
        //                 "noAllowMarketStartTime" => "0",
        //                 "noAllowMarketEndTime" => "0",
        //                 "limitOrderStartTime" => "0",
        //                 "limitOrderEndTime" => "0",
        //                 "limitMinPrice" => "0",
        //                 "limitMaxPrice" => "0",
        //                 "filterType" => "OPEN_QUOTE"
        //             }
        //         ),
        //         "exchangeId" => "301",
        //         "symbol" => "BTCUSDT-PERPETUAL",
        //         "symbolName" => "BTCUSDT-PERPETUAL",
        //         "status" => "TRADING",
        //         "baseAsset" => "BTCUSDT-PERPETUAL",
        //         "baseAssetPrecision" => "0.001",
        //         "quoteAsset" => "USDT",
        //         "quoteAssetPrecision" => "0.1",
        //         "icebergAllowed" => false,
        //         "inverse" => false,
        //         "index" => "USDT",
        //         "marginToken" => "USDT",
        //         "marginPrecision" => "0.0001",
        //         "contractMultiplier" => "0.001",
        //         "underlying" => "BTC",
        //         "riskLimits" => array(
        //             array(
        //                 "riskLimitId" => "200000722",
        //                 "quantity" => "1000.00",
        //                 "initialMargin" => "0.10",
        //                 "maintMargin" => "0.005",
        //                 "isWhite" => false
        //             ),
        //             {
        //                 "riskLimitId" => "200000723",
        //                 "quantity" => "2000.00",
        //                 "initialMargin" => "0.10",
        //                 "maintMargin" => "0.01",
        //                 "isWhite" => false
        //             }
        //         )
        //     }
        //
        $riskLimits = $this->safe_list($info, 'riskLimits', array());
        $marketId = $this->safe_string($info, 'symbol');
        $market = $this->safe_market($marketId, $market);
        $tiers = array();
        for ($i = 0; $i < count($riskLimits); $i++) {
            $tier = $riskLimits[$i];
            $initialMarginRate = $this->safe_string($tier, 'initialMargin');
            $tiers[] = array(
                'tier' => $this->sum($i, 1),
                'symbol' => $this->safe_symbol($marketId, $market),
                'currency' => $market['settle'],
                'minNotional' => null,
                'maxNotional' => $this->safe_number($tier, 'quantity'),
                'maintenanceMarginRate' => $this->safe_number($tier, 'maintMargin'),
                'maxLeverage' => $this->parse_number(Precise::string_div('1', $initialMarginRate)),
                'info' => $tier,
            );
        }
        return $tiers;
    }

    public function fetch_trading_fee(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetch the trading fees for a $market
             *
             * @see https://developers.binance.com/docs/wallet/asset/trade-fee // spot
             * @see https://hashkeyglobal-apidoc.readme.io/reference/get-futures-commission-rate-request-weight // swap
             *
             * @param {string} $symbol unified $market $symbol
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=fee-structure fee structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $methodName = 'fetchTradingFee';
            $response = null;
            if ($market['spot']) {
                $response = Async\await($this->fetch_trading_fees($params));
                return $this->safe_dict($response, $symbol);
            } elseif ($market['swap']) {
                $response = Async\await($this->privateGetApiV1FuturesCommissionRate ($this->extend(array( 'symbol' => $market['id'] ), $params)));
                return $this->parse_trading_fee($response, $market);
                //
                //     {
                //         "openMakerFee" => "0.00025",
                //         "openTakerFee" => "0.0006",
                //         "closeMakerFee" => "0.00025",
                //         "closeTakerFee" => "0.0006"
                //     }
                //
            } else {
                throw new NotSupported($this->id . ' ' . $methodName . '() is not supported for ' . $market['type'] . ' type of markets');
            }
        }) ();
    }

    public function fetch_trading_fees($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * *for spot markets only* fetch the trading fees for multiple markets
             *
             * @see https://developers.binance.com/docs/wallet/asset/trade-$fee
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=$fee-structure $fee structures~ indexed by market symbols
             */
            Async\await($this->load_markets());
            $response = Async\await($this->privateGetApiV1AccountVipInfo ($params));
            //
            //     {
            //         "code" => 0,
            //         "vipLevel" => "0",
            //         "tradeVol30Day" => "67",
            //         "totalAssetBal" => "0",
            //         "data" => array(
            //             array(
            //                 "symbol" => "UXLINKUSDT",
            //                 "productType" => "Token-Token",
            //                 "buyMakerFeeCurrency" => "UXLINK",
            //                 "buyTakerFeeCurrency" => "UXLINK",
            //                 "sellMakerFeeCurrency" => "USDT",
            //                 "sellTakerFeeCurrency" => "USDT",
            //                 "actualMakerRate" => "0.0012",
            //                 "actualTakerRate" => "0.0012"
            //             ),
            //             ...
            //         ),
            //         "updateTimestamp" => "1722320137809"
            //     }
            //
            $data = $this->safe_list($response, 'data', array());
            $result = array();
            for ($i = 0; $i < count($data); $i++) {
                $fee = $this->safe_dict($data, $i, array());
                $parsedFee = $this->parse_trading_fee($fee);
                $result[$parsedFee['symbol']] = $parsedFee;
            }
            return $result;
        }) ();
    }

    public function parse_trading_fee(array $fee, ?array $market = null): array {
        //
        // spot
        //     {
        //         "symbol" => "UXLINKUSDT",
        //         "productType" => "Token-Token",
        //         "buyMakerFeeCurrency" => "UXLINK",
        //         "buyTakerFeeCurrency" => "UXLINK",
        //         "sellMakerFeeCurrency" => "USDT",
        //         "sellTakerFeeCurrency" => "USDT",
        //         "actualMakerRate" => "0.0012",
        //         "actualTakerRate" => "0.0012"
        //     }
        //
        // swap
        //     {
        //         "openMakerFee" => "0.00025",
        //         "openTakerFee" => "0.0006",
        //         "closeMakerFee" => "0.00025",
        //         "closeTakerFee" => "0.0006"
        //     }
        //
        $marketId = $this->safe_string($fee, 'symbol');
        $market = $this->safe_market($marketId, $market);
        return array(
            'info' => $fee,
            'symbol' => $market['symbol'],
            'maker' => $this->safe_number_2($fee, 'openMakerFee', 'actualMakerRate'),
            'taker' => $this->safe_number_2($fee, 'openTakerFee', 'actualTakerRate'),
            'percentage' => true,
            'tierBased' => true,
        );
    }

    public function sign($path, $api = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) {
        $url = $this->urls['api'][$api] . '/' . $path;
        $query = null;
        if ($api === 'private') {
            $this->check_required_credentials();
            $timestamp = $this->milliseconds();
            $additionalParams = array(
                'timestamp' => $timestamp,
            );
            $recvWindow = $this->safe_integer($this->options, 'recvWindow');
            if ($recvWindow !== null) {
                $additionalParams['recvWindow'] = $recvWindow;
            }
            $headers = array(
                'X-HK-APIKEY' => $this->apiKey,
                'Content-Type' => 'application/x-www-form-urlencoded',
            );
            $signature = null;
            if (($method === 'POST') && (($path === 'api/v1/spot/batchOrders') || ($path === 'api/v1/futures/batchOrders'))) {
                $headers['Content-Type'] = 'application/json';
                $body = $this->json($this->safe_list($params, 'orders'));
                $signature = $this->hmac($this->encode($this->custom_urlencode($additionalParams)), $this->encode($this->secret), 'sha256');
                $query = $this->custom_urlencode($this->extend($additionalParams, array( 'signature' => $signature )));
                $url .= '?' . $query;
            } else {
                $totalParams = $this->extend($additionalParams, $params);
                $signature = $this->hmac($this->encode($this->custom_urlencode($totalParams)), $this->encode($this->secret), 'sha256');
                $totalParams['signature'] = $signature;
                $query = $this->custom_urlencode($totalParams);
                if ($method === 'GET') {
                    $url .= '?' . $query;
                } else {
                    $body = $query;
                }
            }
            $headers['INPUT-SOURCE'] = $this->safe_string($this->options, 'broker', '10000700011');
            $headers['broker_sign'] = $signature;
        } else {
            $query = $this->urlencode($params);
            if (strlen($query) !== 0) {
                $url .= '?' . $query;
            }
        }
        return array( 'url' => $url, 'method' => $method, 'body' => $body, 'headers' => $headers );
    }

    public function custom_urlencode(array $params = array ()): ?string {
        $result = $this->urlencode($params);
        $result = str_replace('%2C', ',', $result);
        return $result;
    }

    public function handle_errors($code, $reason, $url, $method, $headers, $body, $response, $requestHeaders, $requestBody) {
        if ($response === null) {
            return null;
        }
        $errorInArray = false;
        $responseCodeString = $this->safe_string($response, 'code', null);
        $responseCodeInteger = $this->safe_integer($response, 'code', null); // some codes in $response are returned as '0000' others
        if ($responseCodeInteger === 0) {
            $result = $this->safe_list($response, 'result', array()); // for batch methods
            for ($i = 0; $i < count($result); $i++) {
                $entry = $this->safe_dict($result, $i);
                $entryCodeInteger = $this->safe_integer($entry, 'code');
                if ($entryCodeInteger !== 0) {
                    $errorInArray = true;
                    $responseCodeString = $this->safe_string($entry, 'code');
                }
            }
        }
        if (($code !== 200) || $errorInArray) {
            $feedback = $this->id . ' ' . $body;
            $this->throw_broadly_matched_exception($this->exceptions['broad'], $responseCodeString, $feedback);
            $this->throw_exactly_matched_exception($this->exceptions['exact'], $responseCodeString, $feedback);
            throw new ExchangeError($feedback);
        }
        return null;
    }
}
