<?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\hibachi as Exchange;
use ccxt\ExchangeError;
use ccxt\Precise;
use \React\Async;
use \React\Promise;
use \React\Promise\PromiseInterface;

class hibachi extends Exchange {

    public function describe(): mixed {
        return $this->deep_extend(parent::describe(), array(
            'id' => 'hibachi',
            'name' => 'Hibachi',
            'countries' => array( 'US' ),
            'rateLimit' => 100,
            'userAgent' => $this->userAgents['chrome'],
            'certified' => false,
            'pro' => false,
            'dex' => true,
            'has' => array(
                'CORS' => null,
                'spot' => false,
                'margin' => false,
                'swap' => true,
                'future' => false,
                'option' => false,
                'addMargin' => false,
                'cancelAllOrders' => true,
                'cancelOrder' => true,
                'cancelOrders' => true,
                'cancelWithdraw' => false,
                'closeAllPositions' => false,
                'closePosition' => false,
                'createConvertTrade' => false,
                'createDepositAddress' => false,
                'createMarketBuyOrderWithCost' => false,
                'createMarketOrder' => false,
                'createMarketOrderWithCost' => false,
                'createMarketSellOrderWithCost' => false,
                'createOrder' => true,
                'createOrders' => true,
                'createOrderWithTakeProfitAndStopLoss' => false,
                'createReduceOnlyOrder' => false,
                'createStopLimitOrder' => false,
                'createStopLossOrder' => false,
                'createStopMarketOrder' => false,
                'createStopOrder' => false,
                'createTakeProfitOrder' => false,
                'createTrailingAmountOrder' => false,
                'createTrailingPercentOrder' => false,
                'createTriggerOrder' => false,
                'editOrder' => true,
                'editOrders' => true,
                'fetchAccounts' => false,
                'fetchBalance' => true,
                'fetchCanceledOrders' => false,
                'fetchClosedOrder' => false,
                'fetchClosedOrders' => false,
                'fetchConvertCurrencies' => false,
                'fetchConvertQuote' => false,
                'fetchCurrencies' => false,
                'fetchDepositAddress' => true,
                'fetchDeposits' => true,
                'fetchDepositsWithdrawals' => false,
                'fetchFundingHistory' => false,
                'fetchFundingInterval' => false,
                'fetchFundingIntervals' => false,
                'fetchFundingRate' => true,
                'fetchFundingRateHistory' => true,
                'fetchFundingRates' => false,
                'fetchIndexOHLCV' => false,
                'fetchLedger' => true,
                'fetchLeverage' => false,
                'fetchMarginAdjustmentHistory' => false,
                'fetchMarginMode' => false,
                'fetchMarkets' => true,
                'fetchMyTrades' => true,
                'fetchOHLCV' => true,
                'fetchOpenInterest' => true,
                'fetchOpenInterestHistory' => false,
                'fetchOpenOrder' => false,
                'fetchOpenOrders' => true,
                'fetchOrder' => true,
                'fetchOrderBook' => true,
                'fetchOrders' => false,
                'fetchOrderTrades' => false,
                'fetchPosition' => false,
                'fetchPositionMode' => false,
                'fetchPositions' => true,
                'fetchPremiumIndexOHLCV' => false,
                'fetchStatus' => false,
                'fetchTicker' => true,
                'fetchTickers' => false,
                'fetchTime' => true,
                'fetchTrades' => true,
                'fetchTradingFee' => false,
                'fetchTradingFees' => true,
                'fetchTradingLimits' => false,
                'fetchTransactions' => 'emulated',
                'fetchTransfers' => false,
                'fetchWithdrawals' => true,
                'reduceMargin' => false,
                'setLeverage' => false,
                'setMargin' => false,
                'setPositionMode' => false,
                'transfer' => false,
                'withdraw' => true,
            ),
            'timeframes' => array(
                '1m' => '1min',
                '5m' => '5min',
                '15m' => '15min',
                '1h' => '1h',
                '4h' => '4h',
                '1d' => '1d',
                '1w' => '1w',
            ),
            'urls' => array(
                'logo' => 'https://github.com/user-attachments/assets/7301bbb1-4f27-4167-8a55-75f74b14e973',
                'api' => array(
                    'public' => 'https://data-api.hibachi.xyz',
                    'private' => 'https://api.hibachi.xyz',
                ),
                'www' => 'https://www.hibachi.xyz/',
                'referral' => array(
                    'url' => 'hibachi.xyz/r/ZBL2YFWIHU',
                ),
            ),
            'api' => array(
                'public' => array(
                    'get' => array(
                        'market/exchange-info' => 1,
                        'market/data/trades' => 1,
                        'market/data/prices' => 1,
                        'market/data/stats' => 1,
                        'market/data/klines' => 1,
                        'market/data/orderbook' => 1,
                        'market/data/open-interest' => 1,
                        'market/data/funding-rates' => 1,
                        'exchange/utc-timestamp' => 1,
                    ),
                ),
                'private' => array(
                    'get' => array(
                        'capital/deposit-info' => 1,
                        'capital/history' => 1,
                        'trade/account/trading_history' => 1,
                        'trade/account/info' => 1,
                        'trade/order' => 1,
                        'trade/account/trades' => 1,
                        'trade/orders' => 1,
                    ),
                    'put' => array(
                        'trade/order' => 1,
                    ),
                    'delete' => array(
                        'trade/order' => 1,
                        'trade/orders' => 1,
                    ),
                    'post' => array(
                        'trade/order' => 1,
                        'trade/orders' => 1,
                        'capital/withdraw' => 1,
                    ),
                ),
            ),
            'requiredCredentials' => array(
                'apiKey' => true,
                'secret' => false,
                'accountId' => true,
                'privateKey' => true,
            ),
            'fees' => array(
                'trading' => array(
                    'tierBased' => false,
                    'percentage' => true,
                    'maker' => $this->parse_number('0.00015'),
                    'taker' => $this->parse_number('0.00045'),
                ),
            ),
            'currencies' => $this->hardcoded_currencies(),
            'options' => array(
            ),
            'features' => array(
                'default' => array(
                    'sandbox' => false,
                    'createOrder' => array(
                        'marginMode' => false,
                        'triggerPrice' => true,
                        'triggerPriceType' => null,
                        'triggerDirection' => null,
                        'stopLossPrice' => false,
                        'takeProfitPrice' => false,
                        'attachedStopLossTakeProfit' => null,
                        'timeInForce' => array(
                            'IOC' => true,
                            'FOK' => false,
                            'PO' => true,
                            'GTD' => false,
                        ),
                        'hedged' => false,
                        'trailing' => false,
                        'leverage' => false,
                        'marketBuyByCost' => false,
                        'marketBuyRequiresPrice' => false,
                        'selfTradePrevention' => false,
                        'iceberg' => false,
                    ),
                    'createOrders' => null,
                    'fetchMyTrades' => array(
                        'marginMode' => false,
                        'limit' => null,
                        'daysBack' => null,
                        'untilDays' => null,
                        'symbolRequired' => false,
                    ),
                    'fetchOrder' => array(
                        'marginMode' => false,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => false,
                    ),
                    'fetchOpenOrders' => array(
                        'marginMode' => false,
                        'limit' => null,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => false,
                    ),
                    'fetchOrders' => null,
                    'fetchClosedOrders' => null,
                    'fetchOHLCV' => array(
                        'limit' => null,
                    ),
                ),
                'swap' => array(
                    'linear' => array(
                        'extends' => 'default',
                    ),
                    'inverse' => null,
                ),
                'future' => array(
                    'linear' => array(
                        'extends' => 'default',
                    ),
                    'inverse' => null,
                ),
            ),
            'commonCurrencies' => array(),
            'exceptions' => array(
                'exact' => array(
                    '2' => '\\ccxt\\BadRequest', // array("errorCode":2,"message":"Invalid signature => Failed to verify signature")
                    '3' => '\\ccxt\\OrderNotFound', // array("errorCode":3,"message":"Not found => order ID 33","status":"failed")
                    '4' => '\\ccxt\\BadRequest', // array("errorCode":4,"message":"Missing accountId","status":"failed")
                ),
                'broad' => array(
                ),
            ),
            'precisionMode' => TICK_SIZE,
        ));
    }

    public function get_account_id() {
        $this->check_required_credentials();
        $id = $this->parse_to_int($this->accountId);
        return $id;
    }

    public function parse_market(array $market): array {
        $marketId = $this->safe_string($market, 'symbol');
        $numericId = $this->safe_number($market, 'id');
        $marketType = 'swap';
        $baseId = $this->safe_string($market, 'underlyingSymbol');
        $quoteId = $this->safe_string($market, 'settlementSymbol');
        $base = $this->safe_currency_code($baseId);
        $quote = $this->safe_currency_code($quoteId);
        $settleId = $this->safe_string($market, 'settlementSymbol');
        $settle = $this->safe_currency_code($settleId);
        $symbol = $base . '/' . $quote . ':' . $settle;
        $created = $this->safe_integer_product($market, 'marketCreationTimestamp', 1000);
        return array(
            'id' => $marketId,
            'numericId' => $numericId,
            'symbol' => $symbol,
            'base' => $base,
            'quote' => $quote,
            'settle' => $settle,
            'baseId' => $baseId,
            'quoteId' => $quoteId,
            'settleId' => $settleId,
            'type' => $marketType,
            'spot' => false,
            'margin' => false,
            'swap' => true,
            'future' => false,
            'option' => false,
            'active' => $this->safe_string($market, 'status') === 'LIVE',
            'contract' => true,
            'linear' => true,
            'inverse' => false,
            'contractSize' => $this->parse_number('1'),
            'expiry' => null,
            'expiryDatetime' => null,
            'strike' => null,
            'optionType' => null,
            'precision' => array(
                'amount' => $this->parse_number($this->parse_precision($this->safe_string($market, 'underlyingDecimals'))),
                'price' => $this->parse_number($this->safe_list($market, 'orderbookGranularities')[0]) / 10000.0,
            ),
            'limits' => array(
                'leverage' => array(
                    'min' => null,
                    'max' => null,
                ),
                'amount' => array(
                    'min' => null,
                    'max' => null,
                ),
                'price' => array(
                    'min' => null,
                    'max' => null,
                ),
                'cost' => array(
                    'min' => $this->safe_number($market, 'minNotional'),
                    'max' => null,
                ),
            ),
            'created' => $created,
            'info' => $market,
        );
    }

