# -*- coding: utf-8 -*-

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

from ccxt_zagrava.async_support.base.exchange import Exchange
from ccxt_zagrava.abstract.oxfun import ImplicitAPI
import asyncio
import hashlib
from ccxt_zagrava.base.types import Account, Balances, Bool, Currencies, Currency, Int, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, Transaction, TransferEntry
from typing import List
from ccxt_zagrava.base.errors import ExchangeError
from ccxt_zagrava.base.errors import AuthenticationError
from ccxt_zagrava.base.errors import AccountNotEnabled
from ccxt_zagrava.base.errors import ArgumentsRequired
from ccxt_zagrava.base.errors import BadRequest
from ccxt_zagrava.base.errors import BadSymbol
from ccxt_zagrava.base.errors import MarketClosed
from ccxt_zagrava.base.errors import InsufficientFunds
from ccxt_zagrava.base.errors import InvalidOrder
from ccxt_zagrava.base.errors import OrderNotFound
from ccxt_zagrava.base.errors import NotSupported
from ccxt_zagrava.base.errors import OperationFailed
from ccxt_zagrava.base.errors import NetworkError
from ccxt_zagrava.base.errors import RateLimitExceeded
from ccxt_zagrava.base.errors import RequestTimeout
from ccxt_zagrava.base.decimal_to_precision import TICK_SIZE
from ccxt_zagrava.base.precise import Precise


