<?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\hitbtc as Exchange;
use ccxt\ExchangeError;
use ccxt\ArgumentsRequired;
use ccxt\BadRequest;
use ccxt\BadSymbol;
use ccxt\InvalidOrder;
use ccxt\NotSupported;
use ccxt\Precise;
use \React\Async;
use \React\Promise\PromiseInterface;

class hitbtc extends Exchange {

    public function describe(): mixed {
        return $this->deep_extend(parent::describe(), array(
            'id' => 'hitbtc',
            'name' => 'HitBTC',
            'countries' => array( 'HK' ),
            // 300 requests per second => 1000ms / 300 = 3.333 (Trading => placing, replacing, deleting)
            // 30 requests per second => ( 1000ms / rateLimit ) / 30 = cost = 10 (Market Data and other Public Requests)
            // 20 requests per second => ( 1000ms / rateLimit ) / 20 = cost = 15 (All Other)
            'rateLimit' => 3.333, // TODO => optimize https://api.hitbtc.com/#rate-limiting
            'version' => '3',
            'has' => array(
                'CORS' => false,
                'spot' => true,
                'margin' => true,
                'swap' => true,
                'future' => false,
                'option' => false,
                'addMargin' => true,
                'cancelAllOrders' => true,
                'cancelOrder' => true,
                'closePosition' => false,
                'createDepositAddress' => true,
                'createOrder' => true,
                'createPostOnlyOrder' => true,
                'createReduceOnlyOrder' => true,
                'createStopLimitOrder' => true,
                'createStopMarketOrder' => true,
                'createStopOrder' => true,
                'editOrder' => true,
                'fetchAccounts' => false,
                'fetchBalance' => true,
                'fetchBorrowRateHistories' => null,
                'fetchBorrowRateHistory' => null,
                'fetchClosedOrders' => true,
                'fetchCrossBorrowRate' => false,
                'fetchCrossBorrowRates' => false,
                'fetchCurrencies' => true,
                'fetchDepositAddress' => true,
                'fetchDepositAddresses' => false,
                'fetchDepositAddressesByNetwork' => false,
                'fetchDeposits' => true,
                'fetchDepositsWithdrawals' => true,
                'fetchDepositWithdrawFee' => 'emulated',
                'fetchDepositWithdrawFees' => true,
                'fetchFundingHistory' => null,
                'fetchFundingRate' => true,
                'fetchFundingRateHistory' => true,
                'fetchFundingRates' => true,
                'fetchGreeks' => false,
                'fetchIndexOHLCV' => true,
                'fetchIsolatedBorrowRate' => false,
                'fetchIsolatedBorrowRates' => false,
                'fetchLeverage' => true,
                'fetchLeverageTiers' => null,
                'fetchLiquidations' => false,
                'fetchMarginMode' => 'emulated',
                'fetchMarginModes' => true,
                'fetchMarketLeverageTiers' => false,
                'fetchMarkets' => true,
                'fetchMarkOHLCV' => true,
                'fetchMyLiquidations' => false,
                'fetchMySettlementHistory' => false,
                'fetchMyTrades' => true,
                'fetchOHLCV' => true,
                'fetchOpenInterest' => true,
                'fetchOpenInterestHistory' => false,
                'fetchOpenInterests' => true,
                'fetchOpenOrder' => true,
                'fetchOpenOrders' => true,
                'fetchOption' => false,
                'fetchOptionChain' => false,
                'fetchOrder' => true,
                'fetchOrderBook' => true,
                'fetchOrderBooks' => true,
                'fetchOrders' => false,
                'fetchOrderTrades' => true,
                'fetchPosition' => true,
                'fetchPositions' => true,
                'fetchPremiumIndexOHLCV' => true,
                'fetchSettlementHistory' => false,
                'fetchTicker' => true,
                'fetchTickers' => true,
                'fetchTrades' => true,
                'fetchTradingFee' => true,
                'fetchTradingFees' => true,
                'fetchTransactions' => 'emulated',
                'fetchVolatilityHistory' => false,
                'fetchWithdrawals' => true,
                'reduceMargin' => true,
                'sandbox' => true,
                'setLeverage' => true,
                'setMargin' => false,
                'setMarginMode' => false,
                'setPositionMode' => false,
                'transfer' => true,
                'withdraw' => true,
            ),
            'precisionMode' => TICK_SIZE,
            'urls' => array(
                'logo' => 'https://user-images.githubusercontent.com/1294454/27766555-8eaec20e-5edc-11e7-9c5b-6dc69fc42f5e.jpg',
                'test' => array(
                    'public' => 'https://api.demo.hitbtc.com/api/3',
                    'private' => 'https://api.demo.hitbtc.com/api/3',
                ),
                'api' => array(
                    'public' => 'https://api.hitbtc.com/api/3',
                    'private' => 'https://api.hitbtc.com/api/3',
                ),
                'www' => 'https://hitbtc.com',
                'referral' => 'https://hitbtc.com/?ref_id=5a5d39a65d466',
                'doc' => array(
                    'https://api.hitbtc.com',
                    'https://github.com/hitbtc-com/hitbtc-api/blob/master/APIv2.md',
                ),
                'fees' => array(
                    'https://hitbtc.com/fees-and-limits',
                    'https://support.hitbtc.com/hc/en-us/articles/115005148605-Fees-and-limits',
                ),
            ),
            'api' => array(
                'public' => array(
                    'get' => array(
                        'public/currency' => 10,
                        'public/currency/{currency}' => 10,
                        'public/symbol' => 10,
                        'public/symbol/{symbol}' => 10,
                        'public/ticker' => 10,
                        'public/ticker/{symbol}' => 10,
                        'public/price/rate' => 10,
                        'public/price/history' => 10,
                        'public/price/ticker' => 10,
                        'public/price/ticker/{symbol}' => 10,
                        'public/trades' => 10,
                        'public/trades/{symbol}' => 10,
                        'public/orderbook' => 10,
                        'public/orderbook/{symbol}' => 10,
                        'public/candles' => 10,
                        'public/candles/{symbol}' => 10,
                        'public/converted/candles' => 10,
                        'public/converted/candles/{symbol}' => 10,
                        'public/futures/info' => 10,
                        'public/futures/info/{symbol}' => 10,
                        'public/futures/history/funding' => 10,
                        'public/futures/history/funding/{symbol}' => 10,
                        'public/futures/candles/index_price' => 10,
                        'public/futures/candles/index_price/{symbol}' => 10,
                        'public/futures/candles/mark_price' => 10,
                        'public/futures/candles/mark_price/{symbol}' => 10,
                        'public/futures/candles/premium_index' => 10,
                        'public/futures/candles/premium_index/{symbol}' => 10,
                        'public/futures/candles/open_interest' => 10,
                        'public/futures/candles/open_interest/{symbol}' => 10,
                    ),
                ),
                'private' => array(
                    'get' => array(
                        'spot/balance' => 15,
                        'spot/balance/{currency}' => 15,
                        'spot/order' => 1,
                        'spot/order/{client_order_id}' => 1,
                        'spot/fee' => 15,
                        'spot/fee/{symbol}' => 15,
                        'spot/history/order' => 15,
                        'spot/history/trade' => 15,
                        'margin/account' => 1,
                        'margin/account/isolated/{symbol}' => 1,
                        'margin/account/cross/{currency}' => 1,
                        'margin/order' => 1,
                        'margin/order/{client_order_id}' => 1,
                        'margin/config' => 15,
                        'margin/history/order' => 15,
                        'margin/history/trade' => 15,
                        'margin/history/positions' => 15,
                        'margin/history/clearing' => 15,
                        'futures/balance' => 15,
                        'futures/balance/{currency}' => 15,
                        'futures/account' => 1,
                        'futures/account/isolated/{symbol}' => 1,
                        'futures/order' => 1,
                        'futures/order/{client_order_id}' => 1,
                        'futures/config' => 15,
                        'futures/fee' => 15,
                        'futures/fee/{symbol}' => 15,
                        'futures/history/order' => 15,
                        'futures/history/trade' => 15,
                        'futures/history/positions' => 15,
                        'futures/history/clearing' => 15,
                        'wallet/balance' => 30,
                        'wallet/balance/{currency}' => 30,
                        'wallet/crypto/address' => 30,
                        'wallet/crypto/address/recent-deposit' => 30,
                        'wallet/crypto/address/recent-withdraw' => 30,
                        'wallet/crypto/address/check-mine' => 30,
                        'wallet/transactions' => 30,
                        'wallet/transactions/{tx_id}' => 30,
                        'wallet/crypto/fee/estimate' => 30,
                        'wallet/airdrops' => 30,
                        'wallet/amount-locks' => 30,
                        'sub-account' => 15,
                        'sub-account/acl' => 15,
                        'sub-account/balance/{subAccID}' => 15,
                        'sub-account/crypto/address/{subAccID}/{currency}' => 15,
                    ),
                    'post' => array(
                        'spot/order' => 1,
                        'spot/order/list' => 1,
                        'margin/order' => 1,
                        'margin/order/list' => 1,
                        'futures/order' => 1,
                        'futures/order/list' => 1,
                        'wallet/crypto/address' => 30,
                        'wallet/crypto/withdraw' => 30,
                        'wallet/convert' => 30,
                        'wallet/transfer' => 30,
                        'wallet/internal/withdraw' => 30,
                        'wallet/crypto/check-offchain-available' => 30,
                        'wallet/crypto/fees/estimate' => 30,
                        'wallet/airdrops/{id}/claim' => 30,
                        'sub-account/freeze' => 15,
                        'sub-account/activate' => 15,
                        'sub-account/transfer' => 15,
                        'sub-account/acl' => 15,
                    ),
                    'patch' => array(
                        'spot/order/{client_order_id}' => 1,
                        'margin/order/{client_order_id}' => 1,
                        'futures/order/{client_order_id}' => 1,
                    ),
                    'delete' => array(
                        'spot/order' => 1,
                        'spot/order/{client_order_id}' => 1,
                        'margin/position' => 1,
                        'margin/position/isolated/{symbol}' => 1,
                        'margin/order' => 1,
                        'margin/order/{client_order_id}' => 1,
                        'futures/position' => 1,
                        'futures/position/{margin_mode}/{symbol}' => 1,
                        'futures/order' => 1,
                        'futures/order/{client_order_id}' => 1,
                        'wallet/crypto/withdraw/{id}' => 30,
                    ),
                    'put' => array(
                        'margin/account/isolated/{symbol}' => 1,
                        'futures/account/isolated/{symbol}' => 1,
                        'wallet/crypto/withdraw/{id}' => 30,
                    ),
                ),
            ),
            'fees' => array(
                'trading' => array(
                    'tierBased' => true,
                    'percentage' => true,
                    'taker' => $this->parse_number('0.0009'),
                    'maker' => $this->parse_number('0.0009'),
                    'tiers' => array(
                        'maker' => array(
                            array( $this->parse_number('0'), $this->parse_number('0.0009') ),
                            array( $this->parse_number('10'), $this->parse_number('0.0007') ),
                            array( $this->parse_number('100'), $this->parse_number('0.0006') ),
                            array( $this->parse_number('500'), $this->parse_number('0.0005') ),
                            array( $this->parse_number('1000'), $this->parse_number('0.0003') ),
                            array( $this->parse_number('5000'), $this->parse_number('0.0002') ),
                            array( $this->parse_number('10000'), $this->parse_number('0.0001') ),
                            array( $this->parse_number('20000'), $this->parse_number('0') ),
                            array( $this->parse_number('50000'), $this->parse_number('-0.0001') ),
                            array( $this->parse_number('100000'), $this->parse_number('-0.0001') ),
                        ),
                        'taker' => array(
                            array( $this->parse_number('0'), $this->parse_number('0.0009') ),
                            array( $this->parse_number('10'), $this->parse_number('0.0008') ),
                            array( $this->parse_number('100'), $this->parse_number('0.0007') ),
                            array( $this->parse_number('500'), $this->parse_number('0.0007') ),
                            array( $this->parse_number('1000'), $this->parse_number('0.0006') ),
                            array( $this->parse_number('5000'), $this->parse_number('0.0006') ),
                            array( $this->parse_number('10000'), $this->parse_number('0.0005') ),
                            array( $this->parse_number('20000'), $this->parse_number('0.0004') ),
                            array( $this->parse_number('50000'), $this->parse_number('0.0003') ),
                            array( $this->parse_number('100000'), $this->parse_number('0.0002') ),
                        ),
                    ),
                ),
            ),
            'features' => array(
                'default' => array(
                    'sandbox' => true,
                    'createOrder' => array(
                        'marginMode' => false,
                        'triggerPrice' => true,
                        'triggerPriceType' => null,
                        'triggerDirection' => false,
                        'stopLossPrice' => false, // todo
                        'takeProfitPrice' => false, // todo
                        'attachedStopLossTakeProfit' => null,
                        'timeInForce' => array(
                            'IOC' => true,
                            'FOK' => true,
                            'PO' => true,
                            'GTD' => true,
                        ),
                        'hedged' => false,
                        'selfTradePrevention' => false,
                        'trailing' => false,
                        'leverage' => false,
                        'marketBuyByCost' => false,
                        'marketBuyRequiresPrice' => false,
                        'iceberg' => true,
                    ),
                    'createOrders' => null,
                    'fetchMyTrades' => array(
                        'marginMode' => true,
                        'limit' => 1000,
                        'daysBack' => 100000,
                        'untilDays' => 100000,
                        'symbolRequired' => false,
                        'marketType' => true,
                    ),
                    'fetchOrder' => array(
                        'marginMode' => true,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => false,
                        'marketType' => true,
                    ),
                    'fetchOpenOrders' => array(
                        'marginMode' => true,
                        'limit' => 1000,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => false,
                        'marketType' => true,
                    ),
                    'fetchOrders' => null,
                    'fetchClosedOrders' => array(
                        'marginMode' => true,
                        'limit' => 1000,
                        'daysBack' => 100000, // todo
                        'daysBackCanceled' => 1, // todo
                        'untilDays' => 100000, // todo
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => false,
                        'marketType' => true,
                    ),
                    'fetchOHLCV' => array(
                        'limit' => 1000,
                    ),
                ),
                'spot' => array(
                    'extends' => 'default',
                ),
                'forDerivatives' => array(
                    'extends' => 'default',
                    'createOrder' => array(
                        'marginMode' => true,
                    ),
                    'fetchOrder' => array(
                        'marginMode' => false,
                    ),
                    'fetchMyTrades' => array(
                        'marginMode' => false,
                    ),
                    'fetchOpenOrders' => array(
                        'marginMode' => false,
                    ),
                    'fetchClosedOrders' => array(
                        'marginMode' => false,
                    ),
                ),
                'swap' => array(
                    'linear' => array(
                        'extends' => 'forDerivatives',
                    ),
                    'inverse' => array(
                        'extends' => 'forDerivatives',
                    ),
                ),
                'future' => array(
                    'linear' => array(
                        'extends' => 'forDerivatives',
                    ),
                    'inverse' => array(
                        'extends' => 'forDerivatives',
                    ),
                ),
            ),
            'timeframes' => array(
                '1m' => 'M1',
                '3m' => 'M3',
                '5m' => 'M5',
                '15m' => 'M15',
                '30m' => 'M30', // default
                '1h' => 'H1',
                '4h' => 'H4',
                '1d' => 'D1',
                '1w' => 'D7',
                '1M' => '1M',
            ),
            'exceptions' => array(
                'exact' => array(
                    '429' => '\\ccxt\\RateLimitExceeded',
                    '500' => '\\ccxt\\ExchangeError',
                    '503' => '\\ccxt\\ExchangeNotAvailable',
                    '504' => '\\ccxt\\ExchangeNotAvailable',
                    '600' => '\\ccxt\\PermissionDenied',
                    '800' => '\\ccxt\\ExchangeError',
                    '1002' => '\\ccxt\\AuthenticationError',
                    '1003' => '\\ccxt\\PermissionDenied',
                    '1004' => '\\ccxt\\AuthenticationError',
                    '1005' => '\\ccxt\\AuthenticationError',
                    '2001' => '\\ccxt\\BadSymbol',
                    '2002' => '\\ccxt\\BadRequest',
                    '2003' => '\\ccxt\\BadRequest',
                    '2010' => '\\ccxt\\BadRequest',
                    '2011' => '\\ccxt\\BadRequest',
                    '2012' => '\\ccxt\\BadRequest',
                    '2020' => '\\ccxt\\BadRequest',
                    '2022' => '\\ccxt\\BadRequest',
                    '2024' => '\\ccxt\\InvalidOrder', // Invalid margin mode.
                    '10001' => '\\ccxt\\BadRequest',
                    '10021' => '\\ccxt\\AccountSuspended',
                    '10022' => '\\ccxt\\BadRequest',
                    '20001' => '\\ccxt\\InsufficientFunds',
                    '20002' => '\\ccxt\\OrderNotFound',
                    '20003' => '\\ccxt\\ExchangeError',
                    '20004' => '\\ccxt\\ExchangeError',
                    '20005' => '\\ccxt\\ExchangeError',
                    '20006' => '\\ccxt\\ExchangeError',
                    '20007' => '\\ccxt\\ExchangeError',
                    '20008' => '\\ccxt\\InvalidOrder',
                    '20009' => '\\ccxt\\InvalidOrder',
                    '20010' => '\\ccxt\\OnMaintenance',
                    '20011' => '\\ccxt\\ExchangeError',
                    '20012' => '\\ccxt\\ExchangeError',
                    '20014' => '\\ccxt\\ExchangeError',
                    '20016' => '\\ccxt\\ExchangeError',
                    '20018' => '\\ccxt\\ExchangeError', // Withdrawals are unavailable due to the current configuration. Any of => - internal withdrawals are disabled; - in-chain withdrawals are disabled.
                    '20031' => '\\ccxt\\ExchangeError',
                    '20032' => '\\ccxt\\ExchangeError',
                    '20033' => '\\ccxt\\ExchangeError',
                    '20034' => '\\ccxt\\ExchangeError',
                    '20040' => '\\ccxt\\ExchangeError',
                    '20041' => '\\ccxt\\ExchangeError',
                    '20042' => '\\ccxt\\ExchangeError',
                    '20043' => '\\ccxt\\ExchangeError',
                    '20044' => '\\ccxt\\PermissionDenied',
                    '20045' => '\\ccxt\\InvalidOrder',
                    '20047' => '\\ccxt\\InvalidOrder', // Order placing exceeds the central counterparty balance limit.
                    '20048' => '\\ccxt\\InvalidOrder', // Provided Time-In-Force instruction is invalid or the combination of the instruction and the order type is not allowed.
                    '20049' => '\\ccxt\\InvalidOrder', // Provided order type is invalid.
                    '20080' => '\\ccxt\\ExchangeError',
                    '21001' => '\\ccxt\\ExchangeError',
                    '21003' => '\\ccxt\\AccountSuspended',
                    '21004' => '\\ccxt\\AccountSuspended',
                    '22004' => '\\ccxt\\ExchangeError', // User is not found.
                    '22008' => '\\ccxt\\ExchangeError', // Gateway timeout exceeded.
                ),
                'broad' => array(),
            ),
            'options' => array(
                'defaultNetwork' => 'ERC20',
                'defaultNetworks' => array(
                    'ETH' => 'ETH',
                    'USDT' => 'TRC20',
                ),
                'networks' => array(
                    // mainnet network ids are in lowercase for BTC & ETH
                    'BTC' => 'btc',
                    'OMNI' => 'BTC',
                    'ETH' => 'eth',
                    'ERC20' => 'ETH',
                    'ETC' => 'ETC',
                    'BEP20' => 'BSC',
                    'TRC20' => 'TRX',
                    // '' => 'UGT',
                    'NEAR' => 'NEAR',
                    // '' => 'LWF',
                    'DGB' => 'DGB',
                    // '' => 'YOYOW',
                    'AE' => 'AE',
                    // 'BCHABC' => 'BCHABC',
                    // '' => 'BCI',
                    // 'BYTECOIN' => 'bcn',
                    'AR' => 'AR',
                    // '' => 'HPC',
                    'ADA' => 'ADA',
                    // 'BELDEX' => 'BDX',
                    // 'ARDOR' => 'ARDR',
                    // 'NEBLIO' => 'NEBL',
                    // '' => 'DIM',
                    'CHZ' => 'CHZ',
                    // '' => 'BET',
                    // '' => '8BT',
                    'ABBC' => 'ABBC',
                    // '' => 'ABTC',
                    // 'ACHAIN' => 'ACT',
                    // '' => 'ADK',
                    // '' => 'AEON',
                    'ALGO' => 'ALGO',
                    // 'AMBROSUS' => 'AMB',
                    // '' => 'APL',
                    'APT' => 'APT',
                    // '' => 'ARK',
                    // 'PIRATECHAIN' => 'ARRR',
                    // '' => 'ASP',
                    // '' => 'ATB',
                    'ATOM' => 'ATOM',
                    'AVAXC' => 'AVAC',
                    'AVAXX' => 'AVAX',
                    // '' => 'AYA',
                    // '' => 'B2G',
                    // '' => 'B2X',
                    // '' => 'BANANO',
                    // '' => 'BCCF',
                    'BSV' => 'BCHSV',
                    'BEP2' => 'BNB',
                    // 'BOSON' => 'BOS',
                    // '' => 'BRL', // brazilian real
                    // '' => 'BST',
                    // 'BITCOINADDITION' => 'BTCADD',
                    // '' => 'BTCP',
                    // 'SUPERBTC' => 'SBTC',
                    // 'BITCOINVAULT' => 'BTCV',
                    // 'BITCOINGOLD' => 'BTG',
                    // 'BITCOINDIAMOND' => 'BCD',
                    // 'BITCONNECT' => 'BCC',
                    // '' => 'BTM',
                    // 'BITSHARES_OLD' => 'BTS',
                    // '' => 'BTX',
                    // '' => 'BWI',
                    'CELO' => 'CELO',
                    // '' => 'CENNZ',
                    // '' => 'CHX',
                    'CKB' => 'CKB',
                    // 'CALLISTO' => 'CLO',
                    // '' => 'CLR',
                    // '' => 'CNX',
                    // '' => 'CRS',
                    // '' => 'CSOV',
                    'CTXC' => 'CTXC',
                    // '' => 'CURE',
                    // 'CONSTELLATION' => 'DAG',
                    // '' => 'DAPS',
                    'DASH' => 'DASH',
                    // '' => 'DBIX',
                    'DCR' => 'DCR',
                    // '' => 'DCT',
                    // '' => 'DDR',
                    // '' => 'DNA',
                    'DOGE' => 'doge',
                    // 'POLKADOT' => 'DOT',
                    // '' => 'NEWDOT', POLKADOT NEW
                    // '' => 'dsh',
                    // '' => 'ECA',
                    // '' => 'ECOIN',
                    // '' => 'EEX',
                    'EGLD' => 'EGLD',
                    // '' => 'ELE',
                    // 'ELECTRONEUM' => 'Electroneum',
                    // '' => 'ELM',
                    // '' => 'EMC',
                    'EOS' => 'EOS',
                    // 'AERGO' => 'ERG',
                    'ETHW' => 'ETHW',
                    // 'ETHERLITE' => 'ETL',
                    // '' => 'ETP', // metaverse etp
                    // '' => 'EUNO',
                    'EVER' => 'EVER',
                    // '' => 'EXP',
                    // '' => 'fcn',
                    'FET' => 'FET',
                    'FIL' => 'FIL',
                    // '' => 'FIRO',
                    'FLOW' => 'FLOW',
                    // '' => 'G999',
                    // '' => 'GAME',
                    // '' => 'GASP',
                    // '' => 'GBX',
                    // '' => 'GHOST',
                    // '' => 'GLEEC',
                    'GLMR' => 'GLMR',
                    // '' => 'GMD',
                    // '' => 'GRAPH',
                    'GRIN' => 'GRIN',
                    'HBAR' => 'HBAR',
                    // '' => 'HDG',
                    'HIVE' => 'HIVE',
                    // 'HARBOR' => 'HRB',
                    // '' => 'HSR',
                    // '' => 'HTML',
                    'HYDRA' => 'HYDRA',
                    'ICP' => 'ICP',
                    'ICX' => 'ICX',
                    // '' => 'IML',
                    'IOST' => 'IOST',
                    'IOTA' => 'IOTA',
                    'IOTX' => 'IOTX',
                    // '' => 'IQ',
                    'KAVA' => 'KAVA',
                    'KLAY' => 'KIM',
                    'KOMODO' => 'KMD',
                    // '' => 'KRM',
                    'KSM' => 'KSM',
                    // '' => 'LAVA',
                    // 'LITECOINCASH' => 'LCC',
                    'LSK' => 'LSK',
                    // '' => 'LOC',
                    'LTC' => 'ltc',
                    // '' => 'LTNM',
                    // 'TERRACLASSIC' => 'LUNA',
                    // 'TERRA' => 'LUNANEW',
                    // '' => 'MAN',
                    // '' => 'MESH',
                    'MINA' => 'MINA',
                    // '' => 'MNX',
                    // 'MOBILECOIN' => 'MOB',
                    'MOVR' => 'MOVR',
                    // '' => 'MPK',
                    // '' => 'MRV',
                    'NANO' => 'NANO',
                    // '' => 'NAV',
                    'NEO' => 'NEO',
                    // 'NIMIQ' => 'NIM',
                    // '' => 'NJBC',
                    // '' => 'NKN',
                    // '' => 'NLC2',
                    // '' => 'NOF',
                    // 'ENERGI' => 'NRG',
                    // '' => 'nxt',
                    // '' => 'ODN',
                    'ONE' => 'ONE',
                    // 'ONTOLOGYGAS' => 'ONG',
                    'ONT' => 'ONT',
                    'OPTIMISM' => 'OP',
                    // '' => 'PAD',
                    // '' => 'PART',
                    // '' => 'PBKX',
                    // '' => 'PLC',
                    'PLCU' => 'PLCU',
                    // '' => 'PLI',
                    // '' => 'POA',
                    'MATIC' => 'POLYGON',
                    // '' => 'PPC',
                    // '' => 'PQT',
                    // '' => 'PROC',
                    // 'PASTEL' => 'PSL',
                    // '' => 'qcn',
                    'QTUM' => 'QTUM',
                    // '' => 'RCOIN',
                    'REI' => 'REI',
                    // '' => 'RIF',
                    // '' => 'ROOTS',
                    'OASIS' => 'ROSE',
                    // '' => 'RPX',
                    // '' => 'RUB',
                    'RVN' => 'RVN',
                    // '' => 'SBD',
                    'SC' => 'SC',
                    'SCRT' => 'SCRT',
                    // '' => 'SLX',
                    // 'SMARTMESH' => 'SMART',
                    // '' => 'SMT',
                    // '' => 'SNM',
                    'SOL' => 'SOL',
                    // '' => 'SRX',
                    // '' => 'STAK',
                    'STEEM' => 'STEEM',
                    // 'STRATIS' => 'STRAT',
                    // '' => 'TCN',
                    // '' => 'TENT',
                    'THETA' => 'Theta',
                    // '' => 'TIV',
                    // '' => 'TNC',
                    // 'TON' => 'TONCOIN',
                    'TRUE' => 'TRUE',
                    // '' => 'TRY', // turkish lira
                    // '' => 'UNO',
                    // '' => 'USNOTA',
                    // '' => 'VEO',
                    'VET' => 'VET',
                    // '' => 'VITAE',
                    // 'VELAS' => 'VLX',
                    'VSYS' => 'VSYS',
                    // '' => 'VTC',
                    'WAVES' => 'WAVES',
                    'WAX' => 'WAX',
                    // '' => 'WEALTH',
                    // 'WALTONCHAIN' => 'WTC',
                    // '' => 'WTT',
                    'XCH' => 'XCH',
                    // '' => 'XDC', // xinfin?
                    // '' => 'xdn',
                    // '' => 'XDNCO',
                    // '' => 'XDNICCO',
                    'XEC' => 'XEC',
                    'NEM' => 'XEM',
                    // 'HAVEN' => 'XHV',
                    // '' => 'XLC',
                    'XLM' => 'XLM',
                    // '' => 'XMO',
                    'XMR' => 'xmr',
                    // 'MONEROCLASSIC' => 'XMC',
                    // '' => 'XNS',
                    // '' => 'XPRM',
                    // '' => 'XRC',
                    'XRD' => 'XRD',
                    'XRP' => 'XRP',
                    'XTZ' => 'XTZ',
                    'XVG' => 'XVG',
                    'XYM' => 'XYM',
                    'ZEC' => 'ZEC',
                    'ZEN' => 'ZEN',
                    'ZIL' => 'ZIL',
                    // '' => 'ZYN',
                ),
                'accountsByType' => array(
                    'spot' => 'spot',
                    'funding' => 'wallet',
                    'swap' => 'derivatives',
                    'future' => 'derivatives',
                ),
                'withdraw' => array(
                    'includeFee' => false,
                ),
            ),
            'commonCurrencies' => array(
                'AUTO' => 'Cube',
                'BCC' => 'BCC', // initial symbol for Bitcoin Cash, now inactive
                'BDP' => 'BidiPass',
                'BET' => 'DAO.Casino',
                'BIT' => 'BitRewards',
                'BOX' => 'BOX Token',
                'CPT' => 'Cryptaur', // conflict with CPT = Contents Protocol https://github.com/ccxt/ccxt/issues/4920 and https://github.com/ccxt/ccxt/issues/6081
                'GET' => 'Themis',
                'GMT' => 'GMT Token',
                'HSR' => 'HC',
                'IQ' => 'IQ.Cash',
                'LNC' => 'LinkerCoin',
                'PLA' => 'PlayChip',
                'PNT' => 'Penta',
                'SBTC' => 'Super Bitcoin',
                'STEPN' => 'GMT',
                'STX' => 'STOX',
                'TV' => 'Tokenville',
                'XMT' => 'MTL',
                'XPNT' => 'PNT',
            ),
        ));
    }