    public function fetch_markets($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * retrieves data on all markets for hibachi
             *
             * @see https://api-doc.hibachi.xyz/#183981da-8df5-40a0-a155-da15015dd536
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} an array of objects representing market data
             */
            $response = Async\await($this->publicGetMarketExchangeInfo ($params));
            // array(
            //     "displayName" => "ETH/USDT Perps",
            //     "id" => 1,
            //     "maintenanceFactorForPositions" => "0.030000",
            //     "marketCloseTimestamp" => null,
            //     "marketOpenTimestamp" => null,
            //     "minNotional" => "1",
            //     "minOrderSize" => "0.000000001",
            //     "orderbookGranularities" => array(
            //         "0.01",
            //         "0.1",
            //         "1",
            //         "10"
            //     ),
            //     "riskFactorForOrders" => "0.066667",
            //     "riskFactorForPositions" => "0.030000",
            //     "settlementDecimals" => 6,
            //     "settlementSymbol" => "USDT",
            //     "status" => "LIVE",
            //     "stepSize" => "0.000000001",
            //     "symbol" => "ETH/USDT-P",
            //     "tickSize" => "0.000001",
            //     "underlyingDecimals" => 9,
            //     "underlyingSymbol" => "ETH"
            // ),
            $rows = $this->safe_list($response, 'futureContracts');
            return $this->parse_markets($rows);
        }) ();
    }

    public function hardcoded_currencies(): ?array {
        // Hibachi only supports USDT on Arbitrum at this time
        // We don't have an API endpoint to expose this information yet
        $result = array();
        $networks = array();
        $networkId = 'ARBITRUM';
        $networks[$networkId] = array(
            'id' => $networkId,
            'network' => $networkId,
            'limits' => array(
                'withdraw' => array(
                    'min' => null,
                    'max' => null,
                ),
                'deposit' => array(
                    'min' => null,
                    'max' => null,
                ),
            ),
            'active' => null,
            'deposit' => null,
            'withdraw' => null,
            'info' => array(),
        );
        $code = $this->safe_currency_code('USDT');
        $result[$code] = $this->safe_currency_structure(array(
            'id' => 'USDT',
            'name' => 'USDT',
            'type' => 'fiat',
            'code' => $code,
            'precision' => $this->parse_number('0.000001'),
            'active' => true,
            'fee' => null,
            'networks' => $networks,
            'deposit' => true,
            'withdraw' => true,
            'limits' => array(
                'deposit' => array(
                    'min' => null,
                    'max' => null,
                ),
                'withdraw' => array(
                    'min' => null,
                    'max' => null,
                ),
            ),
            'info' => array(),
        ));
        return $result;
    }

    public function parse_balance($response): array {
        $result = array(
            'info' => $response,
        );
        // Hibachi only supports USDT on Arbitrum at this time
        $code = $this->safe_currency_code('USDT');
        $account = $this->account();
        $account['total'] = $this->safe_string($response, 'balance');
        $account['free'] = $this->safe_string($response, 'maximalWithdraw');
        $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-doc.hibachi.xyz/#69aafedb-8274-4e21-bbaf-91dace8b8f31
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=balance-structure balance structure~
             */
            $request = array(
                'accountId' => $this->get_account_id(),
            );
            $response = Async\await($this->privateGetTradeAccountInfo ($this->extend($request, $params)));
            //
            // {
            //     assets => array( array( quantity => '3.000000', symbol => 'USDT' ) ),
            //     balance => '3.000000',
            //     maximalWithdraw => '3.000000',
            //     numFreeTransfersRemaining => '100',
            //     positions => array(),
            //     totalOrderNotional => '0.000000',
            //     totalPositionNotional => '0.000000',
            //     totalUnrealizedFundingPnl => '0.000000',
            //     totalUnrealizedPnl => '0.000000',
            //     totalUnrealizedTradingPnl => '0.000000',
            //     tradeMakerFeeRate => '0.00000000',
            //     tradeTakerFeeRate => '0.00020000'
            // }
            //
            return $this->parse_balance($response);
        }) ();
    }

    public function parse_ticker(array $ticker, ?array $market = null): array {
        $prices = $this->safe_dict($ticker, 'prices');
        $stats = $this->safe_dict($ticker, 'stats');
        $bid = $this->safe_number($prices, 'bidPrice');
        $ask = $this->safe_number($prices, 'askPrice');
        $last = $this->safe_number($prices, 'tradePrice');
        $high = $this->safe_number($stats, 'high24h');
        $low = $this->safe_number($stats, 'low24h');
        $volume = $this->safe_number($stats, 'volume24h');
        return $this->safe_ticker(array(
            'symbol' => $this->safe_symbol(null, $market),
            'timestamp' => null,
            'datetime' => null,
            'bid' => $bid,
            'ask' => $ask,
            'last' => $last,
            'high' => $high,
            'low' => $low,
            'bidVolume' => null,
            'askVolume' => null,
            'vwap' => null,
            'open' => null,
            'close' => $last,
            'previousClose' => null,
            'change' => null,
            'percentage' => null,
            'average' => null,
            'baseVolume' => null,
            'quoteVolume' => $volume,
            'info' => $ticker,
        ), $market);
    }

    public function parse_trade(array $trade, ?array $market = null): array {
        // public fetchTrades:
        //      {
        //          "price" => "3512.431902",
        //          "quantity" => "1.414780098",
        //          "takerSide" => "Buy",
        //          "timestamp" => 1712692147
        //      }
        //
        // private fetchMyTrades:
        //      {
        //          "askAccountId" => 221,
        //          "askOrderId" => 589168494921909200,
        //          "bidAccountId" => 132,
        //          "bidOrderId" => 589168494829895700,
        //          "fee" => "0.000477",
        //          "id" => 199511136,
        //          "orderType" => "MARKET",
        //          "price" => "119257.90000",
        //          "quantity" => "0.0000200000",
        //          "realizedPnl" => "-0.000352",
        //          "side" => "Sell",
        //          "symbol" => "BTC/USDT-P",
        //          "timestamp" => 1752543391
        //      }
        $marketId = $this->safe_string($trade, 'symbol');
        $market = $this->safe_market($marketId, $market);
        $symbol = $market['symbol'];
        $id = $this->safe_string($trade, 'id');
        $price = $this->safe_string($trade, 'price');
        $amount = $this->safe_string($trade, 'quantity');
        $timestamp = $this->safe_integer_product($trade, 'timestamp', 1000);
        $cost = Precise::string_mul($price, $amount);
        $side = null;
        $fee = null;
        $orderType = null;
        $orderId = null;
        $takerOrMaker = null;
        if ($id === null) {
            // public trades
            $side = $this->safe_string_lower($trade, 'takerSide');
            $takerOrMaker = 'taker';
        } else {
            // private trades
            $side = $this->safe_string_lower($trade, 'side');
            $fee = array( 'cost' => $this->safe_string($trade, 'fee'), 'currency' => 'USDT' );
            $orderType = $this->safe_string_lower($trade, 'orderType');
            if ($side === 'buy') {
                $orderId = $this->safe_string($trade, 'bidOrderId');
            } else {
                $orderId = $this->safe_string($trade, 'askOrderId');
            }
        }
        return $this->safe_trade(array(
            'id' => $id,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'symbol' => $symbol,
            'side' => $side,
            'price' => $price,
            'amount' => $amount,
            'cost' => $cost,
            'order' => $orderId,
            'takerOrMaker' => $takerOrMaker,
            'type' => $orderType,
            'fee' => $fee,
            'info' => $trade,
        ), $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-doc.hibachi.xyz/#86a53bc1-d3bb-4b93-8a11-7034d4698caa
             *
             * @param {string} $symbol unified $market $symbol
             * @param {int} [$since] timestamp in ms of the earliest trade to fetch
             * @param {int} [$limit] the maximum amount of $trades to fetch (maximum value is 100)
             * @param {array} [$params] extra parameters specific to the hibachi api endpoint
             * @return {array[]} a list of recent [trade structures]
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $response = Async\await($this->publicGetMarketDataTrades ($this->extend($request, $params)));
            //
            // {
            //     "trades" => array(
            //         array(
            //             "price" => "111091.38352",
            //             "quantity" => "0.0090090093",
            //             "takerSide" => "Buy",
            //             "timestamp" => 1752095479
            //         ),
            //     )
            // }
            //
            $trades = $this->safe_list($response, 'trades', array());
            return $this->parse_trades($trades, $market);
        }) ();
    }

    public function fetch_ticker(?string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             *
             * @see https://api-doc.hibachi.xyz/#4abb30c4-e5c7-4b0f-9ade-790111dbfa47
             *
             * fetches a price $ticker and the related information for the past 24h
             * @param {string} $symbol unified $symbol of the $market
             * @param {array} [$params] extra parameters specific to the hibachi 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'],
            );
            $rawPromises = array(
                $this->publicGetMarketDataPrices ($this->extend($request, $params)),
                $this->publicGetMarketDataStats ($this->extend($request, $params)),
            );
            $promises = Async\await(Promise\all($rawPromises));
            $pricesResponse = $promises[0];
            // {
            //     "askPrice" => "3514.650296",
            //     "bidPrice" => "3513.596112",
            //     "fundingRateEstimation" => array(
            //         "estimatedFundingRate" => "0.000001",
            //         "nextFundingTimestamp" => 1712707200
            //     ),
            //     "markPrice" => "3514.288858",
            //     "spotPrice" => "3514.715000",
            //     "symbol" => "ETH/USDT-P",
            //     "tradePrice" => "2372.746570"
            // }
            $statsResponse = $promises[1];
            // {
            //     "high24h" => "3819.507827",
            //     "low24h" => "3754.474162",
            //     "symbol" => "ETH/USDT-P",
            //     "volume24h" => "23554.858590416"
            // }
            $ticker = array(
                'prices' => $pricesResponse,
                'stats' => $statsResponse,
            );
            return $this->parse_ticker($ticker, $market);
        }) ();
    }

    public function parse_order_status(string $status): string {
        $statuses = array(
            'PENDING' => 'open',
            'CHILD_PENDING' => 'open',
            'SCHEDULED_TWAP' => 'open',
            'PLACED' => 'open',
            'PARTIALLY_FILLED' => 'open',
            'FILLED' => 'closed',
            'CANCELLED' => 'canceled',
            'REJECTED' => 'rejected',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function parse_order(array $order, ?array $market = null): array {
        $marketId = $this->safe_string($order, 'symbol');
        $market = $this->safe_market($marketId, $market);
        $status = $this->safe_string($order, 'status');
        $type = $this->safe_string_lower($order, 'orderType');
        $price = $this->safe_string($order, 'price');
        $rawSide = $this->safe_string($order, 'side');
        $side = null;
        if ($rawSide === 'BID') {
            $side = 'buy';
        } elseif ($rawSide === 'ASK') {
            $side = 'sell';
        }
        $amount = $this->safe_string($order, 'totalQuantity');
        $remaining = $this->safe_string($order, 'availableQuantity');
        $totalQuantity = $this->safe_string($order, 'totalQuantity');
        $availableQuantity = $this->safe_string($order, 'availableQuantity');
        $filled = null;
        if ($totalQuantity !== null && $availableQuantity !== null) {
            $filled = Precise::string_sub($totalQuantity, $availableQuantity);
        }
        $timeInForce = 'GTC';
        $orderFlags = $this->safe_value($order, 'orderFlags');
        $postOnly = false;
        $reduceOnly = false;
        if ($orderFlags === 'POST_ONLY') {
            $timeInForce = 'PO';
            $postOnly = true;
        } elseif ($orderFlags === 'IOC') {
            $timeInForce = 'IOC';
        } elseif ($orderFlags === 'REDUCE_ONLY') {
            $reduceOnly = true;
        }
        return $this->safe_order(array(
            'info' => $order,
            'id' => $this->safe_string($order, 'orderId'),
            'clientOrderId' => null,
            'datetime' => null,
            'timestamp' => null,
            'lastTradeTimestamp' => null,
            'lastUpdateTimestamp' => null,
            'status' => $this->parse_order_status($status),
            'symbol' => $market['symbol'],
            'type' => $type,
            'timeInForce' => $timeInForce,
            'side' => $side,
            'price' => $price,
            'average' => null,
            'amount' => $amount,
            'filled' => $filled,
            'remaining' => $remaining,
            'cost' => null,
            'trades' => null,
            'fee' => null,
            'reduceOnly' => $reduceOnly,
            'postOnly' => $postOnly,
            'triggerPrice' => $this->safe_number($order, 'triggerPrice'),
        ), $market);
    }

    public function fetch_order(string $id, ?string $symbol = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($id, $symbol, $params) {
            /**
             * fetches information on an order made by the user
             *
             * @see https://api-doc.hibachi.xyz/#096a8854-b918-4de8-8731-b2a28d26b96d
             *
             * @param {string} $id the order $id
             * @param {string} $symbol unified $symbol of the $market the order was made in
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @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(
                'orderId' => $id,
                'accountId' => $this->get_account_id(),
            );
            $response = Async\await($this->privateGetTradeOrder ($this->extend($request, $params)));
            return $this->parse_order($response, $market);
        }) ();
    }

    public function fetch_trading_fees($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * fetch the trading fee
             * @param $params extra parameters
             * @return {array} a map of market symbols to ~@link https://docs.ccxt.com/#/?id=fee-structure fee structures~
             */
            Async\await($this->load_markets());
            $request = array(
                'accountId' => $this->get_account_id(),
            );
            $response = Async\await($this->privateGetTradeAccountInfo ($this->extend($request, $params)));
            //    array(
            //        "tradeMakerFeeRate" => "0.00000000",
            //        "tradeTakerFeeRate" => "0.00020000"
            //    ),
            $makerFeeRate = $this->safe_number($response, 'tradeMakerFeeRate');
            $takerFeeRate = $this->safe_number($response, 'tradeTakerFeeRate');
            $result = array();
            for ($i = 0; $i < count($this->symbols); $i++) {
                $symbol = $this->symbols[$i];
                $result[$symbol] = array(
                    'info' => $response,
                    'symbol' => $symbol,
                    'maker' => $makerFeeRate,
                    'taker' => $takerFeeRate,
                    'percentage' => true,
                );
            }
            return $result;
        }) ();
    }

    public function order_message($market, float $nonce, float $feeRate, string $type, string $side, float $amount, ?float $price = null) {
        $sideInternal = 0;
        if ($side === 'sell') {
            $sideInternal = 0;
        } elseif ($side === 'buy') {
            $sideInternal = 1;
        }
        // Converting them to internal representation:
        // - Quantity => Internal = External * (10^underlyingDecimals)
        // - Price => Internal = External * (2^32) * (10^(settlementDecimals-underlyingDecimals))
        // - FeeRate => Internal = External * (10^8)
        $amountStr = $this->amount_to_precision($this->safe_string($market, 'symbol'), $amount);
        $feeRateStr = $this->number_to_string($feeRate);
        $info = $this->safe_dict($market, 'info');
        $underlying = '1e' . $this->safe_string($info, 'underlyingDecimals');
        $settlement = '1e' . $this->safe_string($info, 'settlementDecimals');
        $one = '1';
        $feeRateFactor = '100000000'; // 10^8
        $priceFactor = '4294967296'; // 2^32
        $quantityInternal = Precise::string_div(Precise::string_mul($amountStr, $underlying), $one, 0);
        $feeRateInternal = Precise::string_div(Precise::string_mul($feeRateStr, $feeRateFactor), $one, 0);
        // Encoding
        $nonce16 = $this->int_to_base16($nonce);
        $noncePadded = str_pad($nonce16, 16, '0', STR_PAD_LEFT);
        $encodedNonce = $this->base16_to_binary($noncePadded);
        $numericId = $this->int_to_base16($this->safe_integer($market, 'numericId'));
        $numericIdPadded = str_pad($numericId, 8, '0', STR_PAD_LEFT);
        $encodedMarketId = $this->base16_to_binary($numericIdPadded);
        $quantity16 = $this->int_to_base16($this->parse_to_int($quantityInternal));
        $quantityPadded = str_pad($quantity16, 16, '0', STR_PAD_LEFT);
        $encodedQuantity = $this->base16_to_binary($quantityPadded);
        $sideInternal16 = $this->int_to_base16($sideInternal);
        $sidePadded = str_pad($sideInternal16, 8, '0', STR_PAD_LEFT);
        $encodedSide = $this->base16_to_binary($sidePadded);
        $feeRateInternal16 = $this->int_to_base16($this->parse_to_int($feeRateInternal));
        $feeRatePadded = str_pad($feeRateInternal16, 16, '0', STR_PAD_LEFT);
        $encodedFeeRate = $this->base16_to_binary($feeRatePadded);
        $encodedPrice = $this->binary_concat();
        if ($type === 'limit') {
            $priceStr = $this->price_to_precision($this->safe_string($market, 'symbol'), $price);
            $priceInternal = Precise::string_div(Precise::string_div(Precise::string_mul(Precise::string_mul($priceStr, $priceFactor), $settlement), $underlying), $one, 0);
            $price16 = $this->int_to_base16($this->parse_to_int($priceInternal));
            $pricePadded = str_pad($price16, 16, '0', STR_PAD_LEFT);
            $encodedPrice = $this->base16_to_binary($pricePadded);
        }
        $message = $this->binary_concat($encodedNonce, $encodedMarketId, $encodedQuantity, $encodedSide, $encodedPrice, $encodedFeeRate);
        return $message;
    }

    public function create_order_request(float $nonce, string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()) {
        $market = $this->market($symbol);
        $feeRate = max ($this->safe_number($market, 'taker'), $this->safe_number($market, 'maker'));
        $sideInternal = '';
        if ($side === 'sell') {
            $sideInternal = 'ASK';
        } elseif ($side === 'buy') {
            $sideInternal = 'BID';
        }
        $priceInternal = '';
        if ($price) {
            $priceInternal = $this->price_to_precision($symbol, $price);
        }
        $message = $this->order_message($market, $nonce, $feeRate, $type, $side, $amount, $price);
        $signature = $this->sign_message($message, $this->privateKey);
        $request = array(
            'symbol' => $this->safe_string($market, 'id'),
            'nonce' => $nonce,
            'side' => $sideInternal,
            'orderType' => strtoupper($type),
            'quantity' => $this->amount_to_precision($symbol, $amount),
            'price' => $priceInternal,
            'signature' => $signature,
            'maxFeesPercent' => $this->number_to_string($feeRate),
        );
        $postOnly = $this->is_post_only(strtoupper($type) === 'MARKET', null, $params);
        $reduceOnly = $this->safe_bool_2($params, 'reduceOnly', 'reduce_only');
        $timeInForce = $this->safe_string_lower($params, 'timeInForce');
        $triggerPrice = $this->safe_string_2($params, 'triggerPrice', 'stopPrice');
        if ($postOnly) {
            $request['orderFlags'] = 'POST_ONLY';
        } elseif ($timeInForce === 'ioc') {
            $request['orderFlags'] = 'IOC';
        } elseif ($reduceOnly) {
            $request['orderFlags'] = 'REDUCE_ONLY';
        }
        if ($triggerPrice !== null) {
            $request['triggerPrice'] = $triggerPrice;
        }
        $params = $this->omit($params, array( 'reduceOnly', 'reduce_only', 'postOnly', 'timeInForce', 'stopPrice', 'triggerPrice' ));
        return $this->extend($request, $params);
    }

    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-doc.hibachi.xyz/#00f6d5ad-5275-41cb-a1a8-19ed5d142124
             *
             * @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
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $nonce = $this->nonce();
            $request = $this->create_order_request($nonce, $symbol, $type, $side, $amount, $price, $params);
            $request['accountId'] = $this->get_account_id();
            $response = Async\await($this->privatePostTradeOrder ($request));
            //
            // {
            //     "orderId" => "578721673790138368"
            // }
            //
            return $this->safe_order(array(
                'id' => $this->safe_string($response, 'orderId'),
                'status' => 'pending',
            ));
        }) ();
    }

    public function create_orders(array $orders, $params = array ()): PromiseInterface {
        return Async\async(function () use ($orders, $params) {
            /**
             * *contract only* create a list of trade $orders
             *
             * @see https://api-doc.hibachi.xyz/#c2840b9b-f02c-44ed-937d-dc2819f135b4
             *
             * @param {Array} $orders list of $orders to create, each object should contain the parameters required by createOrder, namely $symbol, $type, $side, $amount, $price and $params
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $nonce = $this->nonce();
            $requestOrders = array();
            for ($i = 0; $i < count($orders); $i++) {
                $rawOrder = $orders[$i];
                $symbol = $this->safe_string($rawOrder, 'symbol');
                $type = $this->safe_string($rawOrder, 'type');
                $side = $this->safe_string($rawOrder, 'side');
                $amount = $this->safe_value($rawOrder, 'amount');
                $price = $this->safe_value($rawOrder, 'price');
                $orderParams = $this->safe_dict($rawOrder, 'params', array());
                $orderRequest = $this->create_order_request($nonce . $i, $symbol, $type, $side, $amount, $price, $orderParams);
                $orderRequest['action'] = 'place';
                $requestOrders[] = $orderRequest;
            }
            $request = array(
                'accountId' => $this->get_account_id(),
                'orders' => $requestOrders,
            );
            $response = Async\await($this->privatePostTradeOrders ($this->extend($request, $params)));
            //
            // array( "orders" => array( array( $nonce => '1754349993908', orderId => '589642085255349248' ) ) )
            //
            $ret = array();
            $responseOrders = $this->safe_list($response, 'orders');
            for ($i = 0; $i < count($responseOrders); $i++) {
                $responseOrder = $responseOrders[$i];
                $ret[] = $this->safe_order(array(
                    'info' => $responseOrder,
                    'id' => $this->safe_string($responseOrder, 'orderId'),
                    'status' => 'pending',
                ));
            }
            return $ret;
        }) ();
    }

    public function edit_order_request(float $nonce, string $id, string $symbol, string $type, string $side, ?float $amount = null, ?float $price = null, $params = array ()) {
        $market = $this->market($symbol);
        $feeRate = max ($this->safe_number($market, 'taker'), $this->safe_number($market, 'maker'));
        $message = $this->order_message($market, $nonce, $feeRate, $type, $side, $amount, $price);
        $signature = $this->sign_message($message, $this->privateKey);
        $request = array(
            'orderId' => $id,
            'nonce' => $nonce,
            'updatedQuantity' => $this->amount_to_precision($symbol, $amount),
            'updatedPrice' => $this->price_to_precision($symbol, $price),
            'maxFeesPercent' => $this->number_to_string($feeRate),
            'signature' => $signature,
        );
        return $this->extend($request, $params);
    }

    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) {
            /**
             * edit a limit order that is not matched
             *
             * @see https://api-doc.hibachi.xyz/#94d2cdaf-1c71-440f-a981-da1112824810
             *
             * @param {string} $id order $id
             * @param {string} $symbol unified $symbol of the market to create an order in
             * @param {string} $type must be 'limit'
             * @param {string} $side 'buy' or 'sell', should stay the same with original $side
             * @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
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $nonce = $this->nonce();
            $request = $this->edit_order_request($nonce, $id, $symbol, $type, $side, $amount, $price, $params);
            $request['accountId'] = $this->get_account_id();
            Async\await($this->privatePutTradeOrder ($request));
            // At this time the response body is empty. A 200 response means the update $request is accepted and sent to process
            //
            // array()
            //
            return $this->safe_order(array(
                'id' => $id,
                'status' => 'pending',
            ));
        }) ();
    }

    public function edit_orders(array $orders, $params = array ()): PromiseInterface {
        return Async\async(function () use ($orders, $params) {
            /**
             * edit a list of trade $orders
             *
             * @see https://api-doc.hibachi.xyz/#c2840b9b-f02c-44ed-937d-dc2819f135b4
             *
             * @param {Array} $orders list of $orders to edit, each object should contain the parameters required by editOrder, namely $id, $symbol, $type, $side, $amount, $price and $params
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $nonce = $this->nonce();
            $requestOrders = array();
            for ($i = 0; $i < count($orders); $i++) {
                $rawOrder = $orders[$i];
                $id = $this->safe_string($rawOrder, 'id');
                $symbol = $this->safe_string($rawOrder, 'symbol');
                $type = $this->safe_string($rawOrder, 'type');
                $side = $this->safe_string($rawOrder, 'side');
                $amount = $this->safe_value($rawOrder, 'amount');
                $price = $this->safe_value($rawOrder, 'price');
                $orderParams = $this->safe_dict($rawOrder, 'params', array());
                $orderRequest = $this->edit_order_request($nonce . $i, $id, $symbol, $type, $side, $amount, $price, $orderParams);
                $orderRequest['action'] = 'modify';
                $requestOrders[] = $orderRequest;
            }
            $request = array(
                'accountId' => $this->get_account_id(),
                'orders' => $requestOrders,
            );
            $response = Async\await($this->privatePostTradeOrders ($this->extend($request, $params)));
            //
            // array( "orders" => array( array( "orderId" => "589636801329628160" ) ) )
            //
            $ret = array();
            $responseOrders = $this->safe_list($response, 'orders');
            for ($i = 0; $i < count($responseOrders); $i++) {
                $responseOrder = $responseOrders[$i];
                $ret[] = $this->safe_order(array(
                    'info' => $responseOrder,
                    'id' => $this->safe_string($responseOrder, 'orderId'),
                    'status' => 'pending',
                ));
            }
            return $ret;
        }) ();
    }

    public function cancel_order_request(string $id) {
        $bigid = $this->convert_to_big_int($id);
        $idbase16 = $this->int_to_base16($bigid);
        $idPadded = str_pad($idbase16, 16, '0', STR_PAD_LEFT);
        $message = $this->base16_to_binary($idPadded);
        $signature = $this->sign_message($message, $this->privateKey);
        return array(
            'orderId' => $id,
            'signature' => $signature,
        );
    }

    public function cancel_order(string $id, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($id, $symbol, $params) {
            /**
             *
             * @see https://api-doc.hibachi.xyz/#e99c4f48-e610-4b7c-b7f6-1b4bb7af0271
             *
             * cancels an open order
             * @param {string} $id order $id
             * @param {string} $symbol is unused
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
             */
            $request = $this->cancel_order_request($id);
            $request['accountId'] = $this->get_account_id();
            $response = Async\await($this->privateDeleteTradeOrder ($this->extend($request, $params)));
            // At this time the $response body is empty. A 200 $response means the cancel $request is accepted and sent to cancel
            //
            // array()
            //
            return $this->safe_order(array(
                'info' => $response,
                'id' => $id,
                'status' => 'canceled',
            ));
        }) ();
    }

    public function cancel_orders(array $ids, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($ids, $symbol, $params) {
            /**
             * cancel multiple $orders
             *
             * @see https://api-doc.hibachi.xyz/#c2840b9b-f02c-44ed-937d-dc2819f135b4
             *
             * @param {string[]} $ids order $ids
             * @param {string} [$symbol] unified market $symbol, unused
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            $orders = array();
            for ($i = 0; $i < count($ids); $i++) {
                $orderRequest = $this->cancel_order_request($ids[$i]);
                $orderRequest['action'] = 'cancel';
                $orders[] = $orderRequest;
            }
            $request = array(
                'accountId' => $this->get_account_id(),
                'orders' => $orders,
            );
            $response = Async\await($this->privatePostTradeOrders ($this->extend($request, $params)));
            //
            // array( "orders" => array( array( "orderId" => "589636801329628160" ) ) )
            //
            $ret = array();
            $responseOrders = $this->safe_list($response, 'orders');
            for ($i = 0; $i < count($responseOrders); $i++) {
                $responseOrder = $responseOrders[$i];
                $ret[] = $this->safe_order(array(
                    'info' => $responseOrder,
                    'id' => $this->safe_string($responseOrder, 'orderId'),
                    'status' => 'canceled',
                ));
            }
            return $ret;
        }) ();
    }

    public function cancel_all_orders(?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($symbol, $params) {
            /**
             *
             * @see https://api-doc.hibachi.xyz/#8ed24695-016e-49b2-a72d-7511ca921fee
             *
             * cancel all open orders in a $market
             * @param {string} $symbol unified $market $symbol
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $nonce = $this->nonce();
            $nonce16 = $this->int_to_base16($nonce);
            $noncePadded = str_pad($nonce16, 16, '0', STR_PAD_LEFT);
            $message = $this->base16_to_binary($noncePadded);
            $signature = $this->sign_message($message, $this->privateKey);
            $request = array(
                'accountId' => $this->get_account_id(),
                'nonce' => $nonce,
                'signature' => $signature,
            );
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['contractId'] = $this->safe_integer($market, 'numericId');
            }
            $response = Async\await($this->privateDeleteTradeOrders ($this->extend($request, $params)));
            // At this time the $response body is empty. A 200 $response means the cancel $request is accepted and sent to process
            //
            // array()
            //
            return array(
                $this->safe_order(array(
                    'info' => $response,
                )),
            );
        }) ();
    }

    public function encode_withdraw_message(float $amount, float $maxFees, string $address) {
        // Converting them to internal representation:
        // - Quantity => Internal = External * (10^6)
        // - $maxFees => Internal = External * (10^6)
        // We only have USDT currency time
        $USDTAssetId = 1;
        $USDTFactor = '1000000';
        $amountStr = $this->number_to_string($amount);
        $maxFeesStr = $this->number_to_string($maxFees);
        $one = '1';
        $quantityInternal = Precise::string_div(Precise::string_mul($amountStr, $USDTFactor), $one, 0);
        $maxFeesInternal = Precise::string_div(Precise::string_mul($maxFeesStr, $USDTFactor), $one, 0);
        // Encoding
        $usdtAsset16 = $this->int_to_base16($USDTAssetId);
        $usdtAssetPadded = str_pad($usdtAsset16, 8, '0', STR_PAD_LEFT);
        $encodedAssetId = $this->base16_to_binary($usdtAssetPadded);
        $quantity16 = $this->int_to_base16($this->parse_to_int($quantityInternal));
        $quantityPadded = str_pad($quantity16, 16, '0', STR_PAD_LEFT);
        $encodedQuantity = $this->base16_to_binary($quantityPadded);
        $maxFees16 = $this->int_to_base16($this->parse_to_int($maxFeesInternal));
        $maxFeesPadded = str_pad($maxFees16, 16, '0', STR_PAD_LEFT);
        $encodedMaxFees = $this->base16_to_binary($maxFeesPadded);
        $encodedAddress = $this->base16_to_binary($address);
        $message = $this->binary_concat($encodedAssetId, $encodedQuantity, $encodedMaxFees, $encodedAddress);
        return $message;
    }

    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-doc.hibachi.xyz/#6421625d-3e45-45fa-be9b-d2a0e780c090
             *
             * @param {string} $code unified currency $code, only support USDT
             * @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~
             */
            $withdrawAddress = mb_substr($address, -40);
            // Get the withdraw fees
            $exchangeInfo = Async\await($this->publicGetMarketExchangeInfo ($params));
            // {
            //      "feeConfig" => array(
            //          "depositFees" => "0.004518",
            //          "tradeMakerFeeRate" => "0.00000000",
            //          "tradeTakerFeeRate" => "0.00020000",
            //          "transferFeeRate" => "0.00010000",
            //          "withdrawalFees" => "0.012050"
            //    ),
            // }
            $feeConfig = $this->safe_dict($exchangeInfo, 'feeConfig');
            $maxFees = $this->safe_number($feeConfig, 'withdrawalFees');
            // Generate the $signature
            $message = $this->encode_withdraw_message($amount, $maxFees, $withdrawAddress);
            $signature = $this->sign_message($message, $this->privateKey);
            $request = array(
                'accountId' => $this->get_account_id(),
                'coin' => 'USDT',
                'network' => 'ARBITRUM',
                'withdrawAddress' => $withdrawAddress,
                'selfWithdrawal' => false,
                'quantity' => $this->number_to_string($amount),
                'maxFees' => $this->number_to_string($maxFees),
                'signature' => $signature,
            );
            Async\await($this->privatePostCapitalWithdraw ($this->extend($request, $params)));
            // At this time the response body is empty. A 200 response means the withdraw $request is accepted and sent to process
            //
            // array()
            //
            return array(
                'info' => null,
                'id' => null,
                'txid' => null,
                'timestamp' => $this->milliseconds(),
                'datetime' => null,
                'address' => null,
                'addressFrom' => null,
                'addressTo' => $withdrawAddress,
                'tag' => null,
                'tagFrom' => null,
                'tagTo' => null,
                'type' => 'withdrawal',
                'amount' => $amount,
                'currency' => $code,
                'status' => 'pending',
                'fee' => array( 'currency' => 'USDT', 'cost' => $maxFees ),
                'network' => 'ARBITRUM',
                'updated' => null,
                'comment' => null,
                'internal' => null,
            );
        }) ();
    }

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

    public function sign_message($message, $privateKey) {
        if (strlen($privateKey) === 44) {
            // For Exchange Managed account, the key length is 44 and we use HMAC to sign the $message
            return $this->hmac($message, $this->encode($privateKey), 'sha256', 'hex');
        } else {
            // For Trustless account, the key length is 66 including '0x' and we use ECDSA to sign the $message
            $hash = $this->hash($this->encode($message), 'sha256', 'hex');
            $signature = $this->ecdsa(mb_substr($hash, -64), mb_substr($privateKey, -64), 'secp256k1', null);
            $r = $signature['r'];
            $s = $signature['s'];
            $v = $signature['v'];
            return str_pad($r, 64, '0', STR_PAD_LEFT) . str_pad($s, 64, '0', STR_PAD_LEFT) . $this->int_to_base16($v).padStart (2, '0');
        }
    }

    public function fetch_order_book(string $symbol, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $limit, $params) {
            /**
             * fetches the state of the open orders on the orderbook
             *
             * @see https://api-doc.hibachi.xyz/#4abb30c4-e5c7-4b0f-9ade-790111dbfa47
             *
             * @param {string} $symbol unified $symbol of the $market
             * @param {int} [$limit] currently unused
             * @param {array} [$params] extra parameters to be passed -- see documentation link above
             * @return {array} A dictionary containg ~@link https://docs.ccxt.com/#/?id=order-book-structure orderbook information~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $response = Async\await($this->publicGetMarketDataOrderbook ($this->extend($request, $params)));
            $formattedResponse = array();
            $formattedResponse['ask'] = $this->safe_list($this->safe_dict($response, 'ask'), 'levels');
            $formattedResponse['bid'] = $this->safe_list($this->safe_dict($response, 'bid'), 'levels');
            // {
            //     "ask" => {
            //         "endPrice" => "3512.63",
            //         "levels" => array(
            //             array(
            //                 "price" => "3511.93",
            //                 "quantity" => "0.284772482"
            //             ),
            //             array(
            //                 "price" => "3512.28",
            //                 "quantity" => "0.569544964"
            //             ),
            //             array(
            //                 "price" => "3512.63",
            //                 "quantity" => "0.854317446"
            //             }
            //         ),
            //         "startPrice" => "3511.93"
            //     ),
            //     "bid" => {
            //         "endPrice" => "3510.87",
            //         "levels" => array(
            //             array(
            //                 "price" => "3515.39",
            //                 "quantity" => "2.345153070"
            //             ),
            //             array(
            //                 "price" => "3511.22",
            //                 "quantity" => "0.284772482"
            //             ),
            //             {
            //                 "price" => "3510.87",
            //                 "quantity" => "0.569544964"
            //             }
            //         ),
            //         "startPrice" => "3515.39"
            //     }
            // }
            return $this->parse_order_book($formattedResponse, $symbol, $this->milliseconds(), 'bid', 'ask', 'price', 'quantity');
        }) ();
    }

    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) {
            /**
             *
             * @see https://api-doc.hibachi.xyz/#0adbf143-189f-40e0-afdc-88af4cba3c79
             *
             * fetch all $trades made by the user
             * @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
             * @return {Trade[]} 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( 'accountId' => $this->get_account_id() );
            $response = Async\await($this->privateGetTradeAccountTrades ($this->extend($request, $params)));
            //
            // {
            //     "trades" => array(
            //         {
            //             "askAccountId" => 221,
            //             "askOrderId" => 589168494921909200,
            //             "bidAccountId" => 132,
            //             "bidOrderId" => 589168494829895700,
            //             "fee" => "0.000477",
            //             "id" => 199511136,
            //             "orderType" => "MARKET",
            //             "price" => "119257.90000",
            //             "quantity" => "0.0000200000",
            //             "realizedPnl" => "-0.000352",
            //             "side" => "Sell",
            //             "symbol" => "BTC/USDT-P",
            //             "timestamp" => 1752543391
            //         }
            //     )
            // }
            //
            $trades = $this->safe_list($response, 'trades');
            return $this->parse_trades($trades, $market, $since, $limit, $params);
        }) ();
    }

    public function parse_ohlcv($ohlcv, ?array $market = null): array {
        //
        // array(
        //     {
        //       "close" => "3704.751036",
        //       "high" => "3716.530378",
        //       "interval" => "1h",
        //       "low" => "3699.627883",
        //       "open" => "3716.406894",
        //       "timestamp" => 1712628000,
        //       "volumeNotional" => "1637355.846362"
        //     }
        //   )
        //
        return array(
            $this->safe_integer_product($ohlcv, 'timestamp', 1000),
            $this->safe_number($ohlcv, 'open'),
            $this->safe_number($ohlcv, 'high'),
            $this->safe_number($ohlcv, 'low'),
            $this->safe_number($ohlcv, 'close'),
            $this->safe_number($ohlcv, 'volumeNotional'),
        );
    }

    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) {
            /**
             * fetches all current open orders
             *
             * @see https://api-doc.hibachi.xyz/#3243f8a0-086c-44c5-ab8a-71bbb7bab403
             *
             * @param {string} [$symbol] unified $market $symbol to filter by
             * @param {int} [$since] milisecond timestamp of the earliest order
             * @param {int} [$limit] the maximum number of open orders to return
             * @param {array} [$params] extra parameters
             * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            $request = array(
                'accountId' => $this->get_account_id(),
            );
            $response = Async\await($this->privateGetTradeOrders ($this->extend($request, $params)));
            // array(
            //     array(
            //         "accountId" => 12452,
            //         "availableQuantity" => "0.0000230769",
            //         "contractId" => 2,
            //         "creationTime" => 1752684501,
            //         "orderId" => "589205486123876352",
            //         "orderType" => "LIMIT",
            //         "price" => "130000.00000",
            //         "side" => "ASK",
            //         "status" => "PLACED",
            //         "symbol" => "BTC/USDT-P",
            //         "totalQuantity" => "0.0000230769"
            //     ),
            //     {
            //         "accountId" => 12452,
            //         "availableQuantity" => "1.234000000",
            //         "contractId" => 1,
            //         "creationTime" => 1752240682,
            //         "orderId" => "589089141754429441",
            //         "orderType" => "LIMIT",
            //         "price" => "1.234000",
            //         "side" => "BID",
            //         "status" => "PLACED",
            //         "symbol" => "ETH/USDT-P",
            //         "totalQuantity" => "1.234000000"
            //     }
            // )
            return $this->parse_orders($response, $market, $since, $limit);
        }) ();
    }

    public function fetch_ohlcv(string $symbol, $timeframe = '1m', ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $timeframe, $since, $limit, $params) {
            /**
             *
             * @see  https://api-doc.hibachi.xyz/#4f0eacec-c61e-4d51-afb3-23c51c2c6bac
             *
             * fetches historical candlestick data containing the close, high, low, open prices, interval and the volumeNotional
             * @param {string} $symbol unified $symbol of the $market to fetch OHLCV data for
             * @param {string} $timeframe the length of time each candle represents
             * @param {int} [$since] timestamp in ms of the earliest candle to fetch
             * @param {int} [$limit] the maximum amount of candles to fetch
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {int} [$params->until] timestamp in ms of the latest candle to fetch
             * @return {int[][]} A list of candles ordered, open, high, low, close, volume
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $timeframe = $this->safe_string($this->timeframes, $timeframe, $timeframe);
            $request = array(
                'symbol' => $market['id'],
                'interval' => $timeframe,
            );
            if ($since !== null) {
                $request['fromMs'] = $since;
            }
            $until = null;
            list($until, $params) = $this->handle_option_and_params($params, 'fetchOHLCV', 'until');
            if ($until !== null) {
                $request['toMs'] = $until;
            }
            $response = Async\await($this->publicGetMarketDataKlines ($this->extend($request, $params)));
            //
            // array(
            //     {
            //       "close" => "3704.751036",
            //       "high" => "3716.530378",
            //       "interval" => "1h",
            //       "low" => "3699.627883",
            //       "open" => "3716.406894",
            //       "timestamp" => 1712628000,
            //       "volumeNotional" => "1637355.846362"
            //     }
            //   )
            //
            $klines = $this->safe_list($response, 'klines', array());
            return $this->parse_ohlcvs($klines, $market, $timeframe, $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-doc.hibachi.xyz/#69aafedb-8274-4e21-bbaf-91dace8b8f31
             *
             * @param {string[]} [$symbols] list of unified market $symbols
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=position-structure position structure~
             */
            Async\await($this->load_markets());
            $symbols = $this->market_symbols($symbols);
            $request = array(
                'accountId' => $this->get_account_id(),
            );
            $response = Async\await($this->privateGetTradeAccountInfo ($this->extend($request, $params)));
            //
            // {
            //     "assets" => array(
            //       {
            //         "quantity" => "14.130626",
            //         "symbol" => "USDT"
            //       }
            //     ),
            //     "balance" => "14.186087",
            //     "maximalWithdraw" => "4.152340",
            //     "numFreeTransfersRemaining" => 96,
            //     "positions" => array(
            //       array(
            //         "direction" => "Short",
            //         "entryNotional" => "10.302213",
            //         "notionalValue" => "10.225008",
            //         "quantity" => "0.004310550",
            //         "symbol" => "ETH/USDT-P",
            //         "unrealizedFundingPnl" => "0.000000",
            //         "unrealizedTradingPnl" => "0.077204"
            //       ),
            //       array(
            //         "direction" => "Short",
            //         "entryNotional" => "2.000016",
            //         "notionalValue" => "1.999390",
            //         "quantity" => "0.0000328410",
            //         "symbol" => "BTC/USDT-P",
            //         "unrealizedFundingPnl" => "0.000000",
            //         "unrealizedTradingPnl" => "0.000625"
            //       ),
            //       {
            //         "direction" => "Short",
            //         "entryNotional" => "2.000015",
            //         "notionalValue" => "2.022384",
            //         "quantity" => "0.01470600",
            //         "symbol" => "SOL/USDT-P",
            //         "unrealizedFundingPnl" => "0.000000",
            //         "unrealizedTradingPnl" => "-0.022369"
            //       }
            //     ),
            //   }
            //
            $data = $this->safe_list($response, 'positions', array());
            return $this->parse_positions($data, $symbols);
        }) ();
    }

    public function parse_position(array $position, ?array $market = null) {
        //
        // {
        //     "direction" => "Short",
        //     "entryNotional" => "10.302213",
        //     "notionalValue" => "10.225008",
        //     "quantity" => "0.004310550",
        //     "symbol" => "ETH/USDT-P",
        //     "unrealizedFundingPnl" => "0.000000",
        //     "unrealizedTradingPnl" => "0.077204"
        // }
        //
        $marketId = $this->safe_string($position, 'symbol');
        $market = $this->safe_market($marketId, $market);
        $symbol = $market['symbol'];
        $side = $this->safe_string_lower($position, 'direction');
        $quantity = $this->safe_string($position, 'quantity');
        $unrealizedFunding = $this->safe_string($position, 'unrealizedFundingPnl', '0');
        $unrealizedTrading = $this->safe_string($position, 'unrealizedTradingPnl', '0');
        $unrealizedPnl = Precise::string_add($unrealizedFunding, $unrealizedTrading);
        return $this->safe_position(array(
            'info' => $position,
            'id' => null,
            'symbol' => $symbol,
            'entryPrice' => $this->safe_string($position, 'average_entry_price'),
            'markPrice' => null,
            'notional' => $this->safe_string($position, 'notionalValue'),
            'collateral' => null,
            'unrealizedPnl' => $unrealizedPnl,
            'side' => $side,
            'contracts' => $this->parse_number($quantity),
            'contractSize' => null,
            'timestamp' => null,
            'datetime' => null,
            'hedged' => null,
            'maintenanceMargin' => null,
            'maintenanceMarginPercentage' => null,
            'initialMargin' => null,
            'initialMarginPercentage' => null,
            'leverage' => null,
            'liquidationPrice' => null,
            'marginRatio' => null,
            'marginMode' => null,
            'percentage' => null,
        ));
    }

    public function sign($path, $api = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) {
        $endpoint = '/' . $this->implode_params($path, $params);
        $url = $this->urls['api'][$api] . $endpoint;
        $headers = array( 'Hibachi-Client' => 'HibachiCCXT/unversioned' );
        if ($method === 'GET') {
            $request = $this->omit($params, $this->extract_params($path));
            $query = $this->urlencode($request);
            if (strlen($query) !== 0) {
                $url .= '?' . $query;
            }
        }
        if ($method === 'POST' || $method === 'PUT' || $method === 'DELETE') {
            $headers['Content-Type'] = 'application/json';
            $body = $this->json($params);
        }
        if ($api === 'private') {
            $this->check_required_credentials();
            $headers['Authorization'] = $this->apiKey;
        }
        return array( 'url' => $url, 'method' => $method, 'body' => $body, 'headers' => $headers );
    }

    public function handle_errors(int $httpCode, string $reason, string $url, string $method, array $headers, string $body, $response, $requestHeaders, $requestBody) {
        if ($response === null) {
            return null; // fallback to default error handler
        }
        if (is_array($response) && array_key_exists('status', $response)) {
            //
            //     array("errorCode":4,"message":"Invalid input => Invalid quantity => 0","status":"failed")
            //
            $status = $this->safe_string($response, 'status');
            if ($status === 'failed') {
                $code = $this->safe_string($response, 'errorCode');
                $feedback = $this->id . ' ' . $body;
                $this->throw_broadly_matched_exception($this->exceptions['broad'], $body, $feedback);
                $this->throw_exactly_matched_exception($this->exceptions['exact'], $code, $feedback);
                $message = $this->safe_string($response, 'message');
                $this->throw_exactly_matched_exception($this->exceptions['exact'], $message, $feedback);
                throw new ExchangeError($feedback);
            }
        }
        return null;
    }

    public function parse_transaction_type($type) {
        $types = array(
            'deposit' => 'transaction',
            'withdrawal' => 'transaction',
            'transfer-in' => 'transfer',
            'transfer-out' => 'transfer',
        );
        return $this->safe_string($types, $type, $type);
    }

    public function parse_transaction_status($status) {
        $statuses = array(
            'pending' => 'pending',
            'claimable' => 'pending',
            'completed' => 'ok',
            'failed' => 'canceled',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function parse_ledger_entry(array $item, ?array $currency = null): array {
        $transactionType = $this->safe_string($item, 'transactionType');
        $timestamp = null;
        $type = null;
        $direction = null;
        $amount = null;
        $fee = null;
        $referenceId = null;
        $referenceAccount = null;
        $status = null;
        if ($transactionType === null) {
            // response from TradeAccountTradingHistory
            $timestamp = $this->safe_integer_product($item, 'timestamp', 1000);
            $type = 'trade';
            $amountStr = $this->safe_string($item, 'realizedPnl');
            if (Precise::string_lt($amountStr, '0')) {
                $direction = 'out';
                $amountStr = Precise::string_neg($amountStr);
            } else {
                $direction = 'in';
            }
            $amount = $this->parse_number($amountStr);
            $fee = array( 'currency' => 'USDT', 'cost' => $this->safe_number($item, 'fee') );
            $status = 'ok';
        } else {
            // response from CapitalHistory
            $timestamp = $this->safe_integer_product($item, 'timestampSec', 1000);
            $amount = $this->safe_number($item, 'quantity');
            $direction = ($transactionType === 'deposit' || $transactionType === 'transfer-in') ? 'in' : 'out';
            $type = $this->parse_transaction_type($transactionType);
            $status = $this->parse_transaction_status($this->safe_string($item, 'status'));
            if ($transactionType === 'transfer-in') {
                $referenceAccount = $this->safe_string($item, 'srcAccountId');
            } elseif ($transactionType === 'transfer-out') {
                $referenceAccount = $this->safe_string($item, 'receivingAccountId');
            }
            $referenceId = $this->safe_string($item, 'transactionHash');
        }
        return $this->safe_ledger_entry(array(
            'id' => $this->safe_string($item, 'id'),
            'currency' => $this->currency('USDT'),
            'account' => $this->number_to_string($this->accountId),
            'referenceAccount' => $referenceAccount,
            'referenceId' => $referenceId,
            'status' => $status,
            'amount' => $amount,
            'before' => null,
            'after' => null,
            'fee' => $fee,
            'direction' => $direction,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'type' => $type,
            'info' => $item,
        ), $currency);
    }

    public function fetch_ledger(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch the history of changes, actions done by the user or operations that altered the balance of the user
             *
             * @see https://api-doc.hibachi.xyz/#35125e3f-d154-4bfd-8276-a48bb1c62020
             *
             * @param {string} [$code] unified $currency $code, default is null
             * @param {int} [$since] timestamp in ms of the earliest ledger entry, default is null
             * @param {int} [$limit] max number of ledger entries to return, default is null
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=ledger ledger structure~
             */
            Async\await($this->load_markets());
            $currency = $this->currency('USDT');
            $request = array( 'accountId' => $this->get_account_id() );
            $rawPromises = array(
                $this->privateGetCapitalHistory ($this->extend($request, $params)),
                $this->privateGetTradeAccountTradingHistory ($this->extend($request, $params)),
            );
            $promises = Async\await(Promise\all($rawPromises));
            $responseCapitalHistory = $promises[0];
            //
            // {
            //     "transactions" => array(
            //         array(
            //             "assetId" => 1,
            //             "blockNumber" => 358396669,
            //             "chain" => "Arbitrum",
            //             "etaTsSec" => null,
            //             "id" => 358396669,
            //             "quantity" => "0.999500",
            //             "status" => "pending",
            //             "timestampSec" => 1752692872,
            //             "token" => "USDT",
            //             "transactionHash" => "0x408e48881e0ba77d8638e3fe57bc06bdec513ddaa8b672e0aefa7e22e2f18b5e",
            //             "transactionType" => "deposit"
            //         ),
            //         array(
            //             "assetId" => 1,
            //             "etaTsSec" => null,
            //             "id" => 13116,
            //             "instantWithdrawalChain" => null,
            //             "instantWithdrawalToken" => null,
            //             "isInstantWithdrawal" => false,
            //             "quantity" => "0.040000",
            //             "status" => "completed",
            //             "timestampSec" => 1752542708,
            //             "transactionHash" => "0xe89cf90b2408d1a273dc9427654145def102d9449e5e2cfc10690ccffc3d7e28",
            //             "transactionType" => "withdrawal",
            //             "withdrawalAddress" => "0x23625d5fc6a6e32638d908eb4c3a3415e5121f76"
            //         ),
            //         array(
            //             "assetId" => 1,
            //             "id" => 167,
            //             "quantity" => "10.000000",
            //             "srcAccountId" => 175,
            //             "srcAddress" => "0xc2f77ce029438a3fdfe68ddee25991a9fb985a86",
            //             "status" => "completed",
            //             "timestampSec" => 1732224729,
            //             "transactionType" => "transfer-in"
            //         ),
            //         array(
            //             "assetId" => 1,
            //             "id" => 170,
            //             "quantity" => "10.000000",
            //             "receivingAccountId" => 175,
            //             "receivingAddress" => "0xc2f77ce029438a3fdfe68ddee25991a9fb985a86",
            //             "status" => "completed",
            //             "timestampSec" => 1732225631,
            //             "transactionType" => "transfer-out"
            //         ),
            //     )
            // }
            //
            $rowsCapitalHistory = $this->safe_list($responseCapitalHistory, 'transactions');
            $responseTradingHistory = $promises[1];
            //
            // {
            //     "tradingHistory" => array(
            //         array(
            //             "eventType" => "MARKET",
            //             "fee" => "0.000008",
            //             "priceOrFundingRate" => "119687.82481",
            //             "quantity" => "0.0000003727",
            //             "realizedPnl" => "0.004634",
            //             "side" => "Sell",
            //             "symbol" => "BTC/USDT-P",
            //             "timestamp" => 1752522571
            //         ),
            //         array(
            //             "eventType" => "FundingEvent",
            //             "fee" => "0",
            //             "priceOrFundingRate" => "0.000203",
            //             "quantity" => "0.0000003727",
            //             "realizedPnl" => "-0.000009067899008751979",
            //             "side" => "Long",
            //             "symbol" => "BTC/USDT-P",
            //             "timestamp" => 1752508800
            //         ),
            //     )
            // }
            //
            $rowsTradingHistory = $this->safe_list($responseTradingHistory, 'tradingHistory');
            $rows = $this->array_concat($rowsCapitalHistory, $rowsTradingHistory);
            return $this->parse_ledger($rows, $currency, $since, $limit, $params);
        }) ();
    }

    public function fetch_deposit_address(string $code, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $params) {
            /**
             * fetch deposit address for given currency and chain. currently, we have a single EVM address across multiple EVM chains. Note => This method is currently only supported for trustless accounts
             * @param {string} $code unified currency $code
             * @param {array} [$params] extra parameters for API
             * @param {string} [$params->publicKey] your public key, you can get it from UI after creating API key
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=address-structure address structure~
             */
            $request = array(
                'publicKey' => $this->safe_string($params, 'publicKey'),
                'accountId' => $this->get_account_id(),
            );
            $response = Async\await($this->privateGetCapitalDepositInfo ($this->extend($request, $params)));
            // {
            //     "depositAddressEvm" => "0x0b95d90b9345dadf1460bd38b9f4bb0d2f4ed788"
            // }
            return array(
                'info' => $response,
                'currency' => 'USDT',
                'network' => 'ARBITRUM',
                'address' => $this->safe_string($response, 'depositAddressEvm'),
                'tag' => null,
            );
        }) ();
    }

    public function parse_transaction(array $transaction, ?array $currency = null): array {
        $timestamp = $this->safe_integer_product($transaction, 'timestampSec', 1000);
        $address = $this->safe_string($transaction, 'withdrawalAddress');
        $transactionType = $this->safe_string($transaction, 'transactionType');
        if ($transactionType !== 'deposit' && $transactionType !== 'withdrawal') {
            $transactionType = $this->parse_transaction_type($transactionType);
        }
        return array(
            'info' => $transaction,
            'id' => $this->safe_string($transaction, 'id'),
            'txid' => $this->safe_string($transaction, 'transactionHash'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'network' => 'ARBITRUM', // Currently the exchange only exists on Arbitrum,
            'address' => $address,
            'addressTo' => $address,
            'addressFrom' => null,
            'tag' => null,
            'tagTo' => null,
            'tagFrom' => null,
            'type' => $transactionType,
            'amount' => $this->safe_number($transaction, 'quantity'),
            'currency' => 'USDT',
            'status' => $this->parse_transaction_status($this->safe_string($transaction, 'status')),
            'updated' => null,
            'internal' => null,
            'comment' => null,
            'fee' => null,
        );
    }

    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 $deposits made to account
             *
             * @see https://api-doc.hibachi.xyz/#35125e3f-d154-4bfd-8276-a48bb1c62020
             *
             * @param {string} [$code] unified $currency $code
             * @param {int} [$since] filter by earliest timestamp (ms)
             * @param {int} [$limit] maximum number of $deposits to be returned
             * @param {array} [$params] extra parameters to be passed to API
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=$transaction-structure $transaction structures~
             */
            $currency = $this->safe_currency($code);
            $request = array(
                'accountId' => $this->get_account_id(),
            );
            $response = Async\await($this->privateGetCapitalHistory ($this->extend($request, $params)));
            // {
            //     "transactions" => array(
            //         array(
            //             "assetId" => 1,
            //             "blockNumber" => 0,
            //             "chain" => null,
            //             "etaTsSec" => 1752758789,
            //             "id" => 42688,
            //             "quantity" => "6.130000",
            //             "status" => "completed",
            //             "timestampSec" => 1752758788,
            //             "token" => null,
            //             "transactionHash" => "0x8dcd7bd1155b5624fb5e38a1365888f712ec633a57434340e05080c70b0e3bba",
            //             "transactionType" => "deposit"
            //         ),
            //         array(
            //             "assetId" => 1,
            //             "etaTsSec" => null,
            //             "id" => 12993,
            //             "instantWithdrawalChain" => null,
            //             "instantWithdrawalToken" => null,
            //             "isInstantWithdrawal" => false,
            //             "quantity" => "0.111930",
            //             "status" => "completed",
            //             "timestampSec" => 1752387891,
            //             "transactionHash" => "0x32ab5fe5b90f6d753bab83523ebc8465eb9daef54580e13cb9ff031d400c5620",
            //             "transactionType" => "withdrawal",
            //             "withdrawalAddress" => "0x43f15ef2ef2ab5e61e987ee3d652a5872aea8a6c"
            //         ),
            //     )
            // }
            $transactions = $this->safe_list($response, 'transactions');
            $deposits = array();
            for ($i = 0; $i < count($transactions); $i++) {
                $transaction = $transactions[$i];
                if ($this->safe_string($transaction, 'transactionType') === 'deposit') {
                    $deposits[] = $transaction;
                }
            }
            return $this->parse_transactions($deposits, $currency, $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 $withdrawals made from account
             *
             * @see https://api-doc.hibachi.xyz/#35125e3f-d154-4bfd-8276-a48bb1c62020
             *
             * @param {string} [$code] unified $currency $code
             * @param {int} [$since] filter by earliest timestamp (ms)
             * @param {int} [$limit] maximum number of deposits to be returned
             * @param {array} [$params] extra parameters to be passed to API
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=$transaction-structure $transaction structures~
             */
            $currency = $this->safe_currency($code);
            $request = array(
                'accountId' => $this->get_account_id(),
            );
            $response = Async\await($this->privateGetCapitalHistory ($this->extend($request, $params)));
            // {
            //     "transactions" => array(
            //         array(
            //             "assetId" => 1,
            //             "blockNumber" => 0,
            //             "chain" => null,
            //             "etaTsSec" => 1752758789,
            //             "id" => 42688,
            //             "quantity" => "6.130000",
            //             "status" => "completed",
            //             "timestampSec" => 1752758788,
            //             "token" => null,
            //             "transactionHash" => "0x8dcd7bd1155b5624fb5e38a1365888f712ec633a57434340e05080c70b0e3bba",
            //             "transactionType" => "deposit"
            //         ),
            //         array(
            //             "assetId" => 1,
            //             "etaTsSec" => null,
            //             "id" => 12993,
            //             "instantWithdrawalChain" => null,
            //             "instantWithdrawalToken" => null,
            //             "isInstantWithdrawal" => false,
            //             "quantity" => "0.111930",
            //             "status" => "completed",
            //             "timestampSec" => 1752387891,
            //             "transactionHash" => "0x32ab5fe5b90f6d753bab83523ebc8465eb9daef54580e13cb9ff031d400c5620",
            //             "transactionType" => "withdrawal",
            //             "withdrawalAddress" => "0x43f15ef2ef2ab5e61e987ee3d652a5872aea8a6c"
            //         ),
            //     )
            // }
            $transactions = $this->safe_list($response, 'transactions');
            $withdrawals = array();
            for ($i = 0; $i < count($transactions); $i++) {
                $transaction = $transactions[$i];
                if ($this->safe_string($transaction, 'transactionType') === 'withdrawal') {
                    $withdrawals[] = $transaction;
                }
            }
            return $this->parse_transactions($withdrawals, $currency, $since, $limit, $params);
        }) ();
    }

    public function fetch_time($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * fetches the current integer timestamp in milliseconds from the exchange server
             *
             * @see http://api-doc.hibachi.xyz/#b5c6a3bc-243d-4d35-b6d4-a74c92495434
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {int} the current integer timestamp in milliseconds from the exchange server
             */
            $response = Async\await($this->publicGetExchangeUtcTimestamp ($params));
            //
            //     array( "timestampMs":1754077574040 )
            //
            return $this->safe_integer($response, 'timestampMs');
        }) ();
    }

    public function fetch_open_interest(string $symbol, $params = array ()) {
        return Async\async(function () use ($symbol, $params) {
            /**
             * retrieves the open interest of a contract trading pair
             *
             * @see https://api-doc.hibachi.xyz/#bc34e8ae-e094-4802-8d56-3efe3a7bad49
             *
             * @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=open-interest-structure)
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $response = Async\await($this->publicGetMarketDataOpenInterest ($this->extend($request, $params)));
            //
            //   array( "totalQuantity" : "2.3299770166" )
            //
            $timestamp = $this->milliseconds();
            return $this->safe_open_interest(array(
                'symbol' => $symbol,
                'openInterestAmount' => $this->safe_string($response, 'totalQuantity'),
                'openInterestValue' => null,
                'timestamp' => $timestamp,
                'datetime' => $this->iso8601($timestamp),
                'info' => $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-doc.hibachi.xyz/#bca696ca-b9b2-4072-8864-5d6b8c09807e
             *
             * @param {string} $symbol unified $market $symbol
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=$funding-rate-structure $funding rate structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $response = Async\await($this->publicGetMarketDataPrices ($this->extend($request, $params)));
            //
            // {
            //     "askPrice" => "3514.650296",
            //     "bidPrice" => "3513.596112",
            //     "fundingRateEstimation" => array(
            //         "estimatedFundingRate" => "0.000001",
            //         "nextFundingTimestamp" => 1712707200
            //     ),
            //     "markPrice" => "3514.288858",
            //     "spotPrice" => "3514.715000",
            //     "symbol" => "ETH/USDT-P",
            //     "tradePrice" => "2372.746570"
            // }
            //
            $funding = $this->safe_dict($response, 'fundingRateEstimation', array());
            $timestamp = $this->milliseconds();
            $nextFundingTimestamp = $this->safe_integer_product($funding, 'nextFundingTimestamp', 1000);
            return array(
                'info' => $funding,
                'symbol' => $market['symbol'],
                'markPrice' => null,
                'indexPrice' => null,
                'interestRate' => $this->parse_number('0'),
                'estimatedSettlePrice' => null,
                'timestamp' => $timestamp,
                'datetime' => $this->iso8601($timestamp),
                'fundingRate' => $this->safe_number($funding, 'estimatedFundingRate'),
                'fundingTimestamp' => $nextFundingTimestamp,
                'fundingDatetime' => $this->iso8601($nextFundingTimestamp),
                'nextFundingRate' => null,
                'nextFundingTimestamp' => null,
                'nextFundingDatetime' => null,
                'previousFundingRate' => null,
                'previousFundingTimestamp' => null,
                'previousFundingDatetime' => null,
                'interval' => '8h',
            );
        }) ();
    }

    public function fetch_funding_rate_history(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches historical funding rate prices
             *
             * @see https://api-doc.hibachi.xyz/#4abb30c4-e5c7-4b0f-9ade-790111dbfa47
             *
             * @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
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=funding-rate-history-structure funding rate structures~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $response = Async\await($this->publicGetMarketDataFundingRates ($this->extend($request, $params)));
            //
            // {
            //     "data" => array(
            //         {
            //             "contractId" => 2,
            //             "fundingTimestamp" => 1753488000,
            //             "fundingRate" => "0.000137",
            //             "indexPrice" => "117623.65010"
            //         }
            //     )
            // }
            //
            $data = $this->safe_list($response, 'data');
            $rates = array();
            for ($i = 0; $i < count($data); $i++) {
                $entry = $data[$i];
                $timestamp = $this->safe_integer_product($entry, 'fundingTimestamp', 1000);
                $rates[] = array(
                    'info' => $entry,
                    'symbol' => $symbol,
                    'fundingRate' => $this->safe_number($entry, 'fundingRate'),
                    'timestamp' => $timestamp,
                    'datetime' => $this->iso8601($timestamp),
                );
            }
            $sorted = $this->sort_by($rates, 'timestamp');
            return $this->filter_by_symbol_since_limit($sorted, $symbol, $since, $limit);
        }) ();
    }
}