class oxfun(Exchange, ImplicitAPI):

    def describe(self):
        return self.deep_extend(super(oxfun, self).describe(), {
            'id': 'oxfun',
            'name': 'OXFUN',
            'countries': ['PA'],  # Panama todo check
            'version': 'v3',
            'rateLimit': 120,  # 100 requests per second and 25000 per 5 minutes
            'pro': True,
            'has': {
                'CORS': None,
                'spot': True,
                'margin': False,
                'swap': True,
                'future': False,
                'option': False,
                'addMargin': False,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'cancelOrders': True,
                'closeAllPositions': False,
                'closePosition': False,
                'createDepositAddress': False,
                'createMarketBuyOrderWithCost': True,
                'createMarketOrderWithCost': False,
                'createMarketSellOrderWithCost': False,
                'createOrder': True,
                'createOrders': True,
                'createPostOnlyOrder': True,
                'createReduceOnlyOrder': False,
                'createStopLimitOrder': True,
                'createStopMarketOrder': True,
                'createStopOrder': True,
                'deposit': False,
                'editOrder': False,
                'fetchAccounts': True,
                'fetchBalance': True,
                'fetchBidsAsks': False,
                'fetchBorrowInterest': False,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchCanceledOrders': False,
                'fetchClosedOrder': False,
                'fetchClosedOrders': False,
                'fetchCrossBorrowRate': False,
                'fetchCrossBorrowRates': False,
                'fetchCurrencies': True,
                'fetchDeposit': False,
                'fetchDepositAddress': False,
                'fetchDepositAddresses': False,
                'fetchDepositAddressesByNetwork': False,
                'fetchDeposits': True,
                'fetchDepositWithdrawFee': False,
                'fetchDepositWithdrawFees': False,
                'fetchFundingHistory': True,
                'fetchFundingRate': False,
                'fetchFundingRateHistory': True,
                'fetchFundingRates': True,
                'fetchIndexOHLCV': False,
                'fetchIsolatedBorrowRate': False,
                'fetchIsolatedBorrowRates': False,
                'fetchL3OrderBook': False,
                'fetchLedger': False,
                'fetchLeverage': False,
                'fetchLeverageTiers': True,
                'fetchMarketLeverageTiers': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenInterestHistory': False,
                'fetchOpenOrder': False,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrderBooks': False,
                'fetchOrders': False,
                'fetchOrderTrades': False,
                'fetchPosition': False,
                'fetchPositionHistory': False,
                'fetchPositionMode': False,
                'fetchPositions': True,
                'fetchPositionsForSymbol': False,
                'fetchPositionsHistory': False,
                'fetchPositionsRisk': False,
                'fetchPremiumIndexOHLCV': False,
                'fetchStatus': False,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': False,
                'fetchTrades': True,
                'fetchTradingFee': False,
                'fetchTradingFees': False,
                'fetchTradingLimits': False,
                'fetchTransactionFee': False,
                'fetchTransactionFees': False,
                'fetchTransactions': False,
                'fetchTransfers': True,
                'fetchWithdrawal': False,
                'fetchWithdrawals': True,
                'fetchWithdrawalWhitelist': False,
                'reduceMargin': False,
                'repayCrossMargin': False,
                'repayIsolatedMargin': False,
                'setLeverage': False,
                'setMargin': False,
                'setMarginMode': False,
                'setPositionMode': False,
                'signIn': False,
                'transfer': True,
                'withdraw': True,
                'ws': True,
            },
            'timeframes': {
                '1m': '60s',
                '5m': '300s',
                '15m': '900s',
                '30m': '1800s',
                '1h': '3600s',
                '2h': '7200s',
                '4h': '14400s',
                '1d': '86400s',
            },
            'urls': {
                'logo': 'https://github.com/ccxt_zagrava/ccxt_zagrava/assets/43336371/6a196124-c1ee-4fae-8573-962071b61a85',
                'referral': 'https://ox.fun/register?shareAccountId=5ZUD4a7G',
                'api': {
                    'public': 'https://api.ox.fun',
                    'private': 'https://api.ox.fun',
                },
                'test': {
                    'public': 'https://stgapi.ox.fun',
                    'private': 'https://stgapi.ox.fun',
                },
                'www': 'https://ox.fun/',
                'doc': 'https://docs.ox.fun/',
                'fees': 'https://support.ox.fun/en/articles/8819866-trading-fees',
            },
            'api': {
                'public': {
                    'get': {
                        'v3/markets': 1,
                        'v3/assets': 1,
                        'v3/tickers': 1,
                        'v3/funding/estimates': 1,
                        'v3/candles': 1,
                        'v3/depth': 1,
                        'v3/markets/operational': 1,
                        'v3/exchange-trades': 1,
                        'v3/funding/rates': 1,
                        'v3/leverage/tiers': 1,
                    },
                },
                'private': {
                    'get': {
                        'v3/account': 1,
                        'v3/account/names': 1,
                        'v3/wallet': 1,  # retruns only FUNDING in OX
                        'v3/transfer': 1,
                        'v3/balances': 1,
                        'v3/positions': 1,
                        'v3/funding': 1,
                        'v3/deposit-addresses': 1,
                        'v3/deposit': 1,
                        'v3/withdrawal-addresses': 1,
                        'v3/withdrawal': 1,
                        'v3/withdrawal-fees': 1,
                        'v3/orders/status': 1,
                        'v3/orders/working': 1,
                        'v3/trades': 1,
                    },
                    'post': {
                        'v3/transfer': 1,
                        'v3/withdrawal': 1,
                        'v3/orders/place': 1,
                    },
                    'delete': {
                        'v3/orders/cancel': 1,
                        'v3/orders/cancel-all': 1,
                    },
                },
            },
            'fees': {
                'trading': {
                    'tierBased': True,
                    'percentage': True,
                    'maker': self.parse_number('0.00020'),
                    'taker': self.parse_number('0.00070'),
                    'tiers': {
                        'maker': [
                            [self.parse_number('0'), self.parse_number('0.00020')],
                            [self.parse_number('2500000'), self.parse_number('0.00010')],
                            [self.parse_number('25000000'), self.parse_number('0')],
                        ],
                        'taker': [
                            [self.parse_number('0'), self.parse_number('0.00070')],
                            [self.parse_number('2500000'), self.parse_number('0.00050')],
                            [self.parse_number('25000000'), self.parse_number('0.00040')],
                        ],
                    },
                },
            },
            'precisionMode': TICK_SIZE,
            # exchange-specific options
            'options': {
                'sandboxMode': False,
                'networks': {
                    'BTC': 'Bitcoin',
                    'ERC20': 'Ethereum',
                    'AVAX': 'Avalanche',
                    'SOL': 'Solana',
                    'ARB': 'Arbitrum',
                    'MATIC': 'Polygon',
                    'FTM': 'Fantom',
                    'BNB': 'BNBSmartChain',
                    'OPTIMISM': 'Optimism',
                },
                'networksById': {
                    'Bitcoin': 'BTC',
                    'Ethereum': 'ERC20',
                    'Avalanche': 'AVAX',
                    'Solana': 'SOL',
                    'Arbitrum': 'ARB',
                    'Polygon': 'MATIC',
                    'Fantom': 'FTM',
                    'Base': 'BASE',
                    'BNBSmartChain': 'BNB',
                    'Optimism': 'OPTIMISM',
                },
            },
            'exceptions': {
                'exact': {
                    '-0010': OperationFailed,  # {"event":null,"success":false,"message":"Validation failed","code":"0010","data":null} - failed transfer
                    '-429': RateLimitExceeded,  # Rate limit reached
                    '-05001': AuthenticationError,  # Your operation authority is invalid
                    '-10001': ExchangeError,  # General networking failure
                    '-20000': BadRequest,  # Signature is invalid
                    '-20001': BadRequest,  # "success":false,"code":"20001","message":"marketCode is invalid"
                    '-20002': BadRequest,  # Unexpected error, please check if your request data complies with the specification.
                    '-20003': NotSupported,  # Unrecognized operation
                    '-20005': AuthenticationError,  # Already logged in
                    '-20006': BadRequest,  # Quantity must be greater than zero
                    '-20007': AuthenticationError,  # You are accessing server too rapidly
                    '-20008': BadRequest,  # clientOrderId must be greater than zero if provided
                    '-20009': BadRequest,  # JSON data format is invalid
                    '-20010': ArgumentsRequired,  # Either clientOrderId or orderId is required
                    '-20011': ArgumentsRequired,  # marketCode is required
                    '-20012': ArgumentsRequired,  # side is required
                    '-20013': ArgumentsRequired,  # orderType is required
                    '-20014': BadRequest,  # clientOrderId is not long type
                    '-20015': BadSymbol,  # marketCode is invalid
                    '-20016': BadRequest,  # side is invalid
                    '-20017': BadRequest,  # orderType is invalid
                    '-20018': BadRequest,  # timeInForce is invalid
                    '-20019': BadRequest,  # orderId is invalid
                    '-20020': BadRequest,  # stopPrice or limitPrice is invalid
                    '-20021': BadRequest,  # price is invalid
                    '-20022': ArgumentsRequired,  # price is required for LIMIT order
                    '-20023': ArgumentsRequired,  # timestamp is required
                    '-20024': ExchangeError,  # timestamp exceeds the threshold
                    '-20025': AuthenticationError,  # API key is invalid
                    '-20026': BadRequest,  # Token is invalid or expired
                    '-20027': BadRequest,  # The length of the message exceeds the maximum length
                    '-20028': BadRequest,  # price or stopPrice or limitPrice must be greater than zero
                    '-20029': BadRequest,  # stopPrice must be less than limitPrice for Buy Stop Order
                    '-20030': BadRequest,  # limitPrice must be less than stopPrice for Sell Stop Order
                    '-20031': MarketClosed,  # The marketCode is closed for trading temporarily
                    '-20032': NetworkError,  # Failed to submit due to timeout in server side
                    '-20033': BadRequest,  # triggerType is invalid
                    '-20034': BadRequest,  # The size of tag must be less than 32
                    '-20050': ExchangeError,  # selfTradePreventionMode is invalid
                    '-30001': BadRequest,  # {"success":false,"code":"30001","message":"Required parameter 'marketCode' is missing"}
                    '-35034': AuthenticationError,  # {"success":false,"code":"35034","message":"Wallet API is not functioning properly, please try again or contact support."}
                    '-35046': AuthenticationError,  # {"success":false,"code":"35046","message":"Error. Please refresh the page."}
                    '-40001': ExchangeError,  # Alert from the server
                    '-50001': ExchangeError,  # Unknown server error
                    '-300001': AccountNotEnabled,  # Invalid account status xxx, please contact administration if any questions
                    '-300011': InvalidOrder,  # Repo market orders are not allowed during the auction window
                    '-300012': InvalidOrder,  # Repo bids above 0 and offers below 0 are not allowed during the auction window
                    '-100005': OrderNotFound,  # Open order not found
                    '-100006': InvalidOrder,  # Open order is not owned by the user
                    '-100008': BadRequest,  # Quantity cannot be less than the quantity increment xxx
                    '-100015': NetworkError,  # recvWindow xxx has expired
                    '-710001': ExchangeError,  # System failure, exception thrown -> xxx
                    '-710002': BadRequest,  # The price is lower than the minimum
                    '-710003': BadRequest,  # The price is higher than the maximum
                    '-710004': BadRequest,  # Position quantity exceeds the limit
                    '-710005': InsufficientFunds,  # Insufficient margin
                    '-710006': InsufficientFunds,  # Insufficient balance
                    '-710007': InsufficientFunds,  # Insufficient position
                    '-000101': NetworkError,  # Internal server is unavailable temporary, try again later
                    '-000201': NetworkError,  # Trade service is busy, try again later
                },
                'broad': {
                    '-20001': OperationFailed,  # Operation failed, please contact system administrator
                    '-200050': RequestTimeout,  # The market is not active
                },
            },
        })

    async def fetch_markets(self, params={}) -> List[Market]:
        """
        retrieves data on all markets for bitmex
        :see: https://docs.ox.fun/?json#get-v3-markets
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        responseFromMarkets, responseFromTickers = await asyncio.gather(*[self.publicGetV3Markets(params), self.publicGetV3Tickers(params)])
        marketsFromMarkets = self.safe_list(responseFromMarkets, 'data', [])
        #
        #         {
        #             success: True,
        #             data: [
        #                 {
        #                     marketCode: 'OX-USD-SWAP-LIN',
        #                     name: 'OX/USD Perp',
        #                     referencePair: 'OX/USDT',
        #                     base: 'OX',
        #                     counter: 'USD',
        #                     type: 'FUTURE',
        #                     tickSize: '0.00001',
        #                     minSize: '1',
        #                     listedAt: '1704766320000',
        #                     upperPriceBound: '0.02122',
        #                     lowerPriceBound: '0.01142',
        #                     markPrice: '0.01632',
        #                     indexPrice: '0.01564',
        #                     lastUpdatedAt: '1714762235569'
        #                 },
        #                 {
        #                     marketCode: 'BTC-USD-SWAP-LIN',
        #                     name: 'BTC/USD Perp',
        #                     referencePair: 'BTC/USDT',
        #                     base: 'BTC',
        #                     counter: 'USD',
        #                     type: 'FUTURE',
        #                     tickSize: '1',
        #                     minSize: '0.0001',
        #                     listedAt: '1704686640000',
        #                     upperPriceBound: '67983',
        #                     lowerPriceBound: '55621',
        #                     markPrice: '61802',
        #                     indexPrice: '61813',
        #                     lastUpdatedAt: '1714762234765'
        #                 },
        #                 {
        #                     "marketCode": "MILK-OX",
        #                     "name": "MILK/OX",
        #                     "referencePair": "MILK/OX",
        #                     "base": "MILK",
        #                     "counter": "OX",
        #                     "type": "SPOT",
        #                     "tickSize": "0.0001",
        #                     "minSize": "1",
        #                     "listedAt": "1706608500000",
        #                     "upperPriceBound": "1.0000",
        #                     "lowerPriceBound": "-1.0000",
        #                     "markPrice": "0.0269",
        #                     "indexPrice": "0.0269",
        #                     "lastUpdatedAt": "1714757402185"
        #                 },
        #                 ...
        #             ]
        #         }
        #
        marketsFromTickers = self.safe_list(responseFromTickers, 'data', [])
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "marketCode": "DYM-USD-SWAP-LIN",
        #                 "markPrice": "3.321",
        #                 "open24h": "3.315",
        #                 "high24h": "3.356",
        #                 "low24h": "3.255",
        #                 "volume24h": "0",
        #                 "currencyVolume24h": "0",
        #                 "openInterest": "1768.1",
        #                 "lastTradedPrice": "3.543",
        #                 "lastTradedQuantity": "1.0",
        #                 "lastUpdatedAt": "1714853388102"
        #             },
        #             ...
        #         ]
        #     }
        #
        markets = self.array_concat(marketsFromMarkets, marketsFromTickers)
        return self.parse_markets(markets)

    def parse_markets(self, markets) -> List[Market]:
        marketIds = []
        result = []
        for i in range(0, len(markets)):
            market = markets[i]
            marketId = self.safe_string(market, 'marketCode')
            if not (self.in_array(marketId, marketIds)):
                marketIds.append(marketId)
                result.append(self.parse_market(market))
        return result

    def parse_market(self, market) -> Market:
        id = self.safe_string(market, 'marketCode', '')
        parts = id.split('-')
        baseId = self.safe_string(parts, 0)
        quoteId = self.safe_string(parts, 1)
        base = self.safe_currency_code(baseId)
        quote = self.safe_currency_code(quoteId)
        symbol = base + '/' + quote
        type = self.safe_string_lower(market, 'type', 'spot')  # markets from v3/tickers are spot and have no type
        settleId: Str = None
        settle: Str = None
        isFuture = (type == 'future')  # the exchange has only perpetual futures
        if isFuture:
            type = 'swap'
            settleId = 'OX'
            settle = self.safe_currency_code('OX')
            symbol = symbol + ':' + settle
        isSpot = type == 'spot'
        return self.safe_market_structure({
            'id': id,
            'numericId': None,
            'symbol': symbol,
            'base': base,
            'quote': quote,
            'settle': settle,
            'baseId': baseId,
            'quoteId': quoteId,
            'settleId': settleId,
            'type': type,
            'spot': isSpot,
            'margin': False,
            'swap': isFuture,
            'future': False,
            'option': False,
            'active': True,
            'contract': isFuture,
            'linear': True if isFuture else None,
            'inverse': False if isFuture else None,
            'taker': self.fees['trading']['taker'],
            'maker': self.fees['trading']['maker'],
            'contractSize': 1 if isFuture else None,
            'expiry': None,
            'expiryDatetime': None,
            'strike': None,
            'optionType': None,
            'precision': {
                'amount': None,  # todo find it out
                'price': self.safe_number(market, 'tickSize'),
            },
            'limits': {
                'leverage': {
                    'min': None,
                    'max': None,
                },
                'amount': {
                    'min': self.safe_number(market, 'minSize'),
                    'max': None,
                },
                'price': {
                    'min': None,
                    'max': None,
                },
                'cost': {
                    'min': None,
                    'max': None,
                },
            },
            'created': self.safe_integer(market, 'listedAt'),
            'index': None,
            'info': market,
        })

    async def fetch_currencies(self, params={}) -> Currencies:
        """
        fetches all available currencies on an exchange
        :see: https://docs.ox.fun/?json#get-v3-assets
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an associative dictionary of currencies
        """
        response = await self.publicGetV3Assets(params)
        #
        #     {
        #         "success": True,
        #         "data":  [
        #             {
        #                 "asset": "OX",
        #                 "isCollateral": True,
        #                 "loanToValue": "1.000000000",
        #                 "loanToValueFactor": "0.000000000",
        #                 "networkList":  [
        #                     {
        #                         "network": "BNBSmartChain",
        #                         "tokenId": "0x78a0A62Fba6Fb21A83FE8a3433d44C73a4017A6f",
        #                         "transactionPrecision": "18",
        #                         "isWithdrawalFeeChargedToUser": True,
        #                         "canDeposit": True,
        #                         "canWithdraw": False,
        #                         "minDeposit": "0.00010",
        #                         "minWithdrawal": "0.00010"
        #                     },
        #                     {
        #                         "network": "Polygon",
        #                         "tokenId": "0x78a0A62Fba6Fb21A83FE8a3433d44C73a4017A6f",
        #                         "transactionPrecision": "18",
        #                         "isWithdrawalFeeChargedToUser": True,
        #                         "canDeposit": True,
        #                         "canWithdraw": False,
        #                         "minDeposit": "0.00010",
        #                         "minWithdrawal": "0.00010"
        #                     },
        #                     {
        #                         "network": "Arbitrum",
        #                         "tokenId": "0xba0Dda8762C24dA9487f5FA026a9B64b695A07Ea",
        #                         "transactionPrecision": "18",
        #                         "isWithdrawalFeeChargedToUser": True,
        #                         "canDeposit": True,
        #                         "canWithdraw": True,
        #                         "minDeposit": "0.00010",
        #                         "minWithdrawal": "0.00010"
        #                     },
        #                     {
        #                         "network": "Ethereum",
        #                         "tokenId": "0xba0Dda8762C24dA9487f5FA026a9B64b695A07Ea",
        #                         "transactionPrecision": "18",
        #                         "isWithdrawalFeeChargedToUser": True,
        #                         "canDeposit": True,
        #                         "canWithdraw": True,
        #                         "minDeposit": "0.00010",
        #                         "minWithdrawal": "0.00010"
        #                     },
        #                     {
        #                         "network": "Arbitrum",
        #                         "tokenId": "0x78a0A62Fba6Fb21A83FE8a3433d44C73a4017A6f",
        #                         "transactionPrecision": "18",
        #                         "isWithdrawalFeeChargedToUser": True,
        #                         "canDeposit": True,
        #                         "canWithdraw": False,
        #                         "minDeposit": "0.00010",
        #                         "minWithdrawal": "0.00010"
        #                     },
        #                     {
        #                         "network": "Avalanche",
        #                         "tokenId": "0x78a0A62Fba6Fb21A83FE8a3433d44C73a4017A6f",
        #                         "transactionPrecision": "18",
        #                         "isWithdrawalFeeChargedToUser": True,
        #                         "canDeposit": True,
        #                         "canWithdraw": False,
        #                         "minDeposit": "0.00010",
        #                         "minWithdrawal": "0.00010"
        #                     },
        #                     {
        #                         "network": "Solana",
        #                         "tokenId": "DV3845GEAVXfwpyVGGgWbqBVCtzHdCXNCGfcdboSEuZz",
        #                         "transactionPrecision": "8",
        #                         "isWithdrawalFeeChargedToUser": True,
        #                         "canDeposit": True,
        #                         "canWithdraw": True,
        #                         "minDeposit": "0.00010",
        #                         "minWithdrawal": "0.00010"
        #                     },
        #                     {
        #                         "network": "Ethereum",
        #                         "tokenId": "0x78a0A62Fba6Fb21A83FE8a3433d44C73a4017A6f",
        #                         "transactionPrecision": "18",
        #                         "isWithdrawalFeeChargedToUser": True,
        #                         "canDeposit": True,
        #                         "canWithdraw": False,
        #                         "minDeposit": "0.00010",
        #                         "minWithdrawal": "0.00010"
        #                     }
        #                 ]
        #             },
        #             {
        #                 "asset": "BTC",
        #                 "isCollateral": True,
        #                 "loanToValue": "0.950000000",
        #                 "loanToValueFactor": "0.000000000",
        #                 "networkList":  [
        #                     {
        #                         "network": "Bitcoin",
        #                         "transactionPrecision": "8",
        #                         "isWithdrawalFeeChargedToUser": True,
        #                         "canDeposit": True,
        #                         "canWithdraw": True,
        #                         "minDeposit": "0.00010",
        #                         "minWithdrawal": "0.00010"
        #                     }
        #                 ]
        #             },
        #             {
        #                 "asset": "USDT.ARB",
        #                 "isCollateral": True,
        #                 "loanToValue": "0.950000000",
        #                 "loanToValueFactor": "0.000000000",
        #                 "networkList": [
        #                     {
        #                         "network": "Arbitrum",
        #                         "tokenId": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
        #                         "transactionPrecision": "18",
        #                         "isWithdrawalFeeChargedToUser": True,
        #                         "canDeposit": True,
        #                         "canWithdraw": True,
        #                         "minDeposit": "0.00010",
        #                         "minWithdrawal": "0.00010"
        #                     }
        #                 ]
        #             },
        #             ...
        #         ]
        #     }
        #
        data = self.safe_list(response, 'data', [])
        result: dict = {}
        for i in range(0, len(data)):
            currency = data[i]
            fullId = self.safe_string(currency, 'asset', '')
            parts = fullId.split('.')
            id = parts[0]
            code = self.safe_currency_code(id)
            networks: dict = {}
            chains = self.safe_list(currency, 'networkList', [])
            currencyMaxPrecision: Str = None
            currencyDepositEnabled: Bool = None
            currencyWithdrawEnabled: Bool = None
            for j in range(0, len(chains)):
                chain = chains[j]
                networkId = self.safe_string(chain, 'network')
                networkCode = self.network_id_to_code(networkId)
                deposit = self.safe_bool(chain, 'canDeposit')
                withdraw = self.safe_bool(chain, 'canWithdraw')
                active = (deposit and withdraw)
                minDeposit = self.safe_string(chain, 'minDeposit')
                minWithdrawal = self.safe_string(chain, 'minWithdrawal')
                precision = self.parse_precision(self.safe_string(chain, 'transactionPrecision'))
                networks[networkCode] = {
                    'id': networkId,
                    'network': networkCode,
                    'margin': None,
                    'deposit': deposit,
                    'withdraw': withdraw,
                    'active': active,
                    'fee': None,
                    'precision': self.parse_number(precision),
                    'limits': {
                        'deposit': {
                            'min': minDeposit,
                            'max': None,
                        },
                        'withdraw': {
                            'min': minWithdrawal,
                            'max': None,
                        },
                    },
                    'info': chain,
                }
                if (currencyDepositEnabled is None) or deposit:
                    currencyDepositEnabled = deposit
                if (currencyWithdrawEnabled is None) or withdraw:
                    currencyWithdrawEnabled = withdraw
                if (currencyMaxPrecision is None) or Precise.string_gt(currencyMaxPrecision, precision):
                    currencyMaxPrecision = precision
            if code in result:
                # checking for specific ids.ARB
                networks = self.extend(result[code]['networks'], networks)
            result[code] = {
                'id': id,
                'code': code,
                'name': None,
                'type': None,
                'active': None,
                'deposit': currencyDepositEnabled,
                'withdraw': currencyWithdrawEnabled,
                'fee': None,
                'precision': self.parse_number(currencyMaxPrecision),
                'limits': {
                    'amount': {
                        'min': None,
                        'max': None,
                    },
                    'withdraw': {
                        'min': None,
                        'max': None,
                    },
                },
                'networks': networks,
                'info': currency,
            }
        return result

    async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
        """
        fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
        :see: https://docs.ox.fun/?json#get-v3-tickers
        :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `ticker structures <https://docs.ccxt_zagrava.com/#/?id=ticker-structure>`
        """
        await self.load_markets()
        symbols = self.market_symbols(symbols)
        response = await self.publicGetV3Tickers(params)
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "marketCode": "NII-USDT",
        #                 "markPrice": "0",
        #                 "open24h": "0",
        #                 "high24h": "0",
        #                 "low24h": "0",
        #                 "volume24h": "0",
        #                 "currencyVolume24h": "0",
        #                 "openInterest": "0",
        #                 "lastTradedPrice": "0",
        #                 "lastTradedQuantity": "0",
        #                 "lastUpdatedAt": "1714853388621"
        #             },
        #             {
        #                 "marketCode": "GEC-USDT",
        #                 "markPrice": "0",
        #                 "open24h": "0",
        #                 "high24h": "0",
        #                 "low24h": "0",
        #                 "volume24h": "0",
        #                 "currencyVolume24h": "0",
        #                 "openInterest": "0",
        #                 "lastTradedPrice": "0",
        #                 "lastTradedQuantity": "0",
        #                 "lastUpdatedAt": "1714853388621"
        #             },
        #             {
        #                 "marketCode": "DYM-USD-SWAP-LIN",
        #                 "markPrice": "3.321",
        #                 "open24h": "3.315",
        #                 "high24h": "3.356",
        #                 "low24h": "3.255",
        #                 "volume24h": "0",
        #                 "currencyVolume24h": "0",
        #                 "openInterest": "1768.1",
        #                 "lastTradedPrice": "3.543",
        #                 "lastTradedQuantity": "1.0",
        #                 "lastUpdatedAt": "1714853388102"
        #             },
        #             ...
        #         ]
        #     }
        #
        tickers = self.safe_list(response, 'data', [])
        return self.parse_tickers(tickers, symbols)

    async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
        """
        fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
        :see: https://docs.ox.fun/?json#get-v3-tickers
        :param str symbol: unified symbol of the market to fetch the ticker for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `ticker structure <https://docs.ccxt_zagrava.com/#/?id=ticker-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'marketCode': market['id'],
        }
        response = await self.publicGetV3Tickers(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "marketCode": "BTC-USD-SWAP-LIN",
        #                 "markPrice": "64276",
        #                 "open24h": "63674",
        #                 "high24h": "64607",
        #                 "low24h": "62933",
        #                 "volume24h": "306317655.80000",
        #                 "currencyVolume24h": "48.06810",
        #                 "openInterest": "72.39250",
        #                 "lastTradedPrice": "64300.0",
        #                 "lastTradedQuantity": "1.0",
        #                 "lastUpdatedAt": "1714925196034"
        #             }
        #         ]
        #     }
        #
        data = self.safe_list(response, 'data', [])
        ticker = self.safe_dict(data, 0, {})
        return self.parse_ticker(ticker, market)

    def parse_ticker(self, ticker, market: Market = None) -> Ticker:
        #
        #     {
        #         "marketCode": "BTC-USD-SWAP-LIN",
        #         "markPrice": "64276",
        #         "open24h": "63674",
        #         "high24h": "64607",
        #         "low24h": "62933",
        #         "volume24h": "306317655.80000",
        #         "currencyVolume24h": "48.06810",
        #         "openInterest": "72.39250",
        #         "lastTradedPrice": "64300.0",
        #         "lastTradedQuantity": "1.0",
        #         "lastUpdatedAt": "1714925196034"
        #     }
        #
        timestamp = self.safe_integer(ticker, 'lastUpdatedAt')
        marketId = self.safe_string(ticker, 'marketCode')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        last = self.safe_string(ticker, 'lastTradedPrice')
        return self.safe_ticker({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_string(ticker, 'high24h'),
            'low': self.safe_string(ticker, 'low24h'),
            'bid': None,
            'bidVolume': None,
            'ask': None,
            'askVolume': None,
            'vwap': None,
            'open': self.safe_string(ticker, 'open24h'),
            'close': last,
            'last': last,
            'previousClose': None,
            'change': None,
            'percentage': None,
            'average': None,
            'baseVolume': self.safe_string(ticker, 'currencyVolume24h'),
            'quoteVolume': None,  # the exchange returns cost in OX
            'info': ticker,
        }, market)

    async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
        """
        fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
        :see: https://docs.ox.fun/?json#get-v3-candles
        :param str symbol: unified symbol of the market to fetch OHLCV data for
        :param str timeframe: the length of time each candle represents
        :param int [since]: timestamp in ms of the earliest candle to fetch(default 24 hours ago)
        :param int [limit]: the maximum amount of candles to fetch(default 200, max 500)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms of the latest candle to fetch(default now)
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        await self.load_markets()
        market = self.market(symbol)
        timeframe = self.safe_string(self.timeframes, timeframe, timeframe)
        request: dict = {
            'marketCode': market['id'],
            'timeframe': timeframe,
        }
        if since is not None:
            request['startTime'] = since  # startTime and endTime must be within 7 days of each other
        if limit is not None:
            request['limit'] = limit
        until = self.safe_integer(params, 'until')
        if until is not None:
            request['endTime'] = until
            params = self.omit(params, 'until')
        elif since is not None:
            request['endTime'] = self.sum(since, 7 * 24 * 60 * 60 * 1000)  # for the exchange not to raise an exception if since is younger than 7 days
        response = await self.publicGetV3Candles(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "timeframe": "3600s",
        #         "data": [
        #             {
        #                 "open": "0.03240000",
        #                 "high": "0.03240000",
        #                 "low": "0.03240000",
        #                 "close": "0.03240000",
        #                 "volume": "0",
        #                 "currencyVolume": "0",
        #                 "openedAt": "1714906800000"
        #             },
        #             {
        #                 "open": "0.03240000",
        #                 "high": "0.03240000",
        #                 "low": "0.03240000",
        #                 "close": "0.03240000",
        #                 "volume": "0",
        #                 "currencyVolume": "0",
        #                 "openedAt": "1714903200000"
        #             },
        #             ...
        #         ]
        #     }
        #
        result = self.safe_list(response, 'data', [])
        return self.parse_ohlcvs(result, market, timeframe, since, limit)

    def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
        #
        #     {
        #         "open": "0.03240000",
        #         "high": "0.03240000",
        #         "low": "0.03240000",
        #         "close": "0.03240000",
        #         "volume": "0",
        #         "currencyVolume": "0",
        #         "openedAt": "1714906800000"
        #     }
        #
        return [
            self.safe_integer(ohlcv, 'openedAt'),
            self.safe_number(ohlcv, 'open'),
            self.safe_number(ohlcv, 'high'),
            self.safe_number(ohlcv, 'low'),
            self.safe_number(ohlcv, 'close'),
            self.safe_number(ohlcv, 'currencyVolume'),
        ]

    async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
        """
        fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
        :see: https://docs.ox.fun/?json#get-v3-depth
        :param str symbol: unified symbol of the market to fetch the order book for
        :param int [limit]: the maximum amount of order book entries to return(default 5, max 100)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt_zagrava.com/#/?id=order-book-structure>` indexed by market symbols
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'marketCode': market['id'],
        }
        if limit is not None:
            request['level'] = limit
        response = await self.publicGetV3Depth(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "level": "5",
        #         "data": {
        #             "marketCode": "BTC-USD-SWAP-LIN",
        #             "lastUpdatedAt": "1714933499266",
        #             "asks": [
        #                 [64073.0, 8.4622],
        #                 [64092.0, 8.1912],
        #                 [64111.0, 8.0669],
        #                 [64130.0, 11.7195],
        #                 [64151.0, 10.1798]
        #             ],
        #             "bids": [
        #                 [64022.0, 10.1292],
        #                 [64003.0, 8.1619],
        #                 [64000.0, 1.0],
        #                 [63984.0, 12.7724],
        #                 [63963.0, 11.0073]
        #             ]
        #         }
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        timestamp = self.safe_integer(data, 'lastUpdatedAt')
        return self.parse_order_book(data, market['symbol'], timestamp)

    async def fetch_funding_rates(self, symbols: Strings = None, params={}):
        """
        :see: https://docs.ox.fun/?json#get-v3-funding-estimates
        fetch the current funding rates
        :param str[] symbols: unified market symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Order[]: an array of `funding rate structures <https://docs.ccxt_zagrava.com/#/?id=funding-rate-structure>`
        """
        await self.load_markets()
        symbols = self.market_symbols(symbols)
        response = await self.publicGetV3FundingEstimates(params)
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "marketCode": "OX-USD-SWAP-LIN",
        #                 "fundingAt": "1715515200000",
        #                 "estFundingRate": "0.000200000"
        #             },
        #             {
        #                 "marketCode": "BTC-USD-SWAP-LIN",
        #                 "fundingAt": "1715515200000",
        #                 "estFundingRate": "0.000003"
        #             },
        #             ...
        #         ]
        #     }
        #
        data = self.safe_list(response, 'data', [])
        result = self.parse_funding_rates(data)
        return self.filter_by_array(result, 'symbol', symbols)

    def parse_funding_rate(self, fundingRate, market: Market = None):
        #
        #     {
        #         "marketCode": "OX-USD-SWAP-LIN",
        #         "fundingAt": "1715515200000",
        #         "estFundingRate": "0.000200000"
        #     },
        #
        #
        symbol = self.safe_string(fundingRate, 'marketCode')
        market = self.market(symbol)
        estFundingRateTimestamp = self.safe_integer(fundingRate, 'fundingAt')
        return {
            'info': fundingRate,
            'symbol': market['symbol'],
            'markPrice': None,
            'indexPrice': None,
            'interestRate': self.parse_number('0'),
            'estimatedSettlePrice': None,
            'timestamp': estFundingRateTimestamp,
            'datetime': self.iso8601(estFundingRateTimestamp),
            'fundingRate': self.safe_number(fundingRate, 'estFundingRate'),
            'fundingTimestamp': None,
            'fundingDatetime': None,
            'nextFundingRate': None,
            'nextFundingTimestamp': None,
            'nextFundingDatetime': None,
            'previousFundingRate': None,
            'previousFundingTimestamp': None,
            'previousFundingDatetime': None,
        }

    async def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        Fetches the history of funding rates
        :see: https://docs.ox.fun/?json#get-v3-funding-rates
        :param str symbol: unified symbol of the market to fetch trades for
        :param int [since]: timestamp in ms of the earliest trade to fetch(default 24 hours ago)
        :param int [limit]: the maximum amount of trades to fetch(default 200, max 500)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms of the latest trade to fetch(default now)
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=public-trades>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'marketCode': market['id'],
        }
        if since is not None:
            request['startTime'] = since  # startTime and endTime must be within 7 days of each other
        if limit is not None:
            request['limit'] = limit
        until = self.safe_integer(params, 'until')
        if until is not None:
            request['endTime'] = until
            params = self.omit(params, 'until')
        response = await self.publicGetV3FundingRates(self.extend(request, params))
        #
        #     {
        #         success: True,
        #         data: [
        #         {
        #             marketCode: 'NEAR-USD-SWAP-LIN',
        #             fundingRate: '-0.000010000',
        #             createdAt: '1715428870755'
        #         },
        #         {
        #             marketCode: 'ENA-USD-SWAP-LIN',
        #             fundingRate: '0.000150000',
        #             createdAt: '1715428868616'
        #         },
        #         ...
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_funding_rate_histories(data, market, since, limit)

    def parse_funding_rate_history(self, info, market: Market = None):
        #
        #     {
        #         success: True,
        #         data: [
        #         {
        #             marketCode: 'NEAR-USD-SWAP-LIN',
        #             fundingRate: '-0.000010000',
        #             createdAt: '1715428870755'
        #         },
        #         {
        #             marketCode: 'ENA-USD-SWAP-LIN',
        #             fundingRate: '0.000150000',
        #             createdAt: '1715428868616'
        #         },
        #         ...
        #     }
        #
        marketId = self.safe_string(info, 'marketCode')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        timestamp = self.safe_integer(info, 'createdAt')
        return {
            'info': info,
            'symbol': symbol,
            'fundingRate': self.safe_number(info, 'fundingRate'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
        }

    async def fetch_funding_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetches the history of funding payments
        :see: https://docs.ox.fun/?json#get-v3-funding
        :param str symbol: unified symbol of the market to fetch trades for
        :param int [since]: timestamp in ms of the earliest trade to fetch(default 24 hours ago)
        :param int [limit]: the maximum amount of trades to fetch(default 200, max 500)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms of the latest trade to fetch(default now)
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=public-trades>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'marketCode': market['id'],
        }
        if since is not None:
            request['startTime'] = since  # startTime and endTime must be within 7 days of each other
        if limit is not None:
            request['limit'] = limit
        until = self.safe_integer(params, 'until')
        if until is not None:
            request['endTime'] = until
            params = self.omit(params, 'until')
        response = await self.privateGetV3Funding(self.extend(request, params))
        #
        #     {
        #         success: True,
        #         data: [
        #             {
        #                 id: '966709913041305605',
        #                 marketCode: 'ETH-USD-SWAP-LIN',
        #                 payment: '-0.00430822',
        #                 fundingRate: '0.000014',
        #                 position: '0.001',
        #                 indexPrice: '3077.3',
        #                 createdAt: '1715086852890'
        #             },
        #             {
        #                 id: '966698111997509637',
        #                 marketCode: 'ETH-USD-SWAP-LIN',
        #                 payment: '-0.0067419',
        #                 fundingRate: '0.000022',
        #                 position: '0.001',
        #                 indexPrice: '3064.5',
        #                 createdAt: '1715083251516'
        #             },
        #             ...
        #         ]
        #     }
        #
        result = self.safe_list(response, 'data', [])
        return self.parse_incomes(result, market, since, limit)

    def parse_income(self, income, market: Market = None):
        #
        #     {
        #         id: '966709913041305605',
        #         marketCode: 'ETH-USD-SWAP-LIN',
        #         payment: '-0.00430822',
        #         fundingRate: '0.000014',
        #         position: '0.001',
        #         indexPrice: '3077.3',
        #         createdAt: '1715086852890'
        #     },
        #
        marketId = self.safe_string(income, 'marketCode')
        symbol = self.safe_symbol(marketId, market)
        amount = self.safe_number(income, 'payment')
        code = self.safe_currency_code('OX')
        id = self.safe_string(income, 'id')
        timestamp = self.safe_timestamp(income, 'createdAt')
        rate = self.safe_number(income, 'fundingRate')
        return {
            'info': income,
            'symbol': symbol,
            'code': code,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'id': id,
            'amount': amount,
            'rate': rate,
        }

    async def fetch_leverage_tiers(self, symbols: Strings = None, params={}):
        """
        retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes, if a market has a leverage tier of 0, then the leverage tiers cannot be obtained for self market
        :see: https://docs.ox.fun/?json#get-v3-leverage-tiers
        :param str[] [symbols]: list of unified market symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `leverage tiers structures <https://docs.ccxt_zagrava.com/#/?id=leverage-tiers-structure>`, indexed by market symbols
        """
        await self.load_markets()
        response = await self.publicGetV3LeverageTiers(params)
        #
        #     {
        #         success: True,
        #         data: [
        #            {
        #                 marketCode: 'SOL-USD-SWAP-LIN',
        #                 tiers: [
        #                     {
        #                         tier: '1',
        #                         leverage: '10',
        #                         positionFloor: '0',
        #                         positionCap: '200000000',
        #                         initialMargin: '0.1',
        #                         maintenanceMargin: '0.05',
        #                         maintenanceAmount: '0'
        #                     },
        #                     {
        #                         tier: '2',
        #                         leverage: '5',
        #                         positionFloor: '200000000',
        #                         positionCap: '280000000',
        #                         initialMargin: '0.2',
        #                         maintenanceMargin: '0.1',
        #                         maintenanceAmount: '7000000'
        #                     },
        #                     {
        #                         tier: '3',
        #                         leverage: '4',
        #                         positionFloor: '280000000',
        #                         positionCap: '460000000',
        #                         initialMargin: '0.25',
        #                         maintenanceMargin: '0.125',
        #                         maintenanceAmount: '14000000'
        #                     },
        #                     ...
        #                 ]
        #             },
        #             ...
        #         ]
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_leverage_tiers(data, symbols, 'marketCode')

    def parse_market_leverage_tiers(self, info, market: Market = None):
        #
        #     {
        #         marketCode: 'SOL-USD-SWAP-LIN',
        #         tiers: [
        #             {
        #                 tier: '1',
        #                 leverage: '10',
        #                 positionFloor: '0',
        #                 positionCap: '200000000',
        #                 initialMargin: '0.1',
        #                 maintenanceMargin: '0.05',
        #                 maintenanceAmount: '0'
        #             ...
        #         ]
        #     },
        #
        marketId = self.safe_string(info, 'marketCode')
        market = self.safe_market(marketId, market)
        listOfTiers = self.safe_list(info, 'tiers', [])
        tiers = []
        for j in range(0, len(listOfTiers)):
            tier = listOfTiers[j]
            tiers.append({
                'tier': self.safe_number(tier, 'tier'),
                'currency': market['settle'],
                'minNotional': self.safe_number(tier, 'positionFloor'),
                'maxNotional': self.safe_number(tier, 'positionCap'),
                'maintenanceMarginRate': self.safe_number(tier, 'maintenanceMargin'),
                'maxLeverage': self.safe_number(tier, 'leverage'),
                'info': tier,
            })
        return tiers

    async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        get the list of most recent trades for a particular symbol
        :see: https://docs.ox.fun/?json#get-v3-exchange-trades
        :param str symbol: unified symbol of the market to fetch trades for
        :param int [since]: timestamp in ms of the earliest trade to fetch(default 24 hours ago)
        :param int [limit]: the maximum amount of trades to fetch(default 200, max 500)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms of the latest trade to fetch(default now)
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=public-trades>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'marketCode': market['id'],
        }
        if since is not None:
            request['startTime'] = since  # startTime and endTime must be within 7 days of each other
        if limit is not None:
            request['limit'] = limit
        until = self.safe_integer(params, 'until')
        if until is not None:
            request['endTime'] = until
            params = self.omit(params, 'until')
        elif since is not None:
            request['endTime'] = self.sum(since, 7 * 24 * 60 * 60 * 1000)  # for the exchange not to raise an exception if since is younger than 7 days
        response = await self.publicGetV3ExchangeTrades(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "marketCode": "BTC-USD-SWAP-LIN",
        #                 "matchPrice": "63900",
        #                 "matchQuantity": "1",
        #                 "side": "SELL",
        #                 "matchType": "TAKER",
        #                 "matchedAt": "1714934112352"
        #             },
        #             ...
        #         ]
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_trades(data, market, since, limit)

    async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch all trades made by the user
        :see: https://docs.ox.fun/?json#get-v3-trades
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch trades for
        :param int [limit]: the maximum amount of trades to fetch(default 200, max 500)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms of the latest trade to fetch(default now)
        :returns Trade[]: a list of `trade structures <https://github.com/ccxt_zagrava/ccxt_zagrava/wiki/Manual#trade-structure>`
        """
        await self.load_markets()
        request: dict = {}
        market: Market = None
        if symbol is not None:
            market = self.market(symbol)
            request['marketCode'] = market['id']
        if since is not None:  # startTime and endTime must be within 7 days of each other
            request['startTime'] = since
        if limit is not None:
            request['limit'] = limit
        until = self.safe_integer(params, 'until')
        if until is not None:
            request['endTime'] = until
            params = self.omit(params, 'until')
        elif since is not None:
            request['endTime'] = self.sum(since, 7 * 24 * 60 * 60 * 1000)  # for the exchange not to raise an exception if since is younger than 7 days
        response = await self.privateGetV3Trades(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "orderId": "1000104903698",
        #                 "clientOrderId": "1715000260094",
        #                 "matchId": "400017129522773178",
        #                 "marketCode": "ETH-USD-SWAP-LIN",
        #                 "side": "BUY",
        #                 "matchedQuantity": "0.001",
        #                 "matchPrice": "3100.2",
        #                 "total": "310.02",
        #                 "orderMatchType": "MAKER",
        #                 "feeAsset": "OX",
        #                 "fee": "0.062004",
        #                 "source": "0",
        #                 "matchedAt": "1715000267420"
        #             }
        #         ]
        #     }
        #
        result = self.safe_list(response, 'data', [])
        return self.parse_trades(result, market, since, limit)

    def parse_trade(self, trade, market: Market = None) -> Trade:
        #
        # public fetchTrades
        #
        #     {
        #         "marketCode": "BTC-USD-SWAP-LIN",
        #         "matchPrice": "63900",
        #         "matchQuantity": "1",
        #         "side": "SELL",
        #         "matchType": "TAKER",
        #         "matchedAt": "1714934112352"
        #     }
        #
        #
        # private fetchMyTrades
        #
        #     {
        #         "orderId": "1000104903698",
        #         "clientOrderId": "1715000260094",
        #         "matchId": "400017129522773178",
        #         "marketCode": "ETH-USD-SWAP-LIN",
        #         "side": "BUY",
        #         "matchedQuantity": "0.001",
        #         "matchPrice": "3100.2",
        #         "total": "310.02",
        #         "orderMatchType": "MAKER",
        #         "feeAsset": "OX",
        #         "fee": "0.062004",
        #         "source": "0",
        #         "matchedAt": "1715000267420"
        #     }
        #
        marketId = self.safe_string(trade, 'marketCode')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        timestamp = self.safe_integer(trade, 'matchedAt')
        fee = {
            'cost': self.safe_string(trade, 'fee'),
            'currency': self.safe_currency_code(self.safe_string(trade, 'feeAsset')),
        }
        return self.safe_trade({
            'id': self.safe_string(trade, 'matchId'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'type': None,
            'order': self.safe_string(trade, 'orderId'),
            'side': self.safe_string_lower(trade, 'side'),
            'takerOrMaker': self.safe_string_lower_2(trade, 'matchType', 'orderMatchType'),
            'price': self.safe_string(trade, 'matchPrice'),
            'amount': self.safe_string_2(trade, 'matchQuantity', 'matchedQuantity'),
            'cost': None,  # the exchange returns total cost in OX
            'fee': fee,
            'info': trade,
        }, market)

    async def fetch_balance(self, params={}) -> Balances:
        """
        query for balance and get the amount of funds available for trading or funds locked in orders
        :see: https://docs.ox.fun/?json#get-v3-balances
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.asset]: currency id, if empty the exchange returns info about all currencies
        :param str [params.subAcc]: Name of sub account. If no subAcc is given, then the response contains only the account linked to the API-Key.
        :returns dict: a `balance structure <https://docs.ccxt_zagrava.com/#/?id=balance-structure>`
        """
        await self.load_markets()
        response = await self.privateGetV3Balances(params)
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "accountId": "106490",
        #                 "name": "main",
        #                 "balances": [
        #                     {
        #                         "asset": "OX",
        #                         "total": "-7.55145065000",
        #                         "available": "-71.16445065000",
        #                         "reserved": "0",
        #                         "lastUpdatedAt": "1715000448946"
        #                     },
        #                     {
        #                         "asset": "ETH",
        #                         "total": "0.01",
        #                         "available": "0.01",
        #                         "reserved": "0",
        #                         "lastUpdatedAt": "1714914512750"
        #                     },
        #                     ...
        #                 ]
        #             },
        #             ...
        #         ]
        #     }
        #
        data = self.safe_list(response, 'data', [])
        balance = data[0]
        subAcc = self.safe_string(params, 'subAcc')
        if subAcc is not None:
            for i in range(0, len(data)):
                b = data[i]
                name = self.safe_string(b, 'name')
                if name == subAcc:
                    balance = b
                    break
        return self.parse_balance(balance)

    def parse_balance(self, balance) -> Balances:
        #
        #     {
        #         "accountId": "106490",
        #         "name": "main",
        #         "balances": [
        #             {
        #                 "asset": "OX",
        #                 "total": "-7.55145065000",
        #                 "available": "-71.16445065000",
        #                 "reserved": "0",
        #                 "lastUpdatedAt": "1715000448946"
        #             },
        #             {
        #                 "asset": "ETH",
        #                 "total": "0.01",
        #                 "available": "0.01",
        #                 "reserved": "0",
        #                 "lastUpdatedAt": "1714914512750"
        #             },
        #             ...
        #         ]
        #     }
        #
        result: dict = {
            'info': balance,
        }
        balances = self.safe_list(balance, 'balances', [])
        for i in range(0, len(balances)):
            balanceEntry = balances[i]
            currencyId = self.safe_string(balanceEntry, 'asset')
            code = self.safe_currency_code(currencyId)
            account = self.account()
            account['total'] = self.safe_string(balanceEntry, 'total')
            account['free'] = self.safe_string(balanceEntry, 'available')
            account['used'] = self.safe_string(balanceEntry, 'reserved')
            result[code] = account
        return self.safe_balance(result)

    async def fetch_accounts(self, params={}) -> List[Account]:
        """
        fetch subaccounts associated with a profile
        :see: https://docs.ox.fun/?json#get-v3-account-names
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `account structures <https://docs.ccxt_zagrava.com/#/?id=account-structure>` indexed by the account type
        """
        await self.load_markets()
        # self endpoint can only be called using API keys paired with the parent account! Returns all active subaccounts.
        response = await self.privateGetV3AccountNames(params)
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "accountId": "106526",
        #                 "name": "testSubAccount"
        #             },
        #             ...
        #         ]
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_accounts(data, params)

    def parse_account(self, account):
        #
        #     {
        #         "accountId": "106526",
        #         "name": "testSubAccount"
        #     },
        #
        return {
            'id': self.safe_string(account, 'accountId'),
            'type': None,
            'code': None,
            'info': account,
        }

    async def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
        """
        transfer currency internally between wallets on the same account
        :see: https://docs.ox.fun/?json#post-v3-transfer
        :param str code: unified currency code
        :param float amount: amount to transfer
        :param str fromAccount: account id to transfer from
        :param str toAccount: account id to transfer to
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `transfer structure <https://docs.ccxt_zagrava.com/#/?id=transfer-structure>`
        """
        # transferring funds between sub-accounts is restricted to API keys linked to the parent account.
        await self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'asset': currency['id'],
            'quantity': self.currency_to_precision(code, amount),
            'fromAccount': fromAccount,
            'toAccount': toAccount,
        }
        response = await self.privatePostV3Transfer(self.extend(request, params))
        #
        #     {
        #         timestamp: 1715430036267,
        #         datetime: '2024-05-11T12:20:36.267Z',
        #         currency: 'OX',
        #         amount: 10,
        #         fromAccount: '106464',
        #         toAccount: '106570',
        #         info: {
        #         asset: 'OX',
        #         quantity: '10',
        #         fromAccount: '106464',
        #         toAccount: '106570',
        #         transferredAt: '1715430036267'
        #         }
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        return self.parse_transfer(data, currency)

    async def fetch_transfers(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch a history of internal transfers made on an account
        :see: https://docs.ox.fun/?json#get-v3-transfer
        :param str code: unified currency code of the currency transferred
        :param int [since]: the earliest time in ms to fetch transfers for(default 24 hours ago)
        :param int [limit]: the maximum number of transfer structures to retrieve(default 50, max 200)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch transfers for(default time now)
        :returns dict[]: a list of `transfer structures <https://docs.ccxt_zagrava.com/#/?id=transfer-structure>`
        """
        # API keys linked to the parent account can get all account transfers, while API keys linked to a sub-account can only see transfers where the sub-account is either the "fromAccount" or "toAccount"
        await self.load_markets()
        request: dict = {}
        currency: Currency = None
        if code is not None:
            currency = self.currency(code)
            request['asset'] = currency['id']
        if since is not None:
            request['startTime'] = since  # startTime and endTime must be within 7 days of each other
        if limit is not None:
            request['limit'] = limit
        until = self.safe_integer(params, 'until')
        if until is not None:
            request['endTime'] = until
            params = self.omit(params, 'until')
        elif since is not None:
            request['endTime'] = self.sum(since, 7 * 24 * 60 * 60 * 1000)  # for the exchange not to raise an exception if since is younger than 7 days
        response = await self.privateGetV3Transfer(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "asset": "USDT",
        #                 "quantity": "5",
        #                 "fromAccount": "106490",
        #                 "toAccount": "106526",
        #                 "id": "966706320886267905",
        #                 "status": "COMPLETED",
        #                 "transferredAt": "1715085756708"
        #             },
        #             ...
        #         ]
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_transfers(data, currency, since, limit)

    def parse_transfer(self, transfer, currency: Currency = None):
        #
        # fetchTransfers
        #
        #     {
        #         "asset": "USDT",
        #         "quantity": "5",
        #         "fromAccount": "106490",
        #         "toAccount": "106526",
        #         "id": "966706320886267905",
        #         "status": "COMPLETED",
        #         "transferredAt": "1715085756708"
        #     }
        #
        timestamp = self.safe_integer(transfer, 'transferredAt')
        currencyId = self.safe_string(transfer, 'asset')
        return {
            'id': self.safe_string(transfer, 'id'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'currency': self.safe_currency_code(currencyId, currency),
            'amount': self.safe_number(transfer, 'quantity'),
            'fromAccount': self.safe_string(transfer, 'fromAccount'),
            'toAccount': self.safe_string(transfer, 'toAccount'),
            'status': self.parse_transfer_status(self.safe_string(transfer, 'status')),
            'info': transfer,
        }

    def parse_transfer_status(self, status):
        statuses: dict = {
            'COMPLETED': 'ok',
        }
        return self.safe_string(statuses, status, status)

    async def fetch_deposit_address(self, code: str, params={}):
        """
        fetch the deposit address for a currency associated with self account
        :see: https://docs.ox.fun/?json#get-v3-deposit-addresses
        :param str code: unified currency code
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.network]: network for fetch deposit address
        :returns dict: an `address structure <https://docs.ccxt_zagrava.com/#/?id=address-structure>`
        """
        networkCode = self.safe_string(params, 'network')
        networkId = self.network_code_to_id(networkCode, code)
        if networkId is None:
            raise BadRequest(self.id + ' fetchDepositAddress() require network parameter')
        await self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'asset': currency['id'],
            'network': networkId,
        }
        params = self.omit(params, 'network')
        response = await self.privateGetV3DepositAddresses(self.extend(request, params))
        #
        #     {"success":true,"data":{"address":"0x998dEc76151FB723963Bd8AFD517687b38D33dE8"}}
        #
        data = self.safe_dict(response, 'data', {})
        return self.parse_deposit_address(data, currency)

    def parse_deposit_address(self, depositAddress, currency: Currency = None):
        #
        #     {"address":"0x998dEc76151FB723963Bd8AFD517687b38D33dE8"}
        #
        address = self.safe_string(depositAddress, 'address')
        self.check_address(address)
        return {
            'currency': currency['code'],
            'address': address,
            'tag': None,
            'network': None,
            'info': depositAddress,
        }

    async def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all deposits made to an account
        :see: https://docs.ox.fun/?json#get-v3-deposit
        :param str code: unified currency code of the currency transferred
        :param int [since]: the earliest time in ms to fetch transfers for(default 24 hours ago)
        :param int [limit]: the maximum number of transfer structures to retrieve(default 50, max 200)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch transfers for(default time now)
        :returns dict[]: a list of `transfer structures <https://docs.ccxt_zagrava.com/#/?id=transfer-structure>`
        """
        await self.load_markets()
        request: dict = {}
        currency: Currency = None
        if code is not None:
            currency = self.currency(code)
            request['asset'] = currency['id']
        if since is not None:
            request['startTime'] = since  # startTime and endTime must be within 7 days of each other
        if limit is not None:
            request['limit'] = limit
        until = self.safe_integer(params, 'until')
        if until is not None:
            request['endTime'] = until
            params = self.omit(params, 'until')
        response = await self.privateGetV3Deposit(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "asset":"USDC",
        #                 "network":"Ethereum",
        #                 "address": "0x998dEc76151FB723963Bd8AFD517687b38D33dE8",
        #                 "quantity":"50",
        #                 "id":"5914",
        #                 "status": "COMPLETED",
        #                 "txId":"0xf5e79663830a0c6f94d46638dcfbc134566c12facf1832396f81ecb55d3c75dc",
        #                 "creditedAt":"1714821645154"
        #             }
        #         ]
        #     }
        #
        data = self.safe_list(response, 'data', [])
        for i in range(0, len(data)):
            data[i]['type'] = 'deposit'
        return self.parse_transactions(data, currency, since, limit)

    async def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all withdrawals made from an account
        :see: https://docs.ox.fun/?json#get-v3-withdrawal
        :param str code: unified currency code of the currency transferred
        :param int [since]: the earliest time in ms to fetch transfers for(default 24 hours ago)
        :param int [limit]: the maximum number of transfer structures to retrieve(default 50, max 200)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch transfers for(default time now)
        :returns dict[]: a list of `transaction structures <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        await self.load_markets()
        request: dict = {}
        currency: Currency = None
        if code is not None:
            currency = self.currency(code)
            request['asset'] = currency['id']
        if since is not None:
            request['startTime'] = since  # startTime and endTime must be within 7 days of each other
        if limit is not None:
            request['limit'] = limit
        until = self.safe_integer(params, 'until')
        if until is not None:
            request['endTime'] = until
            params = self.omit(params, 'until')
        response = await self.privateGetV3Withdrawal(self.extend(request, params))
        #
        #     {
        #         success: True,
        #         data: [
        #             {
        #                 id: '968163212989431811',
        #                 asset: 'OX',
        #                 network: 'Arbitrum',
        #                 address: '0x90fc1fB49a4ED8f485dd02A2a1Cf576897f6Bfc9',
        #                 quantity: '11.7444',
        #                 fee: '1.744400000',
        #                 status: 'COMPLETED',
        #                 txId: '0xe96b2d128b737fdbca927edf355cff42202e65b0fb960e64ffb9bd68c121f69f',
        #                 requestedAt: '1715530365450',
        #                 completedAt: '1715530527000'
        #             }
        #         ]
        #     }
        #
        data = self.safe_list(response, 'data', [])
        for i in range(0, len(data)):
            data[i]['type'] = 'withdrawal'
        return self.parse_transactions(data, currency, since, limit)

    def parse_transactions(self, transactions, currency: Currency = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        result = []
        for i in range(0, len(transactions)):
            transactions[i] = self.extend(transactions[i], params)
            transaction = self.parse_transaction(transactions[i], currency)
            result.append(transaction)
        result = self.sort_by(result, 'timestamp')
        code = currency['code'] if (currency is not None) else None
        return self.filter_by_currency_since_limit(result, code, since, limit)

    def parse_transaction(self, transaction, currency: Currency = None) -> Transaction:
        #
        #  fetchDeposits
        #     {
        #         "asset":"USDC",
        #         "network":"Ethereum",
        #         "address": "0x998dEc76151FB723963Bd8AFD517687b38D33dE8",
        #         "quantity":"50",
        #         "id":"5914",
        #         "status": "COMPLETED",
        #         "txId":"0xf5e79663830a0c6f94d46638dcfbc134566c12facf1832396f81ecb55d3c75dc",
        #         "creditedAt":"1714821645154"
        #     }
        #
        # fetchWithdrawals
        #     {
        #         id: '968163212989431811',
        #         asset: 'OX',
        #         network: 'Arbitrum',
        #         address: '0x90fc1fB49a4ED8f485dd02A2a1Cf576897f6Bfc9',
        #         quantity: '11.7444',
        #         fee: '1.744400000',
        #         status: 'COMPLETED',
        #         txId: '0xe96b2d128b737fdbca927edf355cff42202e65b0fb960e64ffb9bd68c121f69f',
        #         requestedAt: '1715530365450',
        #         completedAt: '1715530527000'
        #     }
        #
        # withdraw
        #     {
        #         "id": "968364664449302529",
        #         "asset": "OX",
        #         "network": "Arbitrum",
        #         "address": "0x90fc1fB49a4ED8f485dd02A2a1Cf576897f6Bfc9",
        #         "quantity": "10",
        #         "externalFee": False,
        #         "fee": "1.6728",
        #         "status": "PENDING",
        #         "requestedAt": "1715591843616"
        #     }
        #
        id = self.safe_string(transaction, 'id')
        type = self.safe_string(transaction, 'type')
        transaction = self.omit(transaction, 'type')
        address: Str = None
        addressTo: Str = None
        status: Str = None
        if type == 'deposit':
            address = self.safe_string(transaction, 'address')
            status = self.parse_deposit_status(self.safe_string(transaction, 'status'))
        elif type == 'withdrawal':
            addressTo = self.safe_string(transaction, 'address')
            status = self.parse_withdrawal_status(self.safe_string(transaction, 'status'))
        txid = self.safe_string(transaction, 'txId')
        currencyId = self.safe_string(transaction, 'asset')
        code = self.safe_currency_code(currencyId, currency)
        network = self.safe_string(transaction, 'network')
        networkCode = self.network_id_to_code(network)
        timestamp = self.safe_integer_2(transaction, 'creditedAt', 'requestedAt')
        amount = self.safe_number(transaction, 'quantity')
        feeCost = self.safe_number(transaction, 'fee')
        fee = None
        if feeCost is not None:
            fee = {
                'cost': feeCost,
                'currency': code,
            }
        return {
            'info': transaction,
            'id': id,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'network': networkCode,
            'address': address,
            'addressTo': addressTo,
            'addressFrom': None,
            'tag': None,
            'tagTo': None,
            'tagFrom': None,
            'type': type,
            'amount': amount,
            'currency': code,
            'status': status,
            'updated': None,
            'internal': None,
            'comment': None,
            'fee': fee,
        }

    def parse_deposit_status(self, status):
        statuses: dict = {
            'COMPLETED': 'ok',
        }
        return self.safe_string(statuses, status, status)

    def parse_withdrawal_status(self, status):
        statuses: dict = {
            'COMPLETED': 'ok',
            'PROCESSING': 'pending',
            'IN SWEEPING': 'pending',
            'PENDING': 'pending',
            'ON HOLD': 'pending',
            'CANCELED': 'canceled',
            'FAILED': 'failed',
        }
        return self.safe_string(statuses, status, status)

    async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
        """
        make a withdrawal
        :see: https://docs.bitflex.com/spot#withdraw
        :param str code: unified currency code
        :param float amount: the amount to withdraw
        :param str address: the address to withdraw to
        :param str tag:
        :param str [params.network]: network for withdraw
        :param bool [params.externalFee]: if False, then the fee is taken from the quantity, also with the burn fee for asset SOLO
        :param dict [params]: extra parameters specific to the exchange API endpoint
         *
         * EXCHANGE SPECIFIC PARAMETERS
        :param str [params.tfaType]: GOOGLE, or AUTHY_SECRET, or YUBIKEY, for 2FA
        :param str [params.code]: 2FA code
        :returns dict: a `transaction structure <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        tag, params = self.handle_withdraw_tag_and_params(tag, params)
        await self.load_markets()
        currency = self.currency(code)
        stringAmount = self.currency_to_precision(code, amount)
        request: dict = {
            'asset': currency['id'],
            'address': address,
            'quantity': stringAmount,
        }
        if tag is not None:
            request['memo'] = tag
        networkCode: Str = None
        networkCode, params = self.handle_network_code_and_params(params)
        if networkCode is not None:
            request['network'] = self.network_code_to_id(networkCode)
        request['externalFee'] = False
        response = await self.privatePostV3Withdrawal(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": {
        #             "id": "968364664449302529",
        #             "asset": "OX",
        #             "network": "Arbitrum",
        #             "address": "0x90fc1fB49a4ED8f485dd02A2a1Cf576897f6Bfc9",
        #             "quantity": "10",
        #             "externalFee": False,
        #             "fee": "1.6728",
        #             "status": "PENDING",
        #             "requestedAt": "1715591843616"
        #         }
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        data['type'] = 'withdrawal'
        return self.parse_transaction(data, currency)

    async def fetch_positions(self, symbols: Strings = None, params={}):
        """
        fetch all open positions
        :see: https://docs.ox.fun/?json#get-v3-positions
        :param str[]|None symbols: list of unified market symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param boolean [params.subAcc]:
        :returns dict[]: a list of `position structure <https://docs.ccxt_zagrava.com/#/?id=position-structure>`
        """
        # Calling self endpoint using an API key pair linked to the parent account with the parameter "subAcc"
        # allows the caller to include positions of additional sub-accounts in the response.
        # This feature does not work when using API key pairs linked to a sub-account
        await self.load_markets()
        symbols = self.market_symbols(symbols)
        response = await self.privateGetV3Positions(params)
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "accountId": "106490",
        #                 "name": "main",
        #                 "positions": [
        #                     {
        #                         "marketCode": "BTC-USD-SWAP-LIN",
        #                         "baseAsset": "BTC",
        #                         "counterAsset": "USD",
        #                         "position": "0.00010",
        #                         "entryPrice": "64300.0",
        #                         "markPrice": "63278",
        #                         "positionPnl": "-10.1900",
        #                         "estLiquidationPrice": "0",
        #                         "lastUpdatedAt": "1714915841448"
        #                     },
        #                     ...
        #                 ]
        #             },
        #             {
        #                 "accountId": "106526",
        #                 "name": "testSubAccount",
        #                 "positions": [
        #                     {
        #                         "marketCode": "ETH-USD-SWAP-LIN",
        #                         "baseAsset": "ETH",
        #                         "counterAsset": "USD",
        #                         "position": "0.001",
        #                         "entryPrice": "3080.5",
        #                         "markPrice": "3062.0",
        #                         "positionPnl": "-1.8500",
        #                         "estLiquidationPrice": "0",
        #                         "lastUpdatedAt": "1715089678013"
        #                     },
        #                     ...
        #                 ]
        #             }
        #         ]
        #     }
        #
        data = self.safe_list(response, 'data', [])
        allPositions = []
        for i in range(0, len(data)):
            account = data[i]
            positions = self.safe_list(account, 'positions', [])
            allPositions = self.array_concat(allPositions, positions)
        return self.parse_positions(allPositions, symbols)

    def parse_position(self, position, market: Market = None):
        #
        #     {
        #         "marketCode": "ETH-USD-SWAP-LIN",
        #         "baseAsset": "ETH",
        #         "counterAsset": "USD",
        #         "position": "0.001",
        #         "entryPrice": "3080.5",
        #         "markPrice": "3062.0",
        #         "positionPnl": "-1.8500",
        #         "estLiquidationPrice": "0",
        #         "lastUpdatedAt": "1715089678013"
        #     }
        #
        marketId = self.safe_string(position, 'marketCode')
        market = self.safe_market(marketId, market)
        return self.safe_position({
            'info': position,
            'id': None,
            'symbol': market['symbol'],
            'notional': None,
            'marginMode': 'cross',
            'liquidationPrice': self.safe_number(position, 'estLiquidationPrice'),
            'entryPrice': self.safe_number(position, 'entryPrice'),
            'unrealizedPnl': self.safe_number(position, 'positionPnl'),
            'realizedPnl': None,
            'percentage': None,
            'contracts': self.safe_number(position, 'position'),
            'contractSize': None,
            'markPrice': self.safe_number(position, 'markPrice'),
            'lastPrice': None,
            'side': None,
            'hedged': None,
            'timestamp': None,
            'datetime': None,
            'lastUpdateTimestamp': self.safe_integer(position, 'lastUpdatedAt'),
            'maintenanceMargin': None,
            'maintenanceMarginPercentage': None,
            'collateral': None,
            'initialMargin': None,
            'initialMarginPercentage': None,
            'leverage': None,
            'marginRatio': None,
            'stopLossPrice': None,
            'takeProfitPrice': None,
        })

    async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}) -> Order:
        """
        create a trade order
        :see: https://docs.ox.fun/?json#post-v3-orders-place
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market', 'limit', 'STOP_LIMIT' or 'STOP_MARKET'
        :param str 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 dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.clientOrderId]: a unique id for the order
        :param int [params.timestamp]: in milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected.
        :param int [params.recvWindow]: in milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used.
        :param str [params.responseType]: FULL or ACK
        :param float [params.cost]: the quote quantity that can be used alternative for the amount for market buy orders
        :param float [params.triggerPrice]: The price at which a trigger order is triggered at
        :param float [params.limitPrice]: Limit price for the STOP_LIMIT order
        :param bool [params.postOnly]: if True, the order will only be posted if it will be a maker order
        :param str [params.timeInForce]: GTC(default), IOC, FOK, PO, MAKER_ONLY or MAKER_ONLY_REPRICE(reprices order to the best maker only price if the specified price were to lead to a taker trade)
        :param str [params.selfTradePreventionMode]: NONE, EXPIRE_MAKER, EXPIRE_TAKER or EXPIRE_BOTH for more info check here {@link https://docs.ox.fun/?json#self-trade-prevention-modes}
        :param str [params.displayQuantity]: for an iceberg order, pass both quantity and displayQuantity fields in the order request
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        request: dict = {
            'responseType': self.safe_string(params, 'responseType', 'FULL'),
            'timestamp': self.safe_integer(params, 'timestamp', self.milliseconds()),
        }
        params = self.omit(params, ['responseType', 'timestamp'])
        recvWindow = self.safe_integer(params, 'recvWindow')
        if recvWindow is not None:
            request['recvWindow'] = recvWindow
            params = self.omit(params, 'recvWindow')
        orderRequest = self.create_order_request(symbol, type, side, amount, price, params)
        request['orders'] = [orderRequest]
        response = await self.privatePostV3OrdersPlace(request)
        #
        # accepted market order responseType FULL
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "notice": "OrderMatched",
        #                 "accountId": "106490",
        #                 "orderId": "1000109901865",
        #                 "submitted": True,
        #                 "clientOrderId": "0",
        #                 "marketCode": "OX-USDT",
        #                 "status": "FILLED",
        #                 "side": "SELL",
        #                 "isTriggered": False,
        #                 "quantity": "150.0",
        #                 "amount": "0.0",
        #                 "remainQuantity": "0.0",
        #                 "matchId": "100017047880451399",
        #                 "matchPrice": "0.01465",
        #                 "matchQuantity": "150.0",
        #                 "feeInstrumentId": "USDT",
        #                 "fees": "0.0015382500",
        #                 "orderType": "MARKET",
        #                 "createdAt": "1715592472236",
        #                 "lastMatchedAt": "1715592472200",
        #                 "displayQuantity": "150.0"
        #             }
        #         ]
        #     }
        #
        # accepted limit order responseType FULL
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "notice": "OrderOpened",
        #                 "accountId": "106490",
        #                 "orderId": "1000111482406",
        #                 "submitted": True,
        #                 "clientOrderId": "0",
        #                 "marketCode": "ETH-USD-SWAP-LIN",
        #                 "status": "OPEN",
        #                 "side": "SELL",
        #                 "price": "4000.0",
        #                 "isTriggered": False,
        #                 "quantity": "0.01",
        #                 "amount": "0.0",
        #                 "orderType": "LIMIT",
        #                 "timeInForce": "GTC",
        #                 "createdAt": "1715763507682",
        #                 "displayQuantity": "0.01"
        #             }
        #         ]
        #     }
        #
        # accepted order responseType ACK
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "accountId": "106490",
        #                 "orderId": "1000109892193",
        #                 "submitted": True,
        #                 "marketCode": "OX-USDT",
        #                 "side": "BUY",
        #                 "price": "0.01961",
        #                 "isTriggered": False,
        #                 "quantity": "100",
        #                 "orderType": "MARKET",
        #                 "timeInForce": "IOC",
        #                 "createdAt": "1715591529057",
        #                 "selfTradePreventionMode": "NONE"
        #             }
        #         ]
        #     }
        #
        #  rejected order(balance insufficient)
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "code": "710001",
        #                 "message": "System failure, exception thrown -> null",
        #                 "submitted": False,
        #                 "marketCode": "OX-USDT",
        #                 "side": "BUY",
        #                 "price": "0.01961",
        #                 "amount": "100",
        #                 "orderType": "MARKET",
        #                 "timeInForce": "IOC",
        #                 "createdAt": "1715591678835",
        #                 "source": 11,
        #                 "selfTradePreventionMode": "NONE"
        #             }
        #         ]
        #     }
        #
        # rejected order(bad request)
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "code": "20044",
        #                 "message": "Amount is not supported for self order type",
        #                 "submitted": False,
        #                 "marketCode": "OX-USDT",
        #                 "side": "SELL",
        #                 "amount": "200",
        #                 "orderType": "MARKET",
        #                 "createdAt": "1715592079986",
        #                 "source": 11
        #             }
        #         ]
        #     }
        #
        data = self.safe_list(response, 'data', [])
        order = self.safe_dict(data, 0, {})
        return self.parse_order(order)

    async def create_orders(self, orders: List[OrderRequest], params={}) -> List[Order]:
        """
        create a list of trade orders
        :see: https://docs.ox.fun/?json#post-v3-orders-place
        :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 dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.timestamp]: *for all orders* in milliseconds. If orders reach the matching engine and the current timestamp exceeds timestamp + recvWindow, then all orders will be rejected.
        :param int [params.recvWindow]: *for all orders* in milliseconds. If orders reach the matching engine and the current timestamp exceeds timestamp + recvWindow, then all orders will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used.
        :param str [params.responseType]: *for all orders* FULL or ACK
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        ordersRequests = []
        for i in range(0, len(orders)):
            rawOrder = orders[i]
            symbol = self.safe_string(rawOrder, 'symbol')
            type = self.safe_string(rawOrder, 'type')
            side = self.safe_string(rawOrder, 'side')
            amount = self.safe_number(rawOrder, 'amount')
            price = self.safe_number(rawOrder, 'price')
            orderParams = self.safe_dict(rawOrder, 'params', {})
            orderRequest = self.create_order_request(symbol, type, side, amount, price, orderParams)
            ordersRequests.append(orderRequest)
        request: dict = {
            'responseType': 'FULL',
            'timestamp': self.milliseconds(),
            'orders': ordersRequests,
        }
        response = await self.privatePostV3OrdersPlace(self.extend(request, params))
        data = self.safe_list(response, 'data', [])
        return self.parse_orders(data)

    def create_order_request(self, symbol: str, type: str, side: str, amount, price=None, params={}):
        """
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market', 'limit', 'STOP_LIMIT' or 'STOP_MARKET'
        :param str 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 dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.clientOrderId]: a unique id for the order
        :param float [params.cost]: the quote quantity that can be used alternative for the amount for market buy orders
        :param float [params.triggerPrice]: The price at which a trigger order is triggered at
        :param float [params.limitPrice]: Limit price for the STOP_LIMIT order
        :param bool [params.postOnly]: if True, the order will only be posted if it will be a maker order
        :param str [params.timeInForce]: GTC(default), IOC, FOK, PO, MAKER_ONLY or MAKER_ONLY_REPRICE(reprices order to the best maker only price if the specified price were to lead to a taker trade)
        :param str [params.selfTradePreventionMode]: NONE, EXPIRE_MAKER, EXPIRE_TAKER or EXPIRE_BOTH for more info check here {@link https://docs.ox.fun/?json#self-trade-prevention-modes}
        :param str [params.displayQuantity]: for an iceberg order, pass both quantity and displayQuantity fields in the order request
        """
        market = self.market(symbol)
        request: dict = {
            'marketCode': market['id'],
            'side': side.upper(),
            'source': 1000,
        }
        cost = self.safe_string_2(params, 'cost', 'amount')
        if cost is not None:
            request['amount'] = cost  # todo costToPrecision
            params = self.omit(params, ['cost', 'amount'])
        else:
            request['quantity'] = amount  # todo amountToPrecision
        triggerPrice = self.safe_string_2(params, 'triggerPrice', 'stopPrice')
        orderType = type.upper()
        if triggerPrice is not None:
            if orderType == 'MARKET':
                orderType = 'STOP_MARKET'
            elif orderType == 'LIMIT':
                orderType = 'STOP_LIMIT'
            request['stopPrice'] = triggerPrice  # todo priceToPrecision
            params = self.omit(params, ['triggerPrice', 'stopPrice'])
        request['orderType'] = orderType
        if orderType == 'STOP_LIMIT':
            request['limitPrice'] = price  # todo priceToPrecision
        elif price is not None:
            request['price'] = price  # todo priceToPrecision
        postOnly: Bool = None
        isMarketOrder = (orderType == 'MARKET') or (orderType == 'STOP_MARKET')
        postOnly, params = self.handle_post_only(isMarketOrder, False, params)
        timeInForce = self.safe_string_upper(params, 'timeInForce')
        if postOnly and (timeInForce != 'MAKER_ONLY_REPRICE'):
            request['timeInForce'] = 'MAKER_ONLY'
        return self.extend(request, params)

    async def create_market_buy_order_with_cost(self, symbol: str, cost: float, params={}):
        """
        create a market buy order by providing the symbol and cost
        :see: https://open.big.one/docs/spot_orders.html#create-order
        :param str symbol: unified symbol of the market to create an order in
        :param float cost: how much you want to trade in units of the quote currency
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        if not market['spot']:
            raise NotSupported(self.id + ' createMarketBuyOrderWithCost() supports spot orders only')
        request: dict = {
            'cost': cost,
        }
        return await self.create_order(symbol, 'market', 'buy', None, None, self.extend(request, params))

    async def fetch_order(self, id: str, symbol: Str = None, params={}) -> Order:
        """
        :see: https://docs.ox.fun/?json#get-v3-orders-status
        fetches information on an order made by the user
        :param str id: a unique id for the order
        :param str [symbol]: not used by oxfun fetchOrder
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.clientOrderId]: the client order id of the order
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        request: dict = {
            'orderId': id,
        }
        response = await self.privateGetV3OrdersStatus(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": {
        #             "orderId": "1000111762980",
        #             "clientOrderId": "0",
        #             "marketCode": "ETH-USD-SWAP-LIN",
        #             "status": "OPEN",
        #             "side": "BUY",
        #             "price": "2700.0",
        #             "isTriggered": False,
        #             "remainQuantity": "0.01",
        #             "totalQuantity": "0.01",
        #             "amount": "0",
        #             "displayQuantity": "0.01",
        #             "cumulativeMatchedQuantity": "0",
        #             "orderType": "STOP_LIMIT",
        #             "timeInForce": "GTC",
        #             "source": "11",
        #             "createdAt": "1715794191277"
        #         }
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        return self.parse_order(data)

    async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetch all unfilled currently open orders
        :see: https://docs.ox.fun/?json#get-v3-orders-working
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch open orders for
        :param int [limit]: the maximum number of  open orders structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.orderId]: a unique id for the order
        :param int [params.clientOrderId]: the client order id of the order
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        request: dict = {}
        market: Market = None
        if symbol is not None:
            market = self.market(symbol)
        response = await self.privateGetV3OrdersWorking(self.extend(request, params))
        data = self.safe_list(response, 'data', [])
        return self.parse_orders(data, market, since, limit)

    async def cancel_order(self, id: str, symbol: Str = None, params={}):
        """
        cancels an open order
        :see: https://docs.ox.fun/?json#delete-v3-orders-cancel
        :param str id: order id
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.clientOrderId]: a unique id for the order
        :param int [params.timestamp]: in milliseconds
        :param int [params.recvWindow]: in milliseconds
        :param str [params.responseType]: 'FULL' or 'ACK'
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
        market = self.market(symbol)
        marketId = market['id']
        request: dict = {
            'timestamp': self.milliseconds(),
            'responseType': 'FULL',
        }
        orderRequest = {
            'marketCode': marketId,
            'orderId': id,
        }
        clientOrderId = self.safe_integer(params, 'clientOrderId')
        if clientOrderId is not None:
            orderRequest['clientOrderId'] = clientOrderId
        request['orders'] = [orderRequest]
        response = await self.privateDeleteV3OrdersCancel(self.extend(request, params))
        data = self.safe_list(response, 'data', [])
        order = self.safe_dict(data, 0, {})
        return self.parse_order(order)

    async def cancel_all_orders(self, symbol: Str = None, params={}):
        """
        cancel all open orders
        :see: https://docs.ox.fun/?json#delete-v3-orders-cancel-all
        :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: response from exchange
        """
        request: dict = {}
        if symbol is not None:
            market = self.market(symbol)
            request['marketCode'] = market['id']
        #
        #     {
        #         "success": True,
        #         "data": {"notice": "Orders queued for cancelation"}
        #     }
        #
        #     {
        #         "success": True,
        #         "data": {"notice": "No working orders found"}
        #     }
        #
        return await self.privateDeleteV3OrdersCancelAll(self.extend(request, params))

    async def cancel_orders(self, ids: List[str], symbol: Str = None, params={}):
        """
        cancel multiple orders
        :see: https://docs.ox.fun/?json#delete-v3-orders-cancel
        :param str[] ids: order ids
        :param str [symbol]: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.timestamp]: in milliseconds
        :param int [params.recvWindow]: in milliseconds
        :param str [params.responseType]: 'FULL' or 'ACK'
        :returns dict: an list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrders() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        marketId = market['id']
        request: dict = {
            'timestamp': self.milliseconds(),
            'responseType': 'FULL',
        }
        orders = []
        for i in range(0, len(ids)):
            order = {
                'marketCode': marketId,
                'orderId': ids[i],
            }
            orders.append(order)
        request['orders'] = orders
        response = await self.privateDeleteV3OrdersCancel(self.extend(request, params))
        data = self.safe_list(response, 'data', [])
        return self.parse_orders(data, market)

    def parse_order(self, order, market: Market = None) -> Order:
        #
        # accepted market order responseType FULL
        #     {
        #         "notice": "OrderMatched",
        #         "accountId": "106490",
        #         "orderId": "1000109901865",
        #         "submitted": True,
        #         "clientOrderId": "0",
        #         "marketCode": "OX-USDT",
        #         "status": "FILLED",
        #         "side": "SELL",
        #         "isTriggered": False,
        #         "quantity": "150.0",
        #         "amount": "0.0",
        #         "remainQuantity": "0.0",
        #         "matchId": "100017047880451399",
        #         "matchPrice": "0.01465",
        #         "matchQuantity": "150.0",
        #         "feeInstrumentId": "USDT",
        #         "fees": "0.0015382500",
        #         "orderType": "MARKET",
        #         "createdAt": "1715592472236",
        #         "lastMatchedAt": "1715592472200",
        #         "displayQuantity": "150.0"
        #     }
        #
        # accepted limit order responseType FULL
        #     {
        #         "notice": "OrderOpened",
        #         "accountId": "106490",
        #         "orderId": "1000111482406",
        #         "submitted": True,
        #         "clientOrderId": "0",
        #         "marketCode": "ETH-USD-SWAP-LIN",
        #         "status": "OPEN",
        #         "side": "SELL",
        #         "price": "4000.0",
        #         "isTriggered": False,
        #         "quantity": "0.01",
        #         "amount": "0.0",
        #         "orderType": "LIMIT",
        #         "timeInForce": "GTC",
        #         "createdAt": "1715763507682",
        #         "displayQuantity": "0.01"
        #     }
        #
        # accepted order responseType ACK
        #     {
        #         "accountId": "106490",
        #         "orderId": "1000109892193",
        #         "submitted": True,
        #         "marketCode": "OX-USDT",
        #         "side": "BUY",
        #         "price": "0.01961",
        #         "isTriggered": False,
        #         "quantity": "100",
        #         "orderType": "MARKET",
        #         "timeInForce": "IOC",
        #         "createdAt": "1715591529057",
        #         "selfTradePreventionMode": "NONE"
        #     }
        #
        #  rejected order(balance insufficient)
        #     {
        #         "code": "710001",
        #         "message": "System failure, exception thrown -> null",
        #         "submitted": False,
        #         "marketCode": "OX-USDT",
        #         "side": "BUY",
        #         "price": "0.01961",
        #         "amount": "100",
        #         "orderType": "MARKET",
        #         "timeInForce": "IOC",
        #         "createdAt": "1715591678835",
        #         "source": 11,
        #         "selfTradePreventionMode": "NONE"
        #     }
        #
        # rejected order(bad request)
        #     {
        #         "code": "20044",
        #         "message": "Amount is not supported for self order type",
        #         "submitted": False,
        #         "marketCode": "OX-USDT",
        #         "side": "SELL",
        #         "amount": "200",
        #         "orderType": "MARKET",
        #         "createdAt": "1715592079986",
        #         "source": 11
        #     }
        #
        # fetchOrder
        #     {
        #         "orderId": "1000111762980",
        #         "clientOrderId": "0",
        #         "marketCode": "ETH-USD-SWAP-LIN",
        #         "status": "OPEN",
        #         "side": "BUY",
        #         "price": "2700.0",
        #         "isTriggered": False,
        #         "remainQuantity": "0.01",
        #         "totalQuantity": "0.01",
        #         "amount": "0",
        #         "displayQuantity": "0.01",
        #         "cumulativeMatchedQuantity": "0",
        #         "orderType": "STOP_LIMIT",
        #         "timeInForce": "GTC",
        #         "source": "11",
        #         "createdAt": "1715794191277"
        #     }
        #
        marketId = self.safe_string(order, 'marketCode')
        market = self.safe_market(marketId, market)
        timestamp = self.safe_integer(order, 'createdAt')
        fee = None
        feeCurrency = self.safe_string(order, 'feeInstrumentId')
        if feeCurrency is not None:
            fee = {
                'currency': self.safe_currency_code(feeCurrency),
                'cost': self.safe_number(order, 'fees'),
            }
        status = self.safe_string(order, 'status')
        code = self.safe_integer(order, 'code')  # rejected orders have code of the error
        if code is not None:
            status = 'rejected'
        triggerPrice = self.safe_string(order, 'stopPrice')
        return self.safe_order({
            'id': self.safe_string(order, 'orderId'),
            'clientOrderId': self.safe_string(order, 'clientOrderId'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': self.safe_integer(order, 'lastMatchedAt'),
            'lastUpdateTimestamp': self.safe_integer(order, 'lastModifiedAt'),
            'status': self.parse_order_status(status),
            'symbol': market['symbol'],
            'type': self.parse_order_type(self.safe_string(order, 'orderType')),
            'timeInForce': self.parse_order_time_in_force(self.safe_string(order, 'timeInForce')),  # only for limit orders
            'side': self.safe_string_lower(order, 'side'),
            'price': self.safe_string_n(order, ['price', 'matchPrice', 'limitPrice']),
            'average': None,
            'amount': self.safe_string_2(order, 'totalQuantity', 'quantity'),
            'filled': self.safe_string_2(order, 'cumulativeMatchedQuantity', 'matchQuantity'),
            'remaining': self.safe_string(order, 'remainQuantity'),
            'triggerPrice': triggerPrice,
            'stopLossPrice': triggerPrice,
            'cost': self.omit_zero(self.safe_string(order, 'amount')),
            'trades': None,
            'fee': fee,
            'info': order,
        }, market)

    def parse_order_status(self, status):
        statuses: dict = {
            'OPEN': 'open',
            'PARTIALLY_FILLED': 'open',
            'PARTIAL_FILL': 'open',
            'FILLED': 'closed',
            'CANCELED': 'canceled',
            'CANCELED_BY_USER': 'canceled',
            'CANCELED_BY_MAKER_ONLY': 'rejected',
            'CANCELED_BY_FOK': 'rejected',
            'CANCELED_ALL_BY_IOC': 'rejected',
            'CANCELED_PARTIAL_BY_IOC': 'canceled',
            'CANCELED_BY_SELF_TRADE_PROTECTION': 'rejected',
        }
        return self.safe_string(statuses, status, status)

    def parse_order_type(self, type):
        types: dict = {
            'LIMIT': 'limit',
            'STOP_LIMIT': 'limit',
            'MARKET': 'market',
            'STOP_MARKET': 'market',
        }
        return self.safe_string(types, type, type)

    def parse_order_time_in_force(self, type):
        types: dict = {
            'GTC': 'GTC',
            'IOC': 'IOC',
            'FOK': 'FOK',
            'MAKER_ONLY': 'PO',
            'MAKER_ONLY_REPRICE': 'PO',
        }
        return self.safe_string(types, type, type)

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        baseUrl = self.urls['api'][api]
        url = baseUrl + '/' + path
        queryString = ''
        if method == 'GET':
            queryString = self.urlencode(params)
            if len(queryString) != 0:
                url += '?' + queryString
        if api == 'private':
            self.check_required_credentials()
            timestamp = self.milliseconds()
            isoDatetime = self.iso8601(timestamp)
            datetimeParts = isoDatetime.split('.')
            datetime = datetimeParts[0]
            nonce = self.nonce()
            urlParts = baseUrl.split('//')
            if (method == 'POST') or (method == 'DELETE'):
                body = self.json(params)
                queryString = body
            msgString = datetime + '\n' + str(nonce) + '\n' + method + '\n' + urlParts[1] + '\n/' + path + '\n' + queryString
            signature = self.hmac(self.encode(msgString), self.encode(self.secret), hashlib.sha256, 'base64')
            headers = {
                'Content-Type': 'application/json',
                'AccessKey': self.apiKey,
                'Timestamp': datetime,
                'Signature': signature,
                'Nonce': nonce,
            }
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
        if response is None:
            return None
        if code != 200:
            responseCode = self.safe_string(response, 'code', None)
            feedback = self.id + ' ' + body
            self.throw_broadly_matched_exception(self.exceptions['broad'], body, feedback)
            self.throw_exactly_matched_exception(self.exceptions['exact'], responseCode, feedback)
            raise ExchangeError(feedback)
        return None