    public function nonce() {
        return $this->milliseconds();
    }

    public function fetch_markets($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * retrieves data on all markets for hitbtc
             *
             * @see https://api.hitbtc.com/#symbols
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} an array of objects representing $market data
             */
            $response = Async\await($this->publicGetPublicSymbol ($params));
            //
            //     {
            //         "AAVEUSDT_PERP":array(
            //             "type":"futures",
            //             "expiry":null,
            //             "underlying":"AAVE",
            //             "base_currency":null,
            //             "quote_currency":"USDT",
            //             "quantity_increment":"0.01",
            //             "tick_size":"0.001",
            //             "take_rate":"0.0005",
            //             "make_rate":"0.0002",
            //             "fee_currency":"USDT",
            //             "margin_trading":true,
            //             "max_initial_leverage":"50.00"
            //         ),
            //         "MANAUSDT":array(
            //             "type":"spot",
            //             "base_currency":"MANA",
            //             "quote_currency":"USDT",
            //             "quantity_increment":"1",
            //             "tick_size":"0.0000001",
            //             "take_rate":"0.0025",
            //             "make_rate":"0.001",
            //             "fee_currency":"USDT",
            //             "margin_trading":true,
            //             "max_initial_leverage":"5.00"
            //         ),
            //     }
            //
            $result = array();
            $ids = is_array($response) ? array_keys($response) : array();
            for ($i = 0; $i < count($ids); $i++) {
                $id = $ids[$i];
                if (str_ends_with($id, '_BQX')) {
                    continue; // seems like an invalid $symbol and if we try to access it individually we get => array("timestamp":"2023-09-02T14:38:20.351Z","error":array("description":"Try get /public/symbol, to get list of all available symbols.","code":2001,"message":"No such $symbol => EOSUSD_BQX"),"path":"/api/3/public/symbol/EOSUSD_BQX","requestId":"e1e9fce6-16374591")
                }
                $market = $this->safe_value($response, $id);
                $marketType = $this->safe_string($market, 'type');
                $expiry = $this->safe_integer($market, 'expiry');
                $contract = ($marketType === 'futures');
                $spot = ($marketType === 'spot');
                $marginTrading = $this->safe_bool($market, 'margin_trading', false);
                $margin = $spot && $marginTrading;
                $future = ($expiry !== null);
                $swap = ($contract && !$future);
                $option = false;
                $baseId = $this->safe_string_2($market, 'base_currency', 'underlying');
                $quoteId = $this->safe_string($market, 'quote_currency');
                $feeCurrencyId = $this->safe_string($market, 'fee_currency');
                $base = $this->safe_currency_code($baseId);
                $quote = $this->safe_currency_code($quoteId);
                $feeCurrency = $this->safe_currency_code($feeCurrencyId);
                $settleId = null;
                $settle = null;
                $symbol = $base . '/' . $quote;
                $type = 'spot';
                $contractSize = null;
                $linear = null;
                $inverse = null;
                if ($contract) {
                    $contractSize = $this->parse_number('1');
                    $settleId = $feeCurrencyId;
                    $settle = $this->safe_currency_code($settleId);
                    $linear = (($quote !== null) && ($quote === $settle));
                    $inverse = !$linear;
                    $symbol = $symbol . ':' . $settle;
                    if ($future) {
                        $symbol = $symbol . '-' . $expiry;
                        $type = 'future';
                    } else {
                        $type = 'swap';
                    }
                }
                $lotString = $this->safe_string($market, 'quantity_increment');
                $stepString = $this->safe_string($market, 'tick_size');
                $lot = $this->parse_number($lotString);
                $step = $this->parse_number($stepString);
                $result[] = array(
                    'id' => $id,
                    'symbol' => $symbol,
                    'base' => $base,
                    'quote' => $quote,
                    'settle' => $settle,
                    'baseId' => $baseId,
                    'quoteId' => $quoteId,
                    'settleId' => $settleId,
                    'type' => $type,
                    'spot' => $spot,
                    'margin' => $margin,
                    'swap' => $swap,
                    'future' => $future,
                    'option' => $option,
                    'active' => true,
                    'contract' => $contract,
                    'linear' => $linear,
                    'inverse' => $inverse,
                    'taker' => $this->safe_number($market, 'take_rate'),
                    'maker' => $this->safe_number($market, 'make_rate'),
                    'contractSize' => $contractSize,
                    'expiry' => $expiry,
                    'expiryDatetime' => null,
                    'strike' => null,
                    'optionType' => null,
                    'feeCurrency' => $feeCurrency,
                    'precision' => array(
                        'amount' => $lot,
                        'price' => $step,
                    ),
                    'limits' => array(
                        'leverage' => array(
                            'min' => $this->parse_number('1'),
                            'max' => $this->safe_number($market, 'max_initial_leverage', 1),
                        ),
                        'amount' => array(
                            'min' => $lot,
                            'max' => null,
                        ),
                        'price' => array(
                            'min' => $step,
                            'max' => null,
                        ),
                        'cost' => array(
                            'min' => $this->parse_number(Precise::string_mul($lotString, $stepString)),
                            'max' => null,
                        ),
                    ),
                    'created' => null,
                    'info' => $market,
                );
            }
            return $result;
        }) ();
    }

    public function fetch_currencies($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * fetches all available $currencies on an exchange
             *
             * @see https://api.hitbtc.com/#$currencies
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an associative dictionary of $currencies
             */
            $response = Async\await($this->publicGetPublicCurrency ($params));
            //
            //    {
            //        "DFC" => {
            //            "full_name" => "DeFiScale",
            //            "crypto" => true,
            //            "payin_enabled" => false,
            //            "payout_enabled" => true,
            //            "transfer_enabled" => false,
            //            "transfer_to_wallet_enabled" => true,
            //            "transfer_to_exchange_enabled" => false,
            //            "sign" => "D",
            //            "crypto_payment_id_name" => "",
            //            "crypto_explorer" => "https://etherscan.io/tx/{tx}",
            //            "precision_transfer" => "0.00000001",
            //            "delisted" => false,
            //            "networks" => array(
            //                {
            //                    "code" => "ETH",
            //                    "network_name" => "Ethereum",
            //                    "network" => "ETH",
            //                    "protocol" => "ERC-20",
            //                    "default" => true,
            //                    "is_ens_available" => true,
            //                    "payin_enabled" => true,
            //                    "payout_enabled" => true,
            //                    "precision_payout" => "0.000000000000000001",
            //                    "payout_fee" => "277000.0000000000",
            //                    "payout_is_payment_id" => false,
            //                    "payin_payment_id" => false,
            //                    "payin_confirmations" => "2",
            //                    "contract_address" => "0x1b2a76da77d03b7fc21189d9838f55bd849014af",
            //                    "crypto_payment_id_name" => "",
            //                    "crypto_explorer" => "https://etherscan.io/tx/{tx}",
            //                    "is_multichain" => true,
            //                    "asset_id" => array(
            //                        "contract_address" => "0x1b2a76da77d03b7fc21189d9838f55bd849014af"
            //                    }
            //                }
            //            )
            //        ),
            //    }
            //
            $result = array();
            $currencies = is_array($response) ? array_keys($response) : array();
            for ($i = 0; $i < count($currencies); $i++) {
                $currencyId = $currencies[$i];
                $code = $this->safe_currency_code($currencyId);
                $entry = $response[$currencyId];
                $rawNetworks = $this->safe_list($entry, 'networks', array());
                $networks = array();
                for ($j = 0; $j < count($rawNetworks); $j++) {
                    $rawNetwork = $rawNetworks[$j];
                    $networkId = $this->safe_string_2($rawNetwork, 'protocol', 'network');
                    $networkCode = $this->network_id_to_code($networkId);
                    $networkCode = ($networkCode !== null) ? strtoupper($networkCode) : $code; // is white label, ensure we safeguard from possible bugs
                    $networks[$networkCode] = array(
                        'info' => $rawNetwork,
                        'id' => $networkId,
                        'network' => $networkCode,
                        'active' => null,
                        'fee' => $this->safe_number($rawNetwork, 'payout_fee'),
                        'deposit' => $this->safe_bool($rawNetwork, 'payin_enabled'),
                        'withdraw' => $this->safe_bool($rawNetwork, 'payout_enabled'),
                        'precision' => $this->safe_number($rawNetwork, 'precision_payout'),
                        'limits' => array(
                            'withdraw' => array(
                                'min' => null,
                                'max' => null,
                            ),
                        ),
                    );
                }
                $result[$code] = $this->safe_currency_structure(array(
                    'info' => $entry,
                    'code' => $code,
                    'id' => $currencyId,
                    'precision' => $this->safe_number($entry, 'precision_transfer'),
                    'name' => $this->safe_string($entry, 'full_name'),
                    'active' => !$this->safe_bool($entry, 'delisted'),
                    'deposit' => $this->safe_bool($entry, 'payin_enabled'),
                    'withdraw' => $this->safe_bool($entry, 'payout_enabled'),
                    'networks' => $networks,
                    'fee' => null,
                    'limits' => array(
                        'amount' => array(
                            'min' => null,
                            'max' => null,
                        ),
                    ),
                    'type' => null, // 'crypto' field emits incorrect values
                ));
            }
            return $result;
        }) ();
    }

    public function create_deposit_address(string $code, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $params) {
            /**
             * create a $currency deposit address
             *
             * @see https://api.hitbtc.com/#generate-deposit-crypto-address
             *
             * @param {string} $code unified $currency $code of the $currency for the deposit address
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=address-structure address structure~
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $request = array(
                'currency' => $currency['id'],
            );
            $network = $this->safe_string_upper($params, 'network');
            if (($network !== null) && ($code === 'USDT')) {
                $networks = $this->safe_value($this->options, 'networks');
                $parsedNetwork = $this->safe_string($networks, $network);
                if ($parsedNetwork !== null) {
                    $request['currency'] = $parsedNetwork;
                }
                $params = $this->omit($params, 'network');
            }
            $response = Async\await($this->privatePostWalletCryptoAddress ($this->extend($request, $params)));
            //
            //  array("currency":"ETH","address":"0xd0d9aea60c41988c3e68417e2616065617b7afd3")
            //
            $currencyId = $this->safe_string($response, 'currency');
            return array(
                'currency' => $this->safe_currency_code($currencyId),
                'address' => $this->safe_string($response, 'address'),
                'tag' => $this->safe_string($response, 'payment_id'),
                'network' => null,
                'info' => $response,
            );
        }) ();
    }

    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://api.hitbtc.com/#get-deposit-crypto-$address
             *
             * @param {string} $code unified $currency $code
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=$address-structure $address structure~
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $request = array(
                'currency' => $currency['id'],
            );
            $network = $this->safe_string_upper($params, 'network');
            if (($network !== null) && ($code === 'USDT')) {
                $networks = $this->safe_value($this->options, 'networks');
                $parsedNetwork = $this->safe_string($networks, $network);
                if ($parsedNetwork !== null) {
                    $request['currency'] = $parsedNetwork;
                }
                $params = $this->omit($params, 'network');
            }
            $response = Async\await($this->privateGetWalletCryptoAddress ($this->extend($request, $params)));
            //
            //  [array("currency":"ETH","address":"0xd0d9aea60c41988c3e68417e2616065617b7afd3")]
            //
            $firstAddress = $this->safe_value($response, 0);
            $address = $this->safe_string($firstAddress, 'address');
            $currencyId = $this->safe_string($firstAddress, 'currency');
            $tag = $this->safe_string($firstAddress, 'payment_id');
            $parsedCode = $this->safe_currency_code($currencyId);
            return array(
                'info' => $response,
                'currency' => $parsedCode,
                'network' => null,
                'address' => $address,
                'tag' => $tag,
            );
        }) ();
    }

    public function parse_balance($response): array {
        $result = array( 'info' => $response );
        for ($i = 0; $i < count($response); $i++) {
            $entry = $response[$i];
            $currencyId = $this->safe_string($entry, 'currency');
            $code = $this->safe_currency_code($currencyId);
            $account = $this->account();
            $account['free'] = $this->safe_string($entry, 'available');
            $account['used'] = $this->safe_string($entry, 'reserved');
            $result[$code] = $account;
        }
        return $this->safe_balance($result);
    }

    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://api.hitbtc.com/#wallet-balance
             * @see https://api.hitbtc.com/#get-spot-trading-balance
             * @see https://api.hitbtc.com/#get-trading-balance
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=balance-structure balance structure~
             */
            $type = $this->safe_string_lower($params, 'type', 'spot');
            $params = $this->omit($params, array( 'type' ));
            $accountsByType = $this->safe_value($this->options, 'accountsByType', array());
            $account = $this->safe_string($accountsByType, $type, $type);
            $response = null;
            if ($account === 'wallet') {
                $response = Async\await($this->privateGetWalletBalance ($params));
            } elseif ($account === 'spot') {
                $response = Async\await($this->privateGetSpotBalance ($params));
            } elseif ($account === 'derivatives') {
                $response = Async\await($this->privateGetFuturesBalance ($params));
            } else {
                $keys = is_array($accountsByType) ? array_keys($accountsByType) : array();
                throw new BadRequest($this->id . ' fetchBalance() $type parameter must be one of ' . implode(', ', $keys));
            }
            //
            //     array(
            //       array(
            //         "currency" => "PAXG",
            //         "available" => "0",
            //         "reserved" => "0",
            //         "reserved_margin" => "0",
            //       ),
            //       ...
            //     )
            //
            return $this->parse_balance($response);
        }) ();
    }

    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://api.hitbtc.com/#tickers
             *
             * @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->publicGetPublicTickerSymbol ($this->extend($request, $params)));
            //
            //     {
            //         "ask" => "0.020572",
            //         "bid" => "0.020566",
            //         "last" => "0.020574",
            //         "low" => "0.020388",
            //         "high" => "0.021084",
            //         "open" => "0.020913",
            //         "volume" => "138444.3666",
            //         "volume_quote" => "2853.6874972480",
            //         "timestamp" => "2021-06-02T17:52:36.731Z"
            //     }
            //
            return $this->parse_ticker($response, $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://api.hitbtc.com/#tickers
             *
             * @param {string[]|null} $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);
            $request = array();
            if ($symbols !== null) {
                $marketIds = $this->market_ids($symbols);
                $delimited = implode(',', $marketIds);
                $request['symbols'] = $delimited;
            }
            $response = Async\await($this->publicGetPublicTicker ($this->extend($request, $params)));
            //
            //     {
            //       "BTCUSDT" => {
            //         "ask" => "63049.06",
            //         "bid" => "63046.41",
            //         "last" => "63048.36",
            //         "low" => "62010.00",
            //         "high" => "66657.99",
            //         "open" => "64839.75",
            //         "volume" => "15272.13278",
            //         "volume_quote" => "976312127.6277998",
            //         "timestamp" => "2021-10-22T04:25:47.573Z"
            //       }
            //     }
            //
            $result = array();
            $keys = is_array($response) ? array_keys($response) : array();
            for ($i = 0; $i < count($keys); $i++) {
                $marketId = $keys[$i];
                $market = $this->safe_market($marketId);
                $symbol = $market['symbol'];
                $entry = $response[$marketId];
                $result[$symbol] = $this->parse_ticker($entry, $market);
            }
            return $this->filter_by_array_tickers($result, 'symbol', $symbols);
        }) ();
    }

    public function parse_ticker(array $ticker, ?array $market = null): array {
        //
        //     {
        //       "ask" => "62756.01",
        //       "bid" => "62754.09",
        //       "last" => "62755.87",
        //       "low" => "62010.00",
        //       "high" => "66657.99",
        //       "open" => "65089.27",
        //       "volume" => "16719.50366",
        //       "volume_quote" => "1063422878.8156828",
        //       "timestamp" => "2021-10-22T07:29:14.585Z"
        //     }
        //
        $timestamp = $this->parse8601($ticker['timestamp']);
        $symbol = $this->safe_symbol(null, $market);
        $baseVolume = $this->safe_string($ticker, 'volume');
        $quoteVolume = $this->safe_string($ticker, 'volume_quote');
        $open = $this->safe_string($ticker, 'open');
        $last = $this->safe_string($ticker, 'last');
        return $this->safe_ticker(array(
            'symbol' => $symbol,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'high' => $this->safe_string($ticker, 'high'),
            'low' => $this->safe_string($ticker, 'low'),
            'bid' => $this->safe_string($ticker, 'bid'),
            'bidVolume' => null,
            'ask' => $this->safe_string($ticker, 'ask'),
            'askVolume' => null,
            'vwap' => null,
            'open' => $open,
            'close' => $last,
            'last' => $last,
            'previousClose' => null,
            'change' => null,
            'percentage' => null,
            'average' => null,
            'baseVolume' => $baseVolume,
            'quoteVolume' => $quoteVolume,
            'info' => $ticker,
        ), $market);
    }

    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://api.hitbtc.com/#$trades
             *
             * @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
             * @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 = null;
            $request = array();
            if ($limit !== null) {
                $request['limit'] = min ($limit, 1000);
            }
            if ($since !== null) {
                $request['from'] = $since;
            }
            $response = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
                $response = Async\await($this->publicGetPublicTradesSymbol ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->publicGetPublicTrades ($this->extend($request, $params)));
            }
            if ($symbol !== null) {
                return $this->parse_trades($response, $market);
            }
            $trades = array();
            $marketIds = is_array($response) ? array_keys($response) : array();
            for ($i = 0; $i < count($marketIds); $i++) {
                $marketId = $marketIds[$i];
                $marketInner = $this->market($marketId);
                $rawTrades = $response[$marketId];
                $parsed = $this->parse_trades($rawTrades, $marketInner);
                $trades = $this->array_concat($trades, $parsed);
            }
            return $trades;
        }) ();
    }

    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://api.hitbtc.com/#spot-trades-history
             * @see https://api.hitbtc.com/#futures-trades-history
             * @see https://api.hitbtc.com/#margin-trades-history
             *
             * @param {string} $symbol unified $market $symbol
             * @param {int} [$since] the earliest time in ms to fetch trades for
             * @param {int} [$limit] the maximum number of trades structures to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->marginMode] 'cross' or 'isolated' only 'isolated' is supported
             * @param {bool} [$params->margin] true for fetching margin trades
             * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=trade-structure trade structures~
             */
            Async\await($this->load_markets());
            $market = null;
            $request = array();
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            if ($since !== null) {
                $request['from'] = $since;
            }
            $marketType = null;
            $marginMode = null;
            $response = null;
            list($marketType, $params) = $this->handle_market_type_and_params('fetchMyTrades', $market, $params);
            list($marginMode, $params) = $this->handle_margin_mode_and_params('fetchMyTrades', $params);
            $params = $this->omit($params, array( 'marginMode', 'margin' ));
            if ($marginMode !== null) {
                $response = Async\await($this->privateGetMarginHistoryTrade ($this->extend($request, $params)));
            } else {
                if ($marketType === 'spot') {
                    $response = Async\await($this->privateGetSpotHistoryTrade ($this->extend($request, $params)));
                } elseif ($marketType === 'swap') {
                    $response = Async\await($this->privateGetFuturesHistoryTrade ($this->extend($request, $params)));
                } elseif ($marketType === 'margin') {
                    $response = Async\await($this->privateGetMarginHistoryTrade ($this->extend($request, $params)));
                } else {
                    throw new NotSupported($this->id . ' fetchMyTrades() not support this $market type');
                }
            }
            return $this->parse_trades($response, $market, $since, $limit);
        }) ();
    }

    public function parse_trade(array $trade, ?array $market = null): array {
        //
        // createOrder ($market)
        //
        //  {
        //      "id" => "1569252895",
        //      "position_id" => "0",
        //      "quantity" => "10",
        //      "price" => "0.03919424",
        //      "fee" => "0.000979856000",
        //      "timestamp" => "2022-01-25T19:38:36.153Z",
        //      "taker" => true
        //  }
        //
        // fetchTrades
        //
        //  {
        //      "id" => 974786185,
        //      "price" => "0.032462",
        //      "qty" => "0.3673",
        //      "side" => "buy",
        //      "timestamp" => "2020-10-16T12:57:39.846Z"
        //  }
        //
        // fetchMyTrades spot
        //
        //  {
        //      "id" => 277210397,
        //      "clientOrderId" => "6e102f3e7f3f4e04aeeb1cdc95592f1a",
        //      "orderId" => 28102855393,
        //      "symbol" => "ETHBTC",
        //      "side" => "sell",
        //      "quantity" => "0.002",
        //      "price" => "0.073365",
        //      "fee" => "0.000000147",
        //      "timestamp" => "2018-04-28T18:39:55.345Z",
        //      "taker" => true
        //  }
        //
        // fetchMyTrades swap and margin
        //
        //  {
        //      "id" => 4718564,
        //      "order_id" => 58730811958,
        //      "client_order_id" => "475c47d97f867f09726186eb22b4c3d4",
        //      "symbol" => "BTCUSDT_PERP",
        //      "side" => "sell",
        //      "quantity" => "0.0001",
        //      "price" => "41118.51",
        //      "fee" => "0.002055925500",
        //      "timestamp" => "2022-03-17T05:23:17.795Z",
        //      "taker" => true,
        //      "position_id" => 2350122,
        //      "pnl" => "0.002255000000",
        //      "liquidation" => false
        //  }
        //
        $timestamp = $this->parse8601($trade['timestamp']);
        $marketId = $this->safe_string($trade, 'symbol');
        $market = $this->safe_market($marketId, $market);
        $symbol = $market['symbol'];
        $fee = null;
        $feeCostString = $this->safe_string($trade, 'fee');
        $taker = $this->safe_value($trade, 'taker');
        if ($taker !== null) {
            $takerOrMaker = $taker ? 'taker' : 'maker';
        } else {
            $takerOrMaker = 'taker'; // the only case when `$taker` field is missing, is public fetchTrades and it must be $taker
        }
        if ($feeCostString !== null) {
            $info = $this->safe_value($market, 'info', array());
            $feeCurrency = $this->safe_string($info, 'fee_currency');
            $feeCurrencyCode = $this->safe_currency_code($feeCurrency);
            $fee = array(
                'cost' => $feeCostString,
                'currency' => $feeCurrencyCode,
            );
        }
        // we use clientOrderId order $id with this exchange intentionally
        // because most of their endpoints will require clientOrderId
        // explained here => https://github.com/ccxt/ccxt/issues/5674
        $orderId = $this->safe_string_2($trade, 'clientOrderId', 'client_order_id');
        $priceString = $this->safe_string($trade, 'price');
        $amountString = $this->safe_string_2($trade, 'quantity', 'qty');
        $side = $this->safe_string($trade, 'side');
        $id = $this->safe_string($trade, 'id');
        return $this->safe_trade(array(
            'info' => $trade,
            'id' => $id,
            'order' => $orderId,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'symbol' => $symbol,
            'type' => null,
            'side' => $side,
            'takerOrMaker' => $takerOrMaker,
            'price' => $priceString,
            'amount' => $amountString,
            'cost' => null,
            'fee' => $fee,
        ), $market);
    }

    public function fetch_transactions_helper($types, $code, $since, $limit, $params) {
        return Async\async(function () use ($types, $code, $since, $limit, $params) {
            Async\await($this->load_markets());
            $request = array(
                'types' => $types,
            );
            $currency = null;
            if ($code !== null) {
                $currency = $this->currency($code);
                $request['currencies'] = $currency['id'];
            }
            if ($since !== null) {
                $request['from'] = $this->iso8601($since);
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = Async\await($this->privateGetWalletTransactions ($this->extend($request, $params)));
            //
            //     array(
            //       {
            //         "id" => "101609495",
            //         "created_at" => "2018-03-06T22:05:06.507Z",
            //         "updated_at" => "2018-03-06T22:11:45.03Z",
            //         "status" => "SUCCESS",
            //         "type" => "DEPOSIT",
            //         "subtype" => "BLOCKCHAIN",
            //         "native" => {
            //           "tx_id" => "e20b0965-4024-44d0-b63f-7fb8996a6706",
            //           "index" => "881652766",
            //           "currency" => "ETH",
            //           "amount" => "0.01418088",
            //           "hash" => "d95dbbff3f9234114f1211ab0ba2a94f03f394866fd5749d74a1edab80e6c5d3",
            //           "address" => "0xd9259302c32c0a0295d86a39185c9e14f6ba0a0d",
            //           "confirmations" => "20",
            //           "senders" => array(
            //             "0x243bec9256c9a3469da22103891465b47583d9f1"
            //           )
            //         }
            //       }
            //     )
            //
            return $this->parse_transactions($response, $currency, $since, $limit, $params);
        }) ();
    }

    public function parse_transaction_status(?string $status) {
        $statuses = array(
            'CREATED' => 'pending',
            'PENDING' => 'pending',
            'FAILED' => 'failed',
            'ROLLED_BACK' => 'failed',
            'SUCCESS' => 'ok',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function parse_transaction_type($type) {
        $types = array(
            'DEPOSIT' => 'deposit',
            'WITHDRAW' => 'withdrawal',
        );
        return $this->safe_string($types, $type, $type);
    }

    public function parse_transaction(array $transaction, ?array $currency = null): array {
        //
        // $transaction
        //
        //     {
        //       "id" => "101609495",
        //       "created_at" => "2018-03-06T22:05:06.507Z",
        //       "updated_at" => "2018-03-06T22:11:45.03Z",
        //       "status" => "SUCCESS",
        //       "type" => "DEPOSIT", // DEPOSIT, WITHDRAW, ..
        //       "subtype" => "BLOCKCHAIN",
        //       "native" => array(
        //         "tx_id" => "e20b0965-4024-44d0-b63f-7fb8996a6706",
        //         "index" => "881652766",
        //         "currency" => "ETH",
        //         "amount" => "0.01418088",
        //         "hash" => "d95dbbff3f9234114f1211ab0ba2a94f03f394866fd5749d74a1edab80e6c5d3",
        //         "address" => "0xd9259302c32c0a0295d86a39185c9e14f6ba0a0d",
        //         "confirmations" => "20",
        //         "senders" => array(
        //           "0x243bec9256c9a3469da22103891465b47583d9f1"
        //         ),
        //         "fee" => "1.22" // only for WITHDRAW
        //       }
        //     ),
        //     "operation_id" => "084cfcd5-06b9-4826-882e-fdb75ec3625d", // only for WITHDRAW
        //     "commit_risk" => array()
        // withdraw
        //
        //     {
        //         "id":"084cfcd5-06b9-4826-882e-fdb75ec3625d"
        //     }
        //
        $id = $this->safe_string_2($transaction, 'operation_id', 'id');
        $timestamp = $this->parse8601($this->safe_string($transaction, 'created_at'));
        $updated = $this->parse8601($this->safe_string($transaction, 'updated_at'));
        $type = $this->parse_transaction_type($this->safe_string($transaction, 'type'));
        $status = $this->parse_transaction_status($this->safe_string($transaction, 'status'));
        $native = $this->safe_value($transaction, 'native', array());
        $currencyId = $this->safe_string($native, 'currency');
        $code = $this->safe_currency_code($currencyId);
        $txhash = $this->safe_string($native, 'hash');
        $address = $this->safe_string($native, 'address');
        $addressTo = $address;
        $tag = $this->safe_string($native, 'payment_id');
        $tagTo = $tag;
        $sender = $this->safe_value($native, 'senders');
        $addressFrom = $this->safe_string($sender, 0);
        $amount = $this->safe_number($native, 'amount');
        $subType = $this->safe_string($transaction, 'subtype');
        $internal = $subType === 'OFFCHAIN';
        // https://api.hitbtc.com/#check-if-offchain-is-available
        $fee = array(
            'currency' => null,
            'cost' => null,
            'rate' => null,
        );
        $feeCost = $this->safe_number($native, 'fee');
        if ($feeCost !== null) {
            $fee['currency'] = $code;
            $fee['cost'] = $feeCost;
        }
        return array(
            'info' => $transaction,
            'id' => $id,
            'txid' => $txhash,
            'type' => $type,
            'currency' => $code,
            'network' => null,
            'amount' => $amount,
            'status' => $status,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'address' => $address,
            'addressFrom' => $addressFrom,
            'addressTo' => $addressTo,
            'tag' => $tag,
            'tagFrom' => null,
            'tagTo' => $tagTo,
            'updated' => $updated,
            'comment' => null,
            'internal' => $internal,
            'fee' => $fee,
        );
    }

    public function fetch_deposits_withdrawals(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch history of deposits and withdrawals
             *
             * @see https://api.hitbtc.com/#get-transactions-history
             *
             * @param {string} [$code] unified currency $code for the currency of the deposit/withdrawals, default is null
             * @param {int} [$since] timestamp in ms of the earliest deposit/withdrawal, default is null
             * @param {int} [$limit] max number of deposit/withdrawals to return, default is null
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structure~
             */
            return Async\await($this->fetch_transactions_helper('DEPOSIT,WITHDRAW', $code, $since, $limit, $params));
        }) ();
    }

    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://api.hitbtc.com/#get-transactions-history
             *
             * @param {string} $code unified currency $code
             * @param {int} [$since] the earliest time in ms to fetch deposits for
             * @param {int} [$limit] the maximum number of deposits structures to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structures~
             */
            return Async\await($this->fetch_transactions_helper('DEPOSIT', $code, $since, $limit, $params));
        }) ();
    }

    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://api.hitbtc.com/#get-transactions-history
             *
             * @param {string} $code unified currency $code
             * @param {int} [$since] the earliest time in ms to fetch withdrawals for
             * @param {int} [$limit] the maximum number of withdrawals structures to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structures~
             */
            return Async\await($this->fetch_transactions_helper('WITHDRAW', $code, $since, $limit, $params));
        }) ();
    }

    public function fetch_order_books(?array $symbols = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $limit, $params) {
            /**
             * fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data for multiple markets
             *
             * @see https://api.hitbtc.com/#order-books
             *
             * @param {string[]} [$symbols] list of unified market $symbols, all $symbols fetched if null, default is null
             * @param {int} [$limit] max number of entries per $orderbook to return, default is null
             * @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 $symbol
             */
            Async\await($this->load_markets());
            $request = array();
            if ($symbols !== null) {
                $marketIdsInner = $this->market_ids($symbols);
                $request['symbols'] = implode(',', $marketIdsInner);
            }
            if ($limit !== null) {
                $request['depth'] = $limit;
            }
            $response = Async\await($this->publicGetPublicOrderbook ($this->extend($request, $params)));
            $result = array();
            $marketIds = is_array($response) ? array_keys($response) : array();
            for ($i = 0; $i < count($marketIds); $i++) {
                $marketId = $marketIds[$i];
                $orderbook = $response[$marketId];
                $symbol = $this->safe_symbol($marketId);
                $timestamp = $this->parse8601($this->safe_string($orderbook, 'timestamp'));
                $result[$symbol] = $this->parse_order_book($response[$marketId], $symbol, $timestamp, 'bid', 'ask');
            }
            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://api.hitbtc.com/#order-books
             *
             * @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
             * @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['depth'] = $limit;
            }
            $response = Async\await($this->publicGetPublicOrderbookSymbol ($this->extend($request, $params)));
            $timestamp = $this->parse8601($this->safe_string($response, 'timestamp'));
            return $this->parse_order_book($response, $symbol, $timestamp, 'bid', 'ask');
        }) ();
    }

    public function parse_trading_fee(array $fee, ?array $market = null): array {
        //
        //     {
        //         "symbol":"ARVUSDT", // returned from fetchTradingFees only
        //         "take_rate":"0.0009",
        //         "make_rate":"0.0009"
        //     }
        //
        $taker = $this->safe_number($fee, 'take_rate');
        $maker = $this->safe_number($fee, 'make_rate');
        $marketId = $this->safe_string($fee, 'symbol');
        $symbol = $this->safe_symbol($marketId, $market);
        return array(
            'info' => $fee,
            'symbol' => $symbol,
            'taker' => $taker,
            'maker' => $maker,
            'percentage' => null,
            'tierBased' => null,
        );
    }

    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://api.hitbtc.com/#get-trading-commission
             * @see https://api.hitbtc.com/#get-trading-commission-2
             *
             * @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);
            $request = array(
                'symbol' => $market['id'],
            );
            $response = null;
            if ($market['type'] === 'spot') {
                $response = Async\await($this->privateGetSpotFeeSymbol ($this->extend($request, $params)));
            } elseif ($market['type'] === 'swap') {
                $response = Async\await($this->privateGetFuturesFeeSymbol ($this->extend($request, $params)));
            } else {
                throw new NotSupported($this->id . ' fetchTradingFee() not support this $market type');
            }
            //
            //     {
            //         "take_rate":"0.0009",
            //         "make_rate":"0.0009"
            //     }
            //
            return $this->parse_trading_fee($response, $market);
        }) ();
    }

    public function fetch_trading_fees($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * fetch the trading fees for multiple markets
             *
             * @see https://api.hitbtc.com/#get-all-trading-commissions
             * @see https://api.hitbtc.com/#get-all-trading-commissions-2
             *
             * @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());
            list($marketType, $query) = $this->handle_market_type_and_params('fetchTradingFees', null, $params);
            $response = null;
            if ($marketType === 'spot') {
                $response = Async\await($this->privateGetSpotFee ($query));
            } elseif ($marketType === 'swap') {
                $response = Async\await($this->privateGetFuturesFee ($query));
            } else {
                throw new NotSupported($this->id . ' fetchTradingFees() not support this market type');
            }
            //
            //     array(
            //         {
            //             "symbol":"ARVUSDT",
            //             "take_rate":"0.0009",
            //             "make_rate":"0.0009"
            //         }
            //     )
            //
            $result = array();
            for ($i = 0; $i < count($response); $i++) {
                $fee = $this->parse_trading_fee($response[$i]);
                $symbol = $fee['symbol'];
                $result[$symbol] = $fee;
            }
            return $result;
        }) ();
    }

    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) {
            /**
             * fetches historical candlestick data containing the open, high, low, and close $price, and the volume of a $market
             *
             * @see https://api.hitbtc.com/#candles
             * @see https://api.hitbtc.com/#futures-index-$price-candles
             * @see https://api.hitbtc.com/#futures-mark-$price-candles
             * @see https://api.hitbtc.com/#futures-premium-index-candles
             *
             * @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 funding rate
             * @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
             */
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchOHLCV', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_deterministic('fetchOHLCV', $symbol, $since, $limit, $timeframe, $params, 1000));
            }
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
                'period' => $this->safe_string($this->timeframes, $timeframe, $timeframe),
            );
            if ($since !== null) {
                $request['from'] = $this->iso8601($since);
            }
            list($request, $params) = $this->handle_until_option('until', $request, $params);
            if ($limit !== null) {
                $request['limit'] = min ($limit, 1000);
            }
            $price = $this->safe_string($params, 'price');
            $params = $this->omit($params, 'price');
            $response = null;
            if ($price === 'mark') {
                $response = Async\await($this->publicGetPublicFuturesCandlesMarkPriceSymbol ($this->extend($request, $params)));
            } elseif ($price === 'index') {
                $response = Async\await($this->publicGetPublicFuturesCandlesIndexPriceSymbol ($this->extend($request, $params)));
            } elseif ($price === 'premiumIndex') {
                $response = Async\await($this->publicGetPublicFuturesCandlesPremiumIndexSymbol ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->publicGetPublicCandlesSymbol ($this->extend($request, $params)));
            }
            //
            // Spot and Swap
            //
            //     array(
            //         {
            //             "timestamp" => "2021-10-25T07:38:00.000Z",
            //             "open" => "4173.391",
            //             "close" => "4170.923",
            //             "min" => "4170.923",
            //             "max" => "4173.986",
            //             "volume" => "0.1879",
            //             "volume_quote" => "784.2517846"
            //         }
            //     )
            //
            // Mark, Index and Premium Index
            //
            //     array(
            //         array(
            //             "timestamp" => "2022-04-01T01:28:00.000Z",
            //             "open" => "45146.39",
            //             "close" => "45219.43",
            //             "min" => "45146.39",
            //             "max" => "45219.43"
            //         ),
            //     )
            //
            return $this->parse_ohlcvs($response, $market, $timeframe, $since, $limit);
        }) ();
    }

    public function parse_ohlcv($ohlcv, ?array $market = null): array {
        //
        // Spot and Swap
        //
        //     {
        //         "timestamp":"2015-08-20T19:01:00.000Z",
        //         "open":"0.006",
        //         "close":"0.006",
        //         "min":"0.006",
        //         "max":"0.006",
        //         "volume":"0.003",
        //         "volume_quote":"0.000018"
        //     }
        //
        // Mark, Index and Premium Index
        //
        //     array(
        //         "timestamp" => "2022-04-01T01:28:00.000Z",
        //         "open" => "45146.39",
        //         "close" => "45219.43",
        //         "min" => "45146.39",
        //         "max" => "45219.43"
        //     ),
        //
        return array(
            $this->parse8601($this->safe_string($ohlcv, 'timestamp')),
            $this->safe_number($ohlcv, 'open'),
            $this->safe_number($ohlcv, 'max'),
            $this->safe_number($ohlcv, 'min'),
            $this->safe_number($ohlcv, 'close'),
            $this->safe_number($ohlcv, 'volume'),
        );
    }

    public function fetch_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 closed orders made by the user
             *
             * @see https://api.hitbtc.com/#spot-orders-history
             * @see https://api.hitbtc.com/#futures-orders-history
             * @see https://api.hitbtc.com/#margin-orders-history
             *
             * @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
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->marginMode] 'cross' or 'isolated' only 'isolated' is supported
             * @param {bool} [$params->margin] true for fetching margin orders
             * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $market = null;
            $request = array();
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            if ($since !== null) {
                $request['from'] = $this->iso8601($since);
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $marketType = null;
            $marginMode = null;
            list($marketType, $params) = $this->handle_market_type_and_params('fetchClosedOrders', $market, $params);
            list($marginMode, $params) = $this->handle_margin_mode_and_params('fetchClosedOrders', $params);
            $params = $this->omit($params, array( 'marginMode', 'margin' ));
            $response = null;
            if ($marginMode !== null) {
                $response = Async\await($this->privateGetMarginHistoryOrder ($this->extend($request, $params)));
            } else {
                if ($marketType === 'spot') {
                    $response = Async\await($this->privateGetSpotHistoryOrder ($this->extend($request, $params)));
                } elseif ($marketType === 'swap') {
                    $response = Async\await($this->privateGetFuturesHistoryOrder ($this->extend($request, $params)));
                } elseif ($marketType === 'margin') {
                    $response = Async\await($this->privateGetMarginHistoryOrder ($this->extend($request, $params)));
                } else {
                    throw new NotSupported($this->id . ' fetchClosedOrders() not support this $market type');
                }
            }
            $parsed = $this->parse_orders($response, $market, $since, $limit);
            return $this->filter_by_array($parsed, 'status', array( 'closed', 'canceled' ), false);
        }) ();
    }

    public function fetch_order(string $id, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($id, $symbol, $params) {
            /**
             * fetches information on an $order made by the user
             *
             * @see https://api.hitbtc.com/#spot-orders-history
             * @see https://api.hitbtc.com/#futures-orders-history
             * @see https://api.hitbtc.com/#margin-orders-history
             *
             * @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->marginMode] 'cross' or 'isolated' only 'isolated' is supported
             * @param {bool} [$params->margin] true for fetching a margin $order
             * @return {array} An ~@link https://docs.ccxt.com/#/?$id=$order-structure $order structure~
             */
            Async\await($this->load_markets());
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            $request = array(
                'client_order_id' => $id,
            );
            $marketType = null;
            $marginMode = null;
            list($marketType, $params) = $this->handle_market_type_and_params('fetchOrder', $market, $params);
            list($marginMode, $params) = $this->handle_margin_mode_and_params('fetchOrder', $params);
            $params = $this->omit($params, array( 'marginMode', 'margin' ));
            $response = null;
            if ($marginMode !== null) {
                $response = Async\await($this->privateGetMarginHistoryOrder ($this->extend($request, $params)));
            } else {
                if ($marketType === 'spot') {
                    $response = Async\await($this->privateGetSpotHistoryOrder ($this->extend($request, $params)));
                } elseif ($marketType === 'swap') {
                    $response = Async\await($this->privateGetFuturesHistoryOrder ($this->extend($request, $params)));
                } elseif ($marketType === 'margin') {
                    $response = Async\await($this->privateGetMarginHistoryOrder ($this->extend($request, $params)));
                } else {
                    throw new NotSupported($this->id . ' fetchOrder() not support this $market type');
                }
            }
            //
            //     array(
            //       {
            //         "id" => "685965182082",
            //         "client_order_id" => "B3CBm9uGg9oYQlw96bBSEt38-6gbgBO0",
            //         "symbol" => "BTCUSDT",
            //         "side" => "buy",
            //         "status" => "new",
            //         "type" => "limit",
            //         "time_in_force" => "GTC",
            //         "quantity" => "0.00010",
            //         "quantity_cumulative" => "0",
            //         "price" => "50000.00",
            //         "price_average" => "0",
            //         "created_at" => "2021-10-26T11:40:09.287Z",
            //         "updated_at" => "2021-10-26T11:40:09.287Z"
            //       }
            //     )
            //
            $order = $this->safe_dict($response, 0);
            return $this->parse_order($order, $market);
        }) ();
    }

    public function fetch_order_trades(string $id, ?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($id, $symbol, $since, $limit, $params) {
            /**
             * fetch all the trades made from a single order
             *
             * @see https://api.hitbtc.com/#spot-trades-history
             * @see https://api.hitbtc.com/#futures-trades-history
             * @see https://api.hitbtc.com/#margin-trades-history
             *
             * @param {string} $id order $id
             * @param {string} $symbol unified $market $symbol
             * @param {int} [$since] the earliest time in ms to fetch trades for
             * @param {int} [$limit] the maximum number of trades to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->marginMode] 'cross' or 'isolated' only 'isolated' is supported
             * @param {bool} [$params->margin] true for fetching margin trades
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?$id=trade-structure trade structures~
             */
            Async\await($this->load_markets());
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            $request = array(
                'order_id' => $id, // exchange assigned order $id to the client order $id
            );
            $marketType = null;
            $marginMode = null;
            list($marketType, $params) = $this->handle_market_type_and_params('fetchOrderTrades', $market, $params);
            list($marginMode, $params) = $this->handle_margin_mode_and_params('fetchOrderTrades', $params);
            $params = $this->omit($params, array( 'marginMode', 'margin' ));
            $response = null;
            if ($marginMode !== null) {
                $response = Async\await($this->privateGetMarginHistoryTrade ($this->extend($request, $params)));
            } else {
                if ($marketType === 'spot') {
                    $response = Async\await($this->privateGetSpotHistoryTrade ($this->extend($request, $params)));
                } elseif ($marketType === 'swap') {
                    $response = Async\await($this->privateGetFuturesHistoryTrade ($this->extend($request, $params)));
                } elseif ($marketType === 'margin') {
                    $response = Async\await($this->privateGetMarginHistoryTrade ($this->extend($request, $params)));
                } else {
                    throw new NotSupported($this->id . ' fetchOrderTrades() not support this $market type');
                }
            }
            //
            // Spot
            //
            //     array(
            //       {
            //         "id" => 1393448977,
            //         "order_id" => 653496804534,
            //         "client_order_id" => "065f6f0ff9d54547848454182263d7b4",
            //         "symbol" => "DICEETH",
            //         "side" => "buy",
            //         "quantity" => "1.4",
            //         "price" => "0.00261455",
            //         "fee" => "0.000003294333",
            //         "timestamp" => "2021-09-19T05:35:56.601Z",
            //         "taker" => true
            //       }
            //     )
            //
            // Swap and Margin
            //
            //     array(
            //         {
            //             "id" => 4718551,
            //             "order_id" => 58730748700,
            //             "client_order_id" => "dcbcd8549e3445ee922665946002ef67",
            //             "symbol" => "BTCUSDT_PERP",
            //             "side" => "buy",
            //             "quantity" => "0.0001",
            //             "price" => "41095.96",
            //             "fee" => "0.002054798000",
            //             "timestamp" => "2022-03-17T05:23:02.217Z",
            //             "taker" => true,
            //             "position_id" => 2350122,
            //             "pnl" => "0",
            //             "liquidation" => false
            //         }
            //     )
            //
            return $this->parse_trades($response, $market, $since, $limit);
        }) ();
    }

    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://api.hitbtc.com/#get-all-active-spot-orders
             * @see https://api.hitbtc.com/#get-active-futures-orders
             * @see https://api.hitbtc.com/#get-active-margin-orders
             *
             * @param {string} $symbol unified $market $symbol
             * @param {int} [$since] the earliest time in ms to fetch open orders for
             * @param {int} [$limit] the maximum number of  open orders structures to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->marginMode] 'cross' or 'isolated' only 'isolated' is supported
             * @param {bool} [$params->margin] true for fetching open margin orders
             * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $market = null;
            $request = array();
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            $marketType = null;
            $marginMode = null;
            list($marketType, $params) = $this->handle_market_type_and_params('fetchOpenOrders', $market, $params);
            list($marginMode, $params) = $this->handle_margin_mode_and_params('fetchOpenOrders', $params);
            $params = $this->omit($params, array( 'marginMode', 'margin' ));
            $response = null;
            if ($marginMode !== null) {
                $response = Async\await($this->privateGetMarginOrder ($this->extend($request, $params)));
            } else {
                if ($marketType === 'spot') {
                    $response = Async\await($this->privateGetSpotOrder ($this->extend($request, $params)));
                } elseif ($marketType === 'swap') {
                    $response = Async\await($this->privateGetFuturesOrder ($this->extend($request, $params)));
                } elseif ($marketType === 'margin') {
                    $response = Async\await($this->privateGetMarginOrder ($this->extend($request, $params)));
                } else {
                    throw new NotSupported($this->id . ' fetchOpenOrders() not support this $market type');
                }
            }
            //
            //     array(
            //       {
            //         "id" => "488953123149",
            //         "client_order_id" => "103ad305301e4c3590045b13de15b36e",
            //         "symbol" => "BTCUSDT",
            //         "side" => "buy",
            //         "status" => "new",
            //         "type" => "limit",
            //         "time_in_force" => "GTC",
            //         "quantity" => "0.00001",
            //         "quantity_cumulative" => "0",
            //         "price" => "0.01",
            //         "post_only" => false,
            //         "created_at" => "2021-04-13T13:06:16.567Z",
            //         "updated_at" => "2021-04-13T13:06:16.567Z"
            //       }
            //     )
            //
            return $this->parse_orders($response, $market, $since, $limit);
        }) ();
    }

    public function fetch_open_order(string $id, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($id, $symbol, $params) {
            /**
             * fetch an open order by it's $id
             *
             * @see https://api.hitbtc.com/#get-active-spot-order
             * @see https://api.hitbtc.com/#get-active-futures-order
             * @see https://api.hitbtc.com/#get-active-margin-order
             *
             * @param {string} $id order $id
             * @param {string} $symbol unified $market $symbol, default is null
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->marginMode] 'cross' or 'isolated' only 'isolated' is supported
             * @param {bool} [$params->margin] true for fetching an open margin order
             * @return {array} an ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            $request = array(
                'client_order_id' => $id,
            );
            $marketType = null;
            $marginMode = null;
            list($marketType, $params) = $this->handle_market_type_and_params('fetchOpenOrder', $market, $params);
            list($marginMode, $params) = $this->handle_margin_mode_and_params('fetchOpenOrder', $params);
            $params = $this->omit($params, array( 'marginMode', 'margin' ));
            $response = null;
            if ($marginMode !== null) {
                $response = Async\await($this->privateGetMarginOrderClientOrderId ($this->extend($request, $params)));
            } else {
                if ($marketType === 'spot') {
                    $response = Async\await($this->privateGetSpotOrderClientOrderId ($this->extend($request, $params)));
                } elseif ($marketType === 'swap') {
                    $response = Async\await($this->privateGetFuturesOrderClientOrderId ($this->extend($request, $params)));
                } elseif ($marketType === 'margin') {
                    $response = Async\await($this->privateGetMarginOrderClientOrderId ($this->extend($request, $params)));
                } else {
                    throw new NotSupported($this->id . ' fetchOpenOrder() not support this $market type');
                }
            }
            return $this->parse_order($response, $market);
        }) ();
    }

    public function cancel_all_orders(?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($symbol, $params) {
            /**
             * cancel all open orders
             *
             * @see https://api.hitbtc.com/#cancel-all-spot-orders
             * @see https://api.hitbtc.com/#cancel-futures-orders
             * @see https://api.hitbtc.com/#cancel-all-margin-orders
             *
             * @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->marginMode] 'cross' or 'isolated' only 'isolated' is supported
             * @param {bool} [$params->margin] true for canceling margin orders
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $market = null;
            $request = array();
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            $marketType = null;
            $marginMode = null;
            list($marketType, $params) = $this->handle_market_type_and_params('cancelAllOrders', $market, $params);
            list($marginMode, $params) = $this->handle_margin_mode_and_params('cancelAllOrders', $params);
            $params = $this->omit($params, array( 'marginMode', 'margin' ));
            $response = null;
            if ($marginMode !== null) {
                $response = Async\await($this->privateDeleteMarginOrder ($this->extend($request, $params)));
            } else {
                if ($marketType === 'spot') {
                    $response = Async\await($this->privateDeleteSpotOrder ($this->extend($request, $params)));
                } elseif ($marketType === 'swap') {
                    $response = Async\await($this->privateDeleteFuturesOrder ($this->extend($request, $params)));
                } elseif ($marketType === 'margin') {
                    $response = Async\await($this->privateDeleteMarginOrder ($this->extend($request, $params)));
                } else {
                    throw new NotSupported($this->id . ' cancelAllOrders() not support this $market type');
                }
            }
            return $this->parse_orders($response, $market);
        }) ();
    }

    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://api.hitbtc.com/#cancel-spot-order
             * @see https://api.hitbtc.com/#cancel-futures-order
             * @see https://api.hitbtc.com/#cancel-margin-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->marginMode] 'cross' or 'isolated' only 'isolated' is supported
             * @param {bool} [$params->margin] true for canceling a margin order
             * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $market = null;
            $request = array(
                'client_order_id' => $id,
            );
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            $marketType = null;
            $marginMode = null;
            list($marketType, $params) = $this->handle_market_type_and_params('cancelOrder', $market, $params);
            list($marginMode, $params) = $this->handle_margin_mode_and_params('cancelOrder', $params);
            $params = $this->omit($params, array( 'marginMode', 'margin' ));
            $response = null;
            if ($marginMode !== null) {
                $response = Async\await($this->privateDeleteMarginOrderClientOrderId ($this->extend($request, $params)));
            } else {
                if ($marketType === 'spot') {
                    $response = Async\await($this->privateDeleteSpotOrderClientOrderId ($this->extend($request, $params)));
                } elseif ($marketType === 'swap') {
                    $response = Async\await($this->privateDeleteFuturesOrderClientOrderId ($this->extend($request, $params)));
                } elseif ($marketType === 'margin') {
                    $response = Async\await($this->privateDeleteMarginOrderClientOrderId ($this->extend($request, $params)));
                } else {
                    throw new NotSupported($this->id . ' cancelOrder() not support this $market type');
                }
            }
            return $this->parse_order($response, $market);
        }) ();
    }

    public function edit_order(string $id, string $symbol, string $type, string $side, ?float $amount = null, ?float $price = null, $params = array ()) {
        return Async\async(function () use ($id, $symbol, $type, $side, $amount, $price, $params) {
            Async\await($this->load_markets());
            $market = null;
            $request = array(
                'client_order_id' => $id,
                'quantity' => $this->amount_to_precision($symbol, $amount),
            );
            if (($type === 'limit') || ($type === 'stopLimit')) {
                if ($price === null) {
                    throw new ExchangeError($this->id . ' editOrder() limit order requires price');
                }
                $request['price'] = $this->price_to_precision($symbol, $price);
            }
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            $marketType = null;
            $marginMode = null;
            list($marketType, $params) = $this->handle_market_type_and_params('editOrder', $market, $params);
            list($marginMode, $params) = $this->handle_margin_mode_and_params('editOrder', $params);
            $params = $this->omit($params, array( 'marginMode', 'margin' ));
            $response = null;
            if ($marginMode !== null) {
                $response = Async\await($this->privatePatchMarginOrderClientOrderId ($this->extend($request, $params)));
            } else {
                if ($marketType === 'spot') {
                    $response = Async\await($this->privatePatchSpotOrderClientOrderId ($this->extend($request, $params)));
                } elseif ($marketType === 'swap') {
                    $response = Async\await($this->privatePatchFuturesOrderClientOrderId ($this->extend($request, $params)));
                } elseif ($marketType === 'margin') {
                    $response = Async\await($this->privatePatchMarginOrderClientOrderId ($this->extend($request, $params)));
                } else {
                    throw new NotSupported($this->id . ' editOrder() not support this $market type');
                }
            }
            return $this->parse_order($response, $market);
        }) ();
    }

    public function create_order(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()) {
        return Async\async(function () use ($symbol, $type, $side, $amount, $price, $params) {
            /**
             * create a trade order
             *
             * @see https://api.hitbtc.com/#create-new-spot-order
             * @see https://api.hitbtc.com/#create-margin-order
             * @see https://api.hitbtc.com/#create-futures-order
             *
             * @param {string} $symbol unified $symbol of the $market to create an order in
             * @param {string} $type 'market' or 'limit'
             * @param {string} $side 'buy' or 'sell'
             * @param {float} $amount how much of currency you want to trade in units of base currency
             * @param {float} [$price] the $price at which 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 {string} [$params->marginMode] 'cross' or 'isolated' only 'isolated' is supported for spot-margin, swap supports both, default is 'cross'
             * @param {bool} [$params->margin] true for creating a margin order
             * @param {float} [$params->triggerPrice] The $price at which a trigger order is triggered at
             * @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", "FOK", "Day", "GTD"
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = null;
            $marketType = null;
            list($marketType, $params) = $this->handle_market_type_and_params('createOrder', $market, $params);
            $marginMode = null;
            list($marginMode, $params) = $this->handle_margin_mode_and_params('createOrder', $params);
            list($request, $params) = $this->create_order_request($market, $marketType, $type, $side, $amount, $price, $marginMode, $params);
            $response = null;
            if ($marketType === 'swap') {
                $response = Async\await($this->privatePostFuturesOrder ($this->extend($request, $params)));
            } elseif (($marketType === 'margin') || ($marginMode !== null)) {
                $response = Async\await($this->privatePostMarginOrder ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->privatePostSpotOrder ($this->extend($request, $params)));
            }
            return $this->parse_order($response, $market);
        }) ();
    }

    public function create_order_request(array $market, string $marketType, string $type, string $side, float $amount, ?float $price = null, ?string $marginMode = null, $params = array ()) {
        $isLimit = ($type === 'limit');
        $reduceOnly = $this->safe_value($params, 'reduceOnly');
        $timeInForce = $this->safe_string($params, 'timeInForce');
        $triggerPrice = $this->safe_number_n($params, array( 'triggerPrice', 'stopPrice', 'stop_price' ));
        $isPostOnly = $this->is_post_only($type === 'market', null, $params);
        $request = array(
            'type' => $type,
            'side' => $side,
            'quantity' => $this->amount_to_precision($market['symbol'], $amount),
            'symbol' => $market['id'],
            // 'client_order_id' => 'r42gdPjNMZN-H_xs8RKl2wljg_dfgdg4', // Optional
            // 'time_in_force' => 'GTC', // Optional GTC, IOC, FOK, Day, GTD
            // 'price' => $this->price_to_precision(symbol, $price), // Required if $type is limit, stopLimit, or takeProfitLimit
            // 'stop_price' => $this->safe_number($params, 'stop_price'), // Required if $type is stopLimit, stopMarket, takeProfitLimit, takeProfitMarket
            // 'expire_time' => '2021-06-15T17:01:05.092Z', // Required if $timeInForce is GTD
            // 'strict_validate' => false,
            // 'post_only' => false, // Optional
            // 'reduce_only' => false, // Optional
            // 'display_quantity' => '0', // Optional
            // 'take_rate' => 0.001, // Optional
            // 'make_rate' => 0.001, // Optional
        );
        if ($reduceOnly !== null) {
            if (($market['type'] !== 'swap') && ($market['type'] !== 'margin')) {
                throw new InvalidOrder($this->id . ' createOrder() does not support reduce_only for ' . $market['type'] . ' orders, reduce_only orders are supported for swap and margin markets only');
            }
        }
        if ($reduceOnly === true) {
            $request['reduce_only'] = $reduceOnly;
        }
        if ($isPostOnly) {
            $request['post_only'] = true;
        }
        if ($timeInForce !== null) {
            $request['time_in_force'] = $timeInForce;
        }
        if ($isLimit || ($type === 'stopLimit') || ($type === 'takeProfitLimit')) {
            if ($price === null) {
                throw new ExchangeError($this->id . ' createOrder() requires a $price argument for limit orders');
            }
            $request['price'] = $this->price_to_precision($market['symbol'], $price);
        }
        if (($timeInForce === 'GTD')) {
            $expireTime = $this->safe_string($params, 'expire_time');
            if ($expireTime === null) {
                throw new ExchangeError($this->id . ' createOrder() requires an expire_time parameter for a GTD order');
            }
        }
        if ($triggerPrice !== null) {
            $request['stop_price'] = $this->price_to_precision($market['symbol'], $triggerPrice);
            if ($isLimit) {
                $request['type'] = 'stopLimit';
            } elseif ($type === 'market') {
                $request['type'] = 'stopMarket';
            }
        } elseif (($type === 'stopLimit') || ($type === 'stopMarket') || ($type === 'takeProfitLimit') || ($type === 'takeProfitMarket')) {
            throw new ExchangeError($this->id . ' createOrder() requires a $triggerPrice parameter for stop-loss and take-profit orders');
        }
        $params = $this->omit($params, array( 'triggerPrice', 'timeInForce', 'stopPrice', 'stop_price', 'reduceOnly', 'postOnly' ));
        if ($marketType === 'swap') {
            // set default margin mode to cross
            if ($marginMode === null) {
                $marginMode = 'cross';
            }
            $request['margin_mode'] = $marginMode;
        }
        return array( $request, $params );
    }

    public function parse_order_status(?string $status) {
        $statuses = array(
            'new' => 'open',
            'suspended' => 'open',
            'partiallyFilled' => 'open',
            'filled' => 'closed',
            'canceled' => 'canceled',
            'expired' => 'failed',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function parse_order(array $order, ?array $market = null): array {
        //
        // limit
        //     {
        //       "id" => 488953123149,
        //       "client_order_id" => "103ad305301e4c3590045b13de15b36e",
        //       "symbol" => "BTCUSDT",
        //       "side" => "buy",
        //       "status" => "new",
        //       "type" => "limit",
        //       "time_in_force" => "GTC",
        //       "quantity" => "0.00001",
        //       "quantity_cumulative" => "0",
        //       "price" => "0.01",
        //       "price_average" => "0.01",
        //       "post_only" => false,
        //       "created_at" => "2021-04-13T13:06:16.567Z",
        //       "updated_at" => "2021-04-13T13:06:16.567Z"
        //     }
        //
        // $market
        //     {
        //       "id" => "685877626834",
        //       "client_order_id" => "Yshl7G-EjaREyXQYaGbsmdtVbW-nzQwu",
        //       "symbol" => "BTCUSDT",
        //       "side" => "buy",
        //       "status" => "filled",
        //       "type" => "market",
        //       "time_in_force" => "GTC",
        //       "quantity" => "0.00010",
        //       "quantity_cumulative" => "0.00010",
        //       "post_only" => false,
        //       "created_at" => "2021-10-26T08:55:55.1Z",
        //       "updated_at" => "2021-10-26T08:55:55.1Z",
        //       "trades" => array(
        //         {
        //           "id" => "1437229630",
        //           "position_id" => "0",
        //           "quantity" => "0.00010",
        //           "price" => "62884.78",
        //           "fee" => "0.005659630200",
        //           "timestamp" => "2021-10-26T08:55:55.1Z",
        //           "taker" => true
        //         }
        //       )
        //     }
        //
        // swap and margin
        //
        //     {
        //         "id" => 58418961892,
        //         "client_order_id" => "r42gdPjNMZN-H_xs8RKl2wljg_dfgdg4",
        //         "symbol" => "BTCUSDT_PERP",
        //         "side" => "buy",
        //         "status" => "new",
        //         "type" => "limit",
        //         "time_in_force" => "GTC",
        //         "quantity" => "0.0005",
        //         "quantity_cumulative" => "0",
        //         "price" => "30000.00",
        //         "post_only" => false,
        //         "reduce_only" => false,
        //         "created_at" => "2022-03-16T08:16:53.039Z",
        //         "updated_at" => "2022-03-16T08:16:53.039Z"
        //     }
        //
        $id = $this->safe_string($order, 'client_order_id');
        // we use clientOrderId $order $id with this exchange intentionally
        // because most of their endpoints will require clientOrderId
        // explained here => https://github.com/ccxt/ccxt/issues/5674
        $side = $this->safe_string($order, 'side');
        $type = $this->safe_string($order, 'type');
        $amount = $this->safe_string($order, 'quantity');
        $price = $this->safe_string($order, 'price');
        $average = $this->safe_string($order, 'price_average');
        $created = $this->safe_string($order, 'created_at');
        $timestamp = $this->parse8601($created);
        $updated = $this->safe_string($order, 'updated_at');
        $lastTradeTimestamp = null;
        if ($updated !== $created) {
            $lastTradeTimestamp = $this->parse8601($updated);
        }
        $filled = $this->safe_string($order, 'quantity_cumulative');
        $status = $this->parse_order_status($this->safe_string($order, 'status'));
        $marketId = $this->safe_string($order, 'symbol');
        $market = $this->safe_market($marketId, $market);
        $symbol = $market['symbol'];
        $postOnly = $this->safe_value($order, 'post_only');
        $timeInForce = $this->safe_string($order, 'time_in_force');
        $rawTrades = $this->safe_value($order, 'trades');
        return $this->safe_order(array(
            'info' => $order,
            'id' => $id,
            'clientOrderId' => $id,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'lastTradeTimestamp' => $lastTradeTimestamp,
            'lastUpdateTimestamp' => $lastTradeTimestamp,
            'symbol' => $symbol,
            'price' => $price,
            'amount' => $amount,
            'type' => $type,
            'side' => $side,
            'timeInForce' => $timeInForce,
            'postOnly' => $postOnly,
            'reduceOnly' => $this->safe_value($order, 'reduce_only'),
            'filled' => $filled,
            'remaining' => null,
            'cost' => null,
            'status' => $status,
            'average' => $average,
            'trades' => $rawTrades,
            'fee' => null,
            'triggerPrice' => $this->safe_string($order, 'stop_price'),
            'takeProfitPrice' => null,
            'stopLossPrice' => null,
        ), $market);
    }

    public function fetch_margin_modes(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * fetches margin mode of the user
             *
             * @see https://api.hitbtc.com/#get-margin-position-parameters
             * @see https://api.hitbtc.com/#get-futures-position-parameters
             *
             * @param {string[]} $symbols 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=margin-mode-structure margin mode structures~
             */
            Async\await($this->load_markets());
            $market = null;
            if ($symbols !== null) {
                $symbols = $this->market_symbols($symbols);
                $market = $this->market($symbols[0]);
            }
            $marketType = null;
            list($marketType, $params) = $this->handle_market_type_and_params('fetchMarginMode', $market, $params);
            $response = null;
            if ($marketType === 'margin') {
                $response = Async\await($this->privateGetMarginConfig ($params));
                //
                //     {
                //         "config" => [array(
                //             "symbol" => "BTCUSD",
                //             "margin_call_leverage_mul" => "1.50",
                //             "liquidation_leverage_mul" => "2.00",
                //             "max_initial_leverage" => "10.00",
                //             "margin_mode" => "Isolated",
                //             "force_close_fee" => "0.05",
                //             "enabled" => true,
                //             "active" => true,
                //             "limit_base" => "50000.00",
                //             "limit_power" => "2.2",
                //             "unlimited_threshold" => "10.0"
                //         )]
                //     }
                //
            } elseif ($marketType === 'swap') {
                $response = Async\await($this->privateGetFuturesConfig ($params));
                //
                //     {
                //         "config" => [array(
                //             "symbol" => "BTCUSD_PERP",
                //             "margin_call_leverage_mul" => "1.20",
                //             "liquidation_leverage_mul" => "2.00",
                //             "max_initial_leverage" => "100.00",
                //             "margin_mode" => "Isolated",
                //             "force_close_fee" => "0.001",
                //             "enabled" => true,
                //             "active" => false,
                //             "limit_base" => "5000000.000000000000",
                //             "limit_power" => "1.25",
                //             "unlimited_threshold" => "2.00"
                //         )]
                //     }
                //
            } else {
                throw new BadSymbol($this->id . ' fetchMarginModes () supports swap contracts and margin only');
            }
            $config = $this->safe_list($response, 'config', array());
            return $this->parse_margin_modes($config, $symbols, 'symbol');
        }) ();
    }

    public function parse_margin_mode(array $marginMode, $market = null): array {
        $marketId = $this->safe_string($marginMode, 'symbol');
        return array(
            'info' => $marginMode,
            'symbol' => $this->safe_symbol($marketId, $market),
            'marginMode' => $this->safe_string_lower($marginMode, 'margin_mode'),
        );
    }

    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://api.hitbtc.com/#transfer-between-wallet-and-exchange
             *
             * @param {string} $code unified $currency $code
             * @param {float} $amount amount to transfer
             * @param {string} $fromAccount account to transfer from
             * @param {string} $toAccount account to transfer to
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=transfer-structure transfer structure~
             */
            // account can be "spot", "wallet", or "derivatives"
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $requestAmount = $this->currency_to_precision($code, $amount);
            $accountsByType = $this->safe_value($this->options, 'accountsByType', array());
            $fromAccount = strtolower($fromAccount);
            $toAccount = strtolower($toAccount);
            $fromId = $this->safe_string($accountsByType, $fromAccount, $fromAccount);
            $toId = $this->safe_string($accountsByType, $toAccount, $toAccount);
            if ($fromId === $toId) {
                throw new BadRequest($this->id . ' transfer() $fromAccount and $toAccount arguments cannot be the same account');
            }
            $request = array(
                'currency' => $currency['id'],
                'amount' => $requestAmount,
                'source' => $fromId,
                'destination' => $toId,
            );
            $response = Async\await($this->privatePostWalletTransfer ($this->extend($request, $params)));
            //
            //     array(
            //         "2db6ebab-fb26-4537-9ef8-1a689472d236"
            //     )
            //
            return $this->parse_transfer($response, $currency);
        }) ();
    }

    public function parse_transfer(array $transfer, ?array $currency = null): array {
        //
        // $transfer
        //
        //     array(
        //         "2db6ebab-fb26-4537-9ef8-1a689472d236"
        //     )
        //
        return array(
            'id' => $this->safe_string($transfer, 0),
            'timestamp' => null,
            'datetime' => null,
            'currency' => $this->safe_currency_code(null, $currency),
            'amount' => null,
            'fromAccount' => null,
            'toAccount' => null,
            'status' => null,
            'info' => $transfer,
        );
    }

    public function convert_currency_network(string $code, $amount, $fromNetwork, $toNetwork, $params) {
        return Async\async(function () use ($code, $amount, $fromNetwork, $toNetwork, $params) {
            Async\await($this->load_markets());
            if ($code !== 'USDT') {
                throw new ExchangeError($this->id . ' convertCurrencyNetwork() only supports USDT currently');
            }
            $networks = $this->safe_value($this->options, 'networks', array());
            $fromNetwork = strtoupper($fromNetwork);
            $toNetwork = strtoupper($toNetwork);
            $fromNetwork = $this->safe_string($networks, $fromNetwork); // handle ETH>ERC20 alias
            $toNetwork = $this->safe_string($networks, $toNetwork); // handle ETH>ERC20 alias
            if ($fromNetwork === $toNetwork) {
                throw new BadRequest($this->id . ' convertCurrencyNetwork() $fromNetwork cannot be the same');
            }
            if (($fromNetwork === null) || ($toNetwork === null)) {
                $keys = is_array($networks) ? array_keys($networks) : array();
                throw new ArgumentsRequired($this->id . ' convertCurrencyNetwork() requires a $fromNetwork parameter and a $toNetwork parameter, supported $networks are ' . implode(', ', $keys));
            }
            $request = array(
                'from_currency' => $fromNetwork,
                'to_currency' => $toNetwork,
                'amount' => $this->currency_to_precision($code, $amount),
            );
            $response = Async\await($this->privatePostWalletConvert ($this->extend($request, $params)));
            // array("result":["587a1868-e62d-4d8e-b27c-dbdb2ee96149","e168df74-c041-41f2-b76c-e43e4fed5bc7"])
            return array(
                'info' => $response,
            );
        }) ();
    }

    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://api.hitbtc.com/#withdraw-crypto
             *
             * @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
             * @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());
            $this->check_address($address);
            $currency = $this->currency($code);
            $request = array(
                'currency' => $currency['id'],
                'amount' => $amount,
                'address' => $address,
            );
            if ($tag !== null) {
                $request['payment_id'] = $tag;
            }
            $networks = $this->safe_value($this->options, 'networks', array());
            $network = $this->safe_string_upper($params, 'network');
            if (($network !== null) && ($code === 'USDT')) {
                $parsedNetwork = $this->safe_string($networks, $network);
                if ($parsedNetwork !== null) {
                    $request['network_code'] = $parsedNetwork;
                }
                $params = $this->omit($params, 'network');
            }
            $withdrawOptions = $this->safe_value($this->options, 'withdraw', array());
            $includeFee = $this->safe_bool($withdrawOptions, 'includeFee', false);
            if ($includeFee) {
                $request['include_fee'] = true;
            }
            $response = Async\await($this->privatePostWalletCryptoWithdraw ($this->extend($request, $params)));
            //
            //     {
            //         "id":"084cfcd5-06b9-4826-882e-fdb75ec3625d"
            //     }
            //
            return $this->parse_transaction($response, $currency);
        }) ();
    }

    public function fetch_funding_rates(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * fetches funding rates for multiple markets
             *
             * @see https://api.hitbtc.com/#futures-info
             *
             * @param {string[]} $symbols unified $symbols of the markets to fetch the funding rates for, all $market funding rates are returned if not assigned
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=funding-rate-structure funding rate structures~
             */
            Async\await($this->load_markets());
            $market = null;
            $request = array();
            if ($symbols !== null) {
                $symbols = $this->market_symbols($symbols);
                $market = $this->market($symbols[0]);
                $queryMarketIds = $this->market_ids($symbols);
                $request['symbols'] = implode(',', $queryMarketIds);
            }
            $type = null;
            list($type, $params) = $this->handle_market_type_and_params('fetchFundingRates', $market, $params);
            if ($type !== 'swap') {
                throw new NotSupported($this->id . ' fetchFundingRates() does not support ' . $type . ' markets');
            }
            $response = Async\await($this->publicGetPublicFuturesInfo ($this->extend($request, $params)));
            //
            //     {
            //         "BTCUSDT_PERP" => {
            //             "contract_type" => "perpetual",
            //             "mark_price" => "30897.68",
            //             "index_price" => "30895.29",
            //             "funding_rate" => "0.0001",
            //             "open_interest" => "93.7128",
            //             "next_funding_time" => "2021-07-21T16:00:00.000Z",
            //             "indicative_funding_rate" => "0.0001",
            //             "premium_index" => "0.000047541807127312",
            //             "avg_premium_index" => "0.000087063368020112",
            //             "interest_rate" => "0.0001",
            //             "timestamp" => "2021-07-21T09:48:37.235Z"
            //         }
            //     }
            //
            $marketIds = is_array($response) ? array_keys($response) : array();
            $fundingRates = array();
            for ($i = 0; $i < count($marketIds); $i++) {
                $marketId = $this->safe_string($marketIds, $i);
                $rawFundingRate = $this->safe_value($response, $marketId);
                $marketInner = $this->market($marketId);
                $symbol = $marketInner['symbol'];
                $fundingRate = $this->parse_funding_rate($rawFundingRate, $marketInner);
                $fundingRates[$symbol] = $fundingRate;
            }
            return $this->filter_by_array($fundingRates, 'symbol', $symbols);
        }) ();
    }

    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) {
            /**
             *
             * @see https://api.hitbtc.com/#funding-history
             *
             * fetches historical funding rate prices
             * @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->until] timestamp in ms of the latest funding rate
             * @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 {array[]} a list of ~@link https://docs.ccxt.com/#/?id=funding-rate-history-structure funding rate structures~
             */
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchFundingRateHistory', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_deterministic('fetchFundingRateHistory', $symbol, $since, $limit, '8h', $params, 1000));
            }
            $market = null;
            $request = array(
                // all arguments are optional
                // 'symbols' => Comma separated list of $symbol codes,
                // 'sort' => 'DESC' or 'ASC'
                // 'from' => 'Datetime or Number',
                // 'until' => 'Datetime or Number',
                // 'limit' => 100,
                // 'offset' => 0,
            );
            list($request, $params) = $this->handle_until_option('until', $request, $params);
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $symbol = $market['symbol'];
                $request['symbols'] = $market['id'];
            }
            if ($since !== null) {
                $request['from'] = $since;
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = Async\await($this->publicGetPublicFuturesHistoryFunding ($this->extend($request, $params)));
            //
            //    {
            //        "BTCUSDT_PERP" => array(
            //            array(
            //                "timestamp" => "2021-07-29T16:00:00.271Z",
            //                "funding_rate" => "0.0001",
            //                "avg_premium_index" => "0.000061858585213222",
            //                "next_funding_time" => "2021-07-30T00:00:00.000Z",
            //                "interest_rate" => "0.0001"
            //            ),
            //            ...
            //        ),
            //        ...
            //    }
            //
            $contracts = is_array($response) ? array_keys($response) : array();
            $rates = array();
            for ($i = 0; $i < count($contracts); $i++) {
                $marketId = $contracts[$i];
                $marketInner = $this->safe_market($marketId);
                $fundingRateData = $response[$marketId];
                for ($j = 0; $j < count($fundingRateData); $j++) {
                    $entry = $fundingRateData[$j];
                    $symbolInner = $this->safe_symbol($marketInner['symbol']);
                    $fundingRate = $this->safe_number($entry, 'funding_rate');
                    $datetime = $this->safe_string($entry, 'timestamp');
                    $rates[] = array(
                        'info' => $entry,
                        'symbol' => $symbolInner,
                        'fundingRate' => $fundingRate,
                        'timestamp' => $this->parse8601($datetime),
                        'datetime' => $datetime,
                    );
                }
            }
            $sorted = $this->sort_by($rates, 'timestamp');
            return $this->filter_by_symbol_since_limit($sorted, $symbol, $since, $limit);
        }) ();
    }

    public function fetch_positions(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * fetch all open positions
             *
             * @see https://api.hitbtc.com/#get-futures-margin-accounts
             * @see https://api.hitbtc.com/#get-all-margin-accounts
             *
             * @param {string[]|null} $symbols not used by hitbtc fetchPositions ()
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->marginMode] 'cross' or 'isolated' only 'isolated' is supported, defaults to spot-margin endpoint if this is set
             * @param {bool} [$params->margin] true for fetching spot-margin positions
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=position-structure position structure~
             */
            Async\await($this->load_markets());
            $request = array();
            $marketType = null;
            $marginMode = null;
            list($marketType, $params) = $this->handle_market_type_and_params('fetchPositions', null, $params);
            if ($marketType === 'spot') {
                $marketType = 'swap';
            }
            list($marginMode, $params) = $this->handle_margin_mode_and_params('fetchPositions', $params);
            $params = $this->omit($params, array( 'marginMode', 'margin' ));
            $response = null;
            if ($marginMode !== null) {
                $response = Async\await($this->privateGetMarginAccount ($this->extend($request, $params)));
            } else {
                if ($marketType === 'swap') {
                    $response = Async\await($this->privateGetFuturesAccount ($this->extend($request, $params)));
                } elseif ($marketType === 'margin') {
                    $response = Async\await($this->privateGetMarginAccount ($this->extend($request, $params)));
                } else {
                    throw new NotSupported($this->id . ' fetchPositions() not support this market type');
                }
            }
            //
            //     array(
            //         {
            //             "symbol" => "ETHUSDT_PERP",
            //             "type" => "isolated",
            //             "leverage" => "10.00",
            //             "created_at" => "2022-03-19T07:54:35.24Z",
            //             "updated_at" => "2022-03-19T07:54:58.922Z",
            //             currencies" => array(
            //                 {
            //                     "code" => "USDT",
            //                     "margin_balance" => "7.478100643043",
            //                     "reserved_orders" => "0",
            //                     "reserved_positions" => "0.303530761300"
            //                 }
            //             ),
            //             "positions" => array(
            //                 array(
            //                     "id" => 2470568,
            //                     "symbol" => "ETHUSDT_PERP",
            //                     "quantity" => "0.001",
            //                     "price_entry" => "2927.509",
            //                     "price_margin_call" => "0",
            //                     "price_liquidation" => "0",
            //                     "pnl" => "0",
            //                     "created_at" => "2022-03-19T07:54:35.24Z",
            //                     "updated_at" => "2022-03-19T07:54:58.922Z"
            //                 }
            //             )
            //         ),
            //     )
            //
            $result = array();
            for ($i = 0; $i < count($response); $i++) {
                $result[] = $this->parse_position($response[$i]);
            }
            return $result;
        }) ();
    }

    public function fetch_position(string $symbol, $params = array ()) {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetch data on a single open contract trade position
             *
             * @see https://api.hitbtc.com/#get-futures-margin-account
             * @see https://api.hitbtc.com/#get-isolated-margin-account
             *
             * @param {string} $symbol unified $market $symbol of the $market the position is held in, default is null
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->marginMode] 'cross' or 'isolated' only 'isolated' is supported, defaults to spot-margin endpoint if this is set
             * @param {bool} [$params->margin] true for fetching a spot-margin position
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=position-structure position structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $marketType = null;
            $marginMode = null;
            list($marketType, $params) = $this->handle_market_type_and_params('fetchPosition', null, $params);
            list($marginMode, $params) = $this->handle_margin_mode_and_params('fetchPosition', $params);
            $params = $this->omit($params, array( 'marginMode', 'margin' ));
            $response = null;
            if ($marginMode !== null) {
                $response = Async\await($this->privateGetMarginAccountIsolatedSymbol ($this->extend($request, $params)));
            } else {
                if ($marketType === 'swap') {
                    $response = Async\await($this->privateGetFuturesAccountIsolatedSymbol ($this->extend($request, $params)));
                } elseif ($marketType === 'margin') {
                    $response = Async\await($this->privateGetMarginAccountIsolatedSymbol ($this->extend($request, $params)));
                } else {
                    throw new NotSupported($this->id . ' fetchPosition() not support this $market type');
                }
            }
            //
            //     array(
            //         {
            //             "symbol" => "ETHUSDT_PERP",
            //             "type" => "isolated",
            //             "leverage" => "10.00",
            //             "created_at" => "2022-03-19T07:54:35.24Z",
            //             "updated_at" => "2022-03-19T07:54:58.922Z",
            //             currencies" => array(
            //                 {
            //                     "code" => "USDT",
            //                     "margin_balance" => "7.478100643043",
            //                     "reserved_orders" => "0",
            //                     "reserved_positions" => "0.303530761300"
            //                 }
            //             ),
            //             "positions" => array(
            //                 array(
            //                     "id" => 2470568,
            //                     "symbol" => "ETHUSDT_PERP",
            //                     "quantity" => "0.001",
            //                     "price_entry" => "2927.509",
            //                     "price_margin_call" => "0",
            //                     "price_liquidation" => "0",
            //                     "pnl" => "0",
            //                     "created_at" => "2022-03-19T07:54:35.24Z",
            //                     "updated_at" => "2022-03-19T07:54:58.922Z"
            //                 }
            //             )
            //         ),
            //     )
            //
            return $this->parse_position($response, $market);
        }) ();
    }

    public function parse_position(array $position, ?array $market = null) {
        //
        //     array(
        //         {
        //             "symbol" => "ETHUSDT_PERP",
        //             "type" => "isolated",
        //             "leverage" => "10.00",
        //             "created_at" => "2022-03-19T07:54:35.24Z",
        //             "updated_at" => "2022-03-19T07:54:58.922Z",
        //             $currencies" => array(
        //                 {
        //                     "code" => "USDT",
        //                     "margin_balance" => "7.478100643043",
        //                     "reserved_orders" => "0",
        //                     "reserved_positions" => "0.303530761300"
        //                 }
        //             ),
        //             "positions" => array(
        //                 array(
        //                     "id" => 2470568,
        //                     "symbol" => "ETHUSDT_PERP",
        //                     "quantity" => "0.001",
        //                     "price_entry" => "2927.509",
        //                     "price_margin_call" => "0",
        //                     "price_liquidation" => "0",
        //                     "pnl" => "0",
        //                     "created_at" => "2022-03-19T07:54:35.24Z",
        //                     "updated_at" => "2022-03-19T07:54:58.922Z"
        //                 }
        //             )
        //         ),
        //     )
        //
        $marginMode = $this->safe_string($position, 'type');
        $leverage = $this->safe_number($position, 'leverage');
        $datetime = $this->safe_string($position, 'updated_at');
        $positions = $this->safe_value($position, 'positions', array());
        $liquidationPrice = null;
        $entryPrice = null;
        $contracts = null;
        for ($i = 0; $i < count($positions); $i++) {
            $entry = $positions[$i];
            $liquidationPrice = $this->safe_number($entry, 'price_liquidation');
            $entryPrice = $this->safe_number($entry, 'price_entry');
            $contracts = $this->safe_number($entry, 'quantity');
        }
        $currencies = $this->safe_value($position, 'currencies', array());
        $collateral = null;
        for ($i = 0; $i < count($currencies); $i++) {
            $entry = $currencies[$i];
            $collateral = $this->safe_number($entry, 'margin_balance');
        }
        $marketId = $this->safe_string($position, 'symbol');
        $market = $this->safe_market($marketId, $market);
        $symbol = $market['symbol'];
        return $this->safe_position(array(
            'info' => $position,
            'id' => null,
            'symbol' => $symbol,
            'notional' => null,
            'marginMode' => $marginMode,
            'marginType' => $marginMode,
            'liquidationPrice' => $liquidationPrice,
            'entryPrice' => $entryPrice,
            'unrealizedPnl' => null,
            'percentage' => null,
            'contracts' => $contracts,
            'contractSize' => null,
            'markPrice' => null,
            'lastPrice' => null,
            'side' => null,
            'hedged' => null,
            'timestamp' => $this->parse8601($datetime),
            'datetime' => $datetime,
            'lastUpdateTimestamp' => null,
            'maintenanceMargin' => null,
            'maintenanceMarginPercentage' => null,
            'collateral' => $collateral,
            'initialMargin' => null,
            'initialMarginPercentage' => null,
            'leverage' => $leverage,
            'marginRatio' => null,
            'stopLossPrice' => null,
            'takeProfitPrice' => null,
        ));
    }

    public function parse_open_interest($interest, ?array $market = null) {
        //
        //     {
        //         "contract_type" => "perpetual",
        //         "mark_price" => "42307.43",
        //         "index_price" => "42303.27",
        //         "funding_rate" => "0.0001",
        //         "open_interest" => "30.9826",
        //         "next_funding_time" => "2022-03-22T16:00:00.000Z",
        //         "indicative_funding_rate" => "0.0001",
        //         "premium_index" => "0",
        //         "avg_premium_index" => "0.000029587712038098",
        //         "interest_rate" => "0.0001",
        //         "timestamp" => "2022-03-22T08:08:26.687Z"
        //     }
        //
        $datetime = $this->safe_string($interest, 'timestamp');
        $value = $this->safe_number($interest, 'open_interest');
        return $this->safe_open_interest(array(
            'symbol' => $this->safe_symbol(null, $market),
            'openInterestAmount' => null,
            'openInterestValue' => $value,
            'timestamp' => $this->parse8601($datetime),
            'datetime' => $datetime,
            'info' => $interest,
        ), $market);
    }

    public function fetch_open_interests(?array $symbols = null, $params = array ()) {
        return Async\async(function () use ($symbols, $params) {
            /**
             * Retrieves the open interest for a list of $symbols
             *
             * @see https://api.hitbtc.com/#futures-info
             *
             * @param {string[]} [$symbols] a list of unified CCXT market $symbols
             * @param {array} [$params] exchange specific parameters
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=open-interest-structure open interest structures~
             */
            Async\await($this->load_markets());
            $request = array();
            $symbols = $this->market_symbols($symbols);
            $marketIds = null;
            if ($symbols !== null) {
                $marketIds = $this->market_ids($symbols);
                $request['symbols'] = implode(',', $marketIds);
            }
            $response = Async\await($this->publicGetPublicFuturesInfo ($this->extend($request, $params)));
            //
            //     {
            //         "BTCUSDT_PERP" => {
            //             "contract_type" => "perpetual",
            //             "mark_price" => "97291.83",
            //             "index_price" => "97298.61",
            //             "funding_rate" => "-0.000183473092423284",
            //             "open_interest" => "94.1503",
            //             "next_funding_time" => "2024-12-20T08:00:00.000Z",
            //             "indicative_funding_rate" => "-0.00027495203277752",
            //             "premium_index" => "-0.000789474900583786",
            //             "avg_premium_index" => "-0.000683473092423284",
            //             "interest_rate" => "0.0001",
            //             "timestamp" => "2024-12-20T04:57:33.693Z"
            //         }
            //     }
            //
            $results = array();
            $markets = is_array($response) ? array_keys($response) : array();
            for ($i = 0; $i < count($markets); $i++) {
                $marketId = $markets[$i];
                $marketInner = $this->safe_market($marketId);
                $results[] = $this->parse_open_interest($response[$marketId], $marketInner);
            }
            return $this->filter_by_array($results, 'symbol', $symbols);
        }) ();
    }

    public function fetch_open_interest(string $symbol, $params = array ()) {
        return Async\async(function () use ($symbol, $params) {
            /**
             * Retrieves the open interest of a derivative trading pair
             *
             * @see https://api.hitbtc.com/#futures-info
             *
             * @param {string} $symbol Unified CCXT $market $symbol
             * @param {array} [$params] exchange specific parameters
             * @return {array} an open interest structurearray(@link https://docs.ccxt.com/#/?id=interest-history-structure)
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            if (!$market['swap']) {
                throw new BadSymbol($this->id . ' fetchOpenInterest() supports swap contracts only');
            }
            $request = array(
                'symbol' => $market['id'],
            );
            $response = Async\await($this->publicGetPublicFuturesInfoSymbol ($this->extend($request, $params)));
            //
            //     {
            //         "contract_type" => "perpetual",
            //         "mark_price" => "42307.43",
            //         "index_price" => "42303.27",
            //         "funding_rate" => "0.0001",
            //         "open_interest" => "30.9826",
            //         "next_funding_time" => "2022-03-22T16:00:00.000Z",
            //         "indicative_funding_rate" => "0.0001",
            //         "premium_index" => "0",
            //         "avg_premium_index" => "0.000029587712038098",
            //         "interest_rate" => "0.0001",
            //         "timestamp" => "2022-03-22T08:08:26.687Z"
            //     }
            //
            return $this->parse_open_interest($response, $market);
        }) ();
    }

    public function fetch_funding_rate(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetch the current funding rate
             *
             * @see https://api.hitbtc.com/#futures-info
             *
             * @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);
            if (!$market['swap']) {
                throw new BadSymbol($this->id . ' fetchFundingRate() supports swap contracts only');
            }
            $request = array(
                'symbol' => $market['id'],
            );
            $response = Async\await($this->publicGetPublicFuturesInfoSymbol ($this->extend($request, $params)));
            //
            //     {
            //         "contract_type" => "perpetual",
            //         "mark_price" => "42307.43",
            //         "index_price" => "42303.27",
            //         "funding_rate" => "0.0001",
            //         "open_interest" => "30.9826",
            //         "next_funding_time" => "2022-03-22T16:00:00.000Z",
            //         "indicative_funding_rate" => "0.0001",
            //         "premium_index" => "0",
            //         "avg_premium_index" => "0.000029587712038098",
            //         "interest_rate" => "0.0001",
            //         "timestamp" => "2022-03-22T08:08:26.687Z"
            //     }
            //
            return $this->parse_funding_rate($response, $market);
        }) ();
    }

    public function parse_funding_rate($contract, ?array $market = null): array {
        //
        //     {
        //         "contract_type" => "perpetual",
        //         "mark_price" => "42307.43",
        //         "index_price" => "42303.27",
        //         "funding_rate" => "0.0001",
        //         "open_interest" => "30.9826",
        //         "next_funding_time" => "2022-03-22T16:00:00.000Z",
        //         "indicative_funding_rate" => "0.0001",
        //         "premium_index" => "0",
        //         "avg_premium_index" => "0.000029587712038098",
        //         "interest_rate" => "0.0001",
        //         "timestamp" => "2022-03-22T08:08:26.687Z"
        //     }
        //
        $fundingDateTime = $this->safe_string($contract, 'next_funding_time');
        $datetime = $this->safe_string($contract, 'timestamp');
        return array(
            'info' => $contract,
            'symbol' => $this->safe_symbol(null, $market),
            'markPrice' => $this->safe_number($contract, 'mark_price'),
            'indexPrice' => $this->safe_number($contract, 'index_price'),
            'interestRate' => $this->safe_number($contract, 'interest_rate'),
            'estimatedSettlePrice' => null,
            'timestamp' => $this->parse8601($datetime),
            'datetime' => $datetime,
            'fundingRate' => $this->safe_number($contract, 'funding_rate'),
            'fundingTimestamp' => $this->parse8601($fundingDateTime),
            'fundingDatetime' => $fundingDateTime,
            'nextFundingRate' => $this->safe_number($contract, 'indicative_funding_rate'),
            'nextFundingTimestamp' => null,
            'nextFundingDatetime' => null,
            'previousFundingRate' => null,
            'previousFundingTimestamp' => null,
            'previousFundingDatetime' => null,
            'interval' => null,
        );
    }

    public function modify_margin_helper(string $symbol, $amount, $type, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $amount, $type, $params) {
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $leverage = $this->safe_string($params, 'leverage');
            if ($market['swap']) {
                if ($leverage === null) {
                    throw new ArgumentsRequired($this->id . ' modifyMarginHelper() requires a $leverage parameter for swap markets');
                }
            }
            $stringAmount = $this->number_to_string($amount);
            if ($stringAmount !== '0') {
                $amount = $this->amount_to_precision($symbol, $stringAmount);
            } else {
                $amount = '0';
            }
            $request = array(
                'symbol' => $market['id'], // swap and margin
                'margin_balance' => $amount, // swap and margin
                // "leverage" => "10", // swap only required
                // "strict_validate" => false, // swap and margin
            );
            if ($leverage !== null) {
                $request['leverage'] = $leverage;
            }
            $marketType = null;
            $marginMode = null;
            list($marketType, $params) = $this->handle_market_type_and_params('modifyMarginHelper', $market, $params);
            list($marginMode, $params) = $this->handle_margin_mode_and_params('modifyMarginHelper', $params);
            $response = null;
            if ($marketType === 'swap') {
                $response = Async\await($this->privatePutFuturesAccountIsolatedSymbol ($this->extend($request, $params)));
            } elseif (($marketType === 'margin') || ($marketType === 'spot') || ($marginMode === 'isolated')) {
                $response = Async\await($this->privatePutMarginAccountIsolatedSymbol ($this->extend($request, $params)));
            } else {
                throw new NotSupported($this->id . ' modifyMarginHelper() not support this $market type');
            }
            //
            //     {
            //         "symbol" => "BTCUSDT_PERP",
            //         "type" => "isolated",
            //         "leverage" => "8.00",
            //         "created_at" => "2022-03-30T23:34:27.161Z",
            //         "updated_at" => "2022-03-30T23:34:27.161Z",
            //         "currencies" => array(
            //             {
            //                 "code" => "USDT",
            //                 "margin_balance" => "7.000000000000",
            //                 "reserved_orders" => "0",
            //                 "reserved_positions" => "0"
            //             }
            //         ),
            //         "positions" => null
            //     }
            //
            return $this->extend($this->parse_margin_modification($response, $market), array(
                'amount' => $this->parse_number($amount),
                'type' => $type,
            ));
        }) ();
    }

    public function parse_margin_modification(array $data, ?array $market = null): array {
        //
        // addMargin/reduceMargin
        //
        //     {
        //         "symbol" => "BTCUSDT_PERP",
        //         "type" => "isolated",
        //         "leverage" => "8.00",
        //         "created_at" => "2022-03-30T23:34:27.161Z",
        //         "updated_at" => "2022-03-30T23:34:27.161Z",
        //         "currencies" => array(
        //             {
        //                 "code" => "USDT",
        //                 "margin_balance" => "7.000000000000",
        //                 "reserved_orders" => "0",
        //                 "reserved_positions" => "0"
        //             }
        //         ),
        //         "positions" => null
        //     }
        //
        $currencies = $this->safe_value($data, 'currencies', array());
        $currencyInfo = $this->safe_value($currencies, 0);
        $datetime = $this->safe_string($data, 'updated_at');
        return array(
            'info' => $data,
            'symbol' => $market['symbol'],
            'type' => null,
            'marginMode' => 'isolated',
            'amount' => null,
            'total' => null,
            'code' => $this->safe_string($currencyInfo, 'code'),
            'status' => null,
            'timestamp' => $this->parse8601($datetime),
            'datetime' => $datetime,
        );
    }

    public function reduce_margin(string $symbol, float $amount, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $amount, $params) {
            /**
             * remove margin from a position
             *
             * @see https://api.hitbtc.com/#create-update-margin-account-2
             * @see https://api.hitbtc.com/#create-update-margin-account
             *
             * @param {string} $symbol unified market $symbol
             * @param {float} $amount the $amount of margin to remove
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->marginMode] 'cross' or 'isolated' only 'isolated' is supported, defaults to the spot-margin endpoint if this is set
             * @param {bool} [$params->margin] true for reducing spot-margin
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=reduce-margin-structure margin structure~
             */
            if ($this->number_to_string($amount) !== '0') {
                throw new BadRequest($this->id . ' reduceMargin() on hitbtc requires the $amount to be 0 and that will remove the entire margin amount');
            }
            return Async\await($this->modify_margin_helper($symbol, $amount, 'reduce', $params));
        }) ();
    }

    public function add_margin(string $symbol, float $amount, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $amount, $params) {
            /**
             * add margin
             *
             * @see https://api.hitbtc.com/#create-update-margin-account-2
             * @see https://api.hitbtc.com/#create-update-margin-account
             *
             * @param {string} $symbol unified market $symbol
             * @param {float} $amount amount of margin to add
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->marginMode] 'cross' or 'isolated' only 'isolated' is supported, defaults to the spot-margin endpoint if this is set
             * @param {bool} [$params->margin] true for adding spot-margin
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=add-margin-structure margin structure~
             */
            return Async\await($this->modify_margin_helper($symbol, $amount, 'add', $params));
        }) ();
    }

    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://api.hitbtc.com/#get-futures-margin-account
             * @see https://api.hitbtc.com/#get-isolated-margin-account
             *
             * @param {string} $symbol unified $market $symbol
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->marginMode] 'cross' or 'isolated' only 'isolated' is supported, defaults to the spot-margin endpoint if this is set
             * @param {bool} [$params->margin] true for fetching spot-margin leverage
             * @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'],
            );
            $marginMode = null;
            list($marginMode, $params) = $this->handle_margin_mode_and_params('fetchLeverage', $params);
            $params = $this->omit($params, array( 'marginMode', 'margin' ));
            $response = null;
            if ($marginMode !== null) {
                $response = Async\await($this->privateGetMarginAccountIsolatedSymbol ($this->extend($request, $params)));
            } else {
                if ($market['type'] === 'spot') {
                    $response = Async\await($this->privateGetMarginAccountIsolatedSymbol ($this->extend($request, $params)));
                } elseif ($market['type'] === 'swap') {
                    $response = Async\await($this->privateGetFuturesAccountIsolatedSymbol ($this->extend($request, $params)));
                } elseif ($market['type'] === 'margin') {
                    $response = Async\await($this->privateGetMarginAccountIsolatedSymbol ($this->extend($request, $params)));
                } else {
                    throw new NotSupported($this->id . ' fetchLeverage() not support this $market type');
                }
            }
            //
            //     {
            //         "symbol" => "BTCUSDT",
            //         "type" => "isolated",
            //         "leverage" => "12.00",
            //         "created_at" => "2022-03-29T22:31:29.067Z",
            //         "updated_at" => "2022-03-30T00:00:00.125Z",
            //         "currencies" => array(
            //             {
            //                 "code" => "USDT",
            //                 "margin_balance" => "20.824360374174",
            //                 "reserved_orders" => "0",
            //                 "reserved_positions" => "0.973330435000"
            //             }
            //         ),
            //         "positions" => array(
            //             {
            //                 "id" => 631301,
            //                 "symbol" => "BTCUSDT",
            //                 "quantity" => "0.00022",
            //                 "price_entry" => "47425.57",
            //                 "price_margin_call" => "",
            //                 "price_liquidation" => "0",
            //                 "pnl" => "0",
            //                 "created_at" => "2022-03-29T22:31:29.067Z",
            //                 "updated_at" => "2022-03-30T00:00:00.125Z"
            //             }
            //         )
            //     }
            //
            return $this->parse_leverage($response, $market);
        }) ();
    }

    public function parse_leverage(array $leverage, ?array $market = null): array {
        $marketId = $this->safe_string($leverage, 'symbol');
        $leverageValue = $this->safe_integer($leverage, 'leverage');
        return array(
            'info' => $leverage,
            'symbol' => $this->safe_symbol($marketId, $market),
            'marginMode' => $this->safe_string_lower($leverage, 'type'),
            '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://api.hitbtc.com/#create-update-margin-account-2
             *
             * @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());
            if ($params['margin_balance'] === null) {
                throw new ArgumentsRequired($this->id . ' setLeverage() requires a margin_balance parameter that will transfer margin to the specified trading pair');
            }
            $market = $this->market($symbol);
            $amount = $this->safe_number($params, 'margin_balance');
            $maxLeverage = $this->safe_integer($market['limits']['leverage'], 'max', 50);
            if ($market['type'] !== 'swap') {
                throw new BadSymbol($this->id . ' setLeverage() supports swap contracts only');
            }
            if (($leverage < 1) || ($leverage > $maxLeverage)) {
                throw new BadRequest($this->id . ' setLeverage() $leverage should be between 1 and ' . (string) $maxLeverage . ' for ' . $symbol);
            }
            $request = array(
                'symbol' => $market['id'],
                'leverage' => (string) $leverage,
                'margin_balance' => $this->amount_to_precision($symbol, $amount),
                // 'strict_validate' => false,
            );
            return Async\await($this->privatePutFuturesAccountIsolatedSymbol ($this->extend($request, $params)));
        }) ();
    }

    public function fetch_deposit_withdraw_fees(?array $codes = null, $params = array ()) {
        return Async\async(function () use ($codes, $params) {
            /**
             * fetch deposit and withdraw fees
             *
             * @see https://api.hitbtc.com/#currencies
             *
             * @param {string[]|null} $codes list of unified currency $codes
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=fee-structure fees structures~
             */
            Async\await($this->load_markets());
            $response = Async\await($this->publicGetPublicCurrency ($params));
            //
            //     {
            //       "WEALTH" => {
            //         "full_name" => "ConnectWealth",
            //         "payin_enabled" => false,
            //         "payout_enabled" => false,
            //         "transfer_enabled" => true,
            //         "precision_transfer" => "0.001",
            //         "networks" => array(
            //           {
            //             "network" => "ETH",
            //             "protocol" => "ERC20",
            //             "default" => true,
            //             "payin_enabled" => false,
            //             "payout_enabled" => false,
            //             "precision_payout" => "0.001",
            //             "payout_fee" => "0.016800000000",
            //             "payout_is_payment_id" => false,
            //             "payin_payment_id" => false,
            //             "payin_confirmations" => "2"
            //           }
            //         )
            //       }
            //     }
            //
            return $this->parse_deposit_withdraw_fees($response, $codes);
        }) ();
    }

    public function parse_deposit_withdraw_fee($fee, ?array $currency = null) {
        //
        //    {
        //         "full_name" => "ConnectWealth",
        //         "payin_enabled" => false,
        //         "payout_enabled" => false,
        //         "transfer_enabled" => true,
        //         "precision_transfer" => "0.001",
        //         "networks" => array(
        //           {
        //             "network" => "ETH",
        //             "protocol" => "ERC20",
        //             "default" => true,
        //             "payin_enabled" => false,
        //             "payout_enabled" => false,
        //             "precision_payout" => "0.001",
        //             "payout_fee" => "0.016800000000",
        //             "payout_is_payment_id" => false,
        //             "payin_payment_id" => false,
        //             "payin_confirmations" => "2"
        //           }
        //         )
        //    }
        //
        $networks = $this->safe_value($fee, 'networks', array());
        $result = $this->deposit_withdraw_fee($fee);
        for ($j = 0; $j < count($networks); $j++) {
            $networkEntry = $networks[$j];
            $networkId = $this->safe_string($networkEntry, 'network');
            $networkCode = $this->network_id_to_code($networkId);
            $networkCode = ($networkCode !== null) ? strtoupper($networkCode) : null;
            $withdrawFee = $this->safe_number($networkEntry, 'payout_fee');
            $isDefault = $this->safe_value($networkEntry, 'default');
            $withdrawResult = array(
                'fee' => $withdrawFee,
                'percentage' => ($withdrawFee !== null) ? false : null,
            );
            if ($isDefault === true) {
                $result['withdraw'] = $withdrawResult;
            }
            $result['networks'][$networkCode] = array(
                'withdraw' => $withdrawResult,
                'deposit' => array(
                    'fee' => null,
                    'percentage' => null,
                ),
            );
        }
        return $result;
    }

    public function close_position(string $symbol, ?string $side = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $side, $params) {
            /**
             * closes open positions for a $market
             *
             * @see https://api.hitbtc.com/#close-all-futures-margin-positions
             *
             * @param {string} $symbol unified ccxt $market $symbol
             * @param {string} $side 'buy' or 'sell'
             * @param {array} [$params] extra parameters specific to the okx api endpoint
             * @param {string} [$params->symbol] *required* unified $market $symbol
             * @param {string} [$params->marginMode] 'cross' or 'isolated', default is 'cross'
             * @return {array} An ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $marginMode = null;
            list($marginMode, $params) = $this->handle_margin_mode_and_params('closePosition', $params, 'cross');
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
                'margin_mode' => $marginMode,
            );
            $response = Async\await($this->privateDeleteFuturesPositionMarginModeSymbol ($this->extend($request, $params)));
            //
            // {
            //     "id":"202471640",
            //     "symbol":"TRXUSDT_PERP",
            //     "margin_mode":"Cross",
            //     "leverage":"1.00",
            //     "quantity":"0",
            //     "price_entry":"0",
            //     "price_margin_call":"0",
            //     "price_liquidation":"0",
            //     "pnl":"0.001234100000",
            //     "created_at":"2023-10-29T14:46:13.235Z",
            //     "updated_at":"2023-12-19T09:34:40.014Z"
            // }
            //
            return $this->parse_order($response, $market);
        }) ();
    }

    public function handle_margin_mode_and_params($methodName, $params = array (), $defaultValue = null) {
        /**
         * @ignore
         * $marginMode specified by $params["marginMode"], $this->options["marginMode"], $this->options["defaultMarginMode"], $params["margin"] = true or $this->options["defaultType"] = 'margin'
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {Array} the $marginMode in lowercase
         */
        $defaultType = $this->safe_string($this->options, 'defaultType');
        $isMargin = $this->safe_bool($params, 'margin', false);
        $marginMode = null;
        list($marginMode, $params) = parent::handle_margin_mode_and_params($methodName, $params, $defaultValue);
        if ($marginMode === null) {
            if (($defaultType === 'margin') || ($isMargin === true)) {
                $marginMode = 'isolated';
            }
        }
        return array( $marginMode, $params );
    }

    public function handle_errors(int $code, string $reason, string $url, string $method, array $headers, string $body, $response, $requestHeaders, $requestBody) {
        //
        //     {
        //       "error" => {
        //         "code" => 20001,
        //         "message" => "Insufficient funds",
        //         "description" => "Check that the funds are sufficient, given commissions"
        //       }
        //     }
        //
        //     {
        //       "error" => {
        //         "code" => "600",
        //         "message" => "Action not allowed"
        //       }
        //     }
        //
        $error = $this->safe_value($response, 'error');
        $errorCode = $this->safe_string($error, 'code');
        if ($errorCode !== null) {
            $feedback = $this->id . ' ' . $body;
            $message = $this->safe_string_2($error, 'message', 'description');
            $this->throw_exactly_matched_exception($this->exceptions['exact'], $errorCode, $feedback);
            $this->throw_broadly_matched_exception($this->exceptions['broad'], $message, $feedback);
            throw new ExchangeError($feedback);
        }
        return null;
    }

    public function sign($path, $api = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) {
        $query = $this->omit($params, $this->extract_params($path));
        $implodedPath = $this->implode_params($path, $params);
        $url = $this->urls['api'][$api] . '/' . $implodedPath;
        $getRequest = null;
        $keys = is_array($query) ? array_keys($query) : array();
        $queryLength = count($keys);
        $headers = array(
            'Content-Type' => 'application/json',
        );
        if ($method === 'GET') {
            if ($queryLength) {
                $getRequest = '?' . $this->urlencode($query);
                $url = $url . $getRequest;
            }
        } else {
            $body = $this->json($params);
        }
        if ($api === 'private') {
            $this->check_required_credentials();
            $timestamp = (string) $this->nonce();
            $payload = array( $method, '/api/3/' . $implodedPath );
            if ($method === 'GET') {
                if ($getRequest !== null) {
                    $payload[] = $getRequest;
                }
            } else {
                $payload[] = $body;
            }
            $payload[] = $timestamp;
            $payloadString = implode('', $payload);
            $signature = $this->hmac($this->encode($payloadString), $this->encode($this->secret), 'sha256', 'hex');
            $secondPayload = $this->apiKey . ':' . $signature . ':' . $timestamp;
            $encoded = base64_encode($secondPayload);
            $headers['Authorization'] = 'HS256 ' . $encoded;
        }
        return array( 'url' => $url, 'method' => $method, 'body' => $body, 'headers' => $headers );
    }
}
