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

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

from ccxt.base.exchange import Exchange
from ccxt.abstract.bydfi import ImplicitAPI
import hashlib
from ccxt.base.types import Any, Balances, Currency, Int, Leverage, MarginMode, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, FundingRate, Trade, Transaction, FundingRateHistory, TransferEntry
from typing import List
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import PermissionDenied
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import InsufficientFunds
from ccxt.base.errors import NotSupported
from ccxt.base.errors import RateLimitExceeded
from ccxt.base.decimal_to_precision import TICK_SIZE
from ccxt.base.precise import Precise


class bydfi(Exchange, ImplicitAPI):

    def describe(self) -> Any:
        return self.deep_extend(super(bydfi, self).describe(), {
            'id': 'bydfi',
            'name': 'BYDFi',
            'countries': ['SG'],  # Singapore todo check
            'rateLimit': 50,  # 20 requests per second
            'version': 'v1',
            'certified': False,
            'pro': True,
            'has': {
                'CORS': None,
                'spot': False,
                'margin': False,
                'swap': True,
                'future': False,
                'option': False,
                'addMargin': False,
                'borrowCrossMargin': False,
                'borrowIsolatedMargin': False,
                'borrowMargin': False,
                'cancelAllOrders': True,
                'cancelOrder': False,
                'cancelOrders': False,
                'cancelOrdersWithClientOrderId': False,
                'cancelOrderWithClientOrderId': False,
                'closeAllPositions': False,
                'closePosition': False,
                'createDepositAddress': False,
                'createLimitBuyOrder': False,
                'createLimitOrder': True,
                'createLimitSellOrder': False,
                'createMarketBuyOrder': False,
                'createMarketBuyOrderWithCost': False,
                'createMarketOrder': True,
                'createMarketOrderWithCost': False,
                'createMarketSellOrder': False,
                'createMarketSellOrderWithCost': False,
                'createOrder': True,
                'createOrders': True,
                'createOrderWithTakeProfitAndStopLoss': False,
                'createPostOnlyOrder': True,
                'createReduceOnlyOrder': True,
                'createStopLimitOrder': True,
                'createStopLossOrder': True,
                'createStopMarketOrder': False,
                'createStopOrder': False,
                'createTakeProfitOrder': True,
                'createTrailingAmountOrder': False,
                'createTrailingPercentOrder': True,
                'createTriggerOrder': False,
                'deposit': False,
                'editOrder': True,
                'editOrders': True,
                'editOrderWithClientOrderId': True,
                'fetchAccounts': False,
                'fetchBalance': True,
                'fetchBidsAsks': False,
                'fetchBorrowInterest': False,
                'fetchBorrowRate': False,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchBorrowRates': False,
                'fetchBorrowRatesPerSymbol': False,
                'fetchCanceledAndClosedOrders': True,
                'fetchCanceledOrders': False,
                'fetchClosedOrder': False,
                'fetchClosedOrders': False,
                'fetchConvertCurrencies': False,
                'fetchConvertQuote': False,
                'fetchConvertTrade': False,
                'fetchConvertTradeHistory': False,
                'fetchCrossBorrowRate': False,
                'fetchCrossBorrowRates': False,
                'fetchCurrencies': False,
                'fetchDeposit': False,
                'fetchDepositAddress': False,
                'fetchDepositAddresses': False,
                'fetchDepositAddressesByNetwork': False,
                'fetchDeposits': True,
                'fetchDepositsWithdrawals': False,
                'fetchDepositWithdrawFee': False,
                'fetchDepositWithdrawFees': False,
                'fetchFundingHistory': False,
                'fetchFundingInterval': False,
                'fetchFundingIntervals': False,
                'fetchFundingRate': True,
                'fetchFundingRateHistory': True,
                'fetchFundingRates': False,
                'fetchGreeks': False,
                'fetchIndexOHLCV': False,
                'fetchIsolatedBorrowRate': False,
                'fetchIsolatedBorrowRates': False,
                'fetchIsolatedPositions': False,
                'fetchL2OrderBook': True,
                'fetchL3OrderBook': False,
                'fetchLastPrices': False,
                'fetchLedger': False,
                'fetchLedgerEntry': False,
                'fetchLeverage': True,
                'fetchLeverages': False,
                'fetchLeverageTiers': False,
                'fetchLiquidations': False,
                'fetchLongShortRatio': False,
                'fetchLongShortRatioHistory': False,
                'fetchMarginAdjustmentHistory': False,
                'fetchMarginMode': True,
                'fetchMarginModes': False,
                'fetchMarketLeverageTiers': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMarkPrices': False,
                'fetchMyLiquidations': False,
                'fetchMySettlementHistory': False,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenInterest': False,
                'fetchOpenInterestHistory': False,
                'fetchOpenInterests': False,
                'fetchOpenOrder': False,
                'fetchOpenOrders': True,
                'fetchOption': False,
                'fetchOptionChain': False,
                'fetchOrder': False,
                'fetchOrderBook': True,
                'fetchOrderBooks': False,
                'fetchOrders': False,
                'fetchOrdersByStatus': False,
                'fetchOrderTrades': False,
                'fetchOrderWithClientOrderId': False,
                'fetchPosition': False,
                'fetchPositionHistory': True,
                'fetchPositionMode': True,
                'fetchPositions': True,
                'fetchPositionsForSymbol': True,
                'fetchPositionsHistory': True,
                'fetchPositionsRisk': False,
                'fetchPremiumIndexOHLCV': False,
                'fetchSettlementHistory': False,
                'fetchStatus': False,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': False,
                'fetchTrades': True,
                'fetchTradingFee': False,
                'fetchTradingFees': False,
                'fetchTradingLimits': False,
                'fetchTransactionFee': False,
                'fetchTransactionFees': False,
                'fetchTransactions': False,
                'fetchTransfer': False,
                'fetchTransfers': True,
                'fetchUnderlyingAssets': False,
                'fetchVolatilityHistory': False,
                'fetchWithdrawAddresses': False,
                'fetchWithdrawal': False,
                'fetchWithdrawals': True,
                'fetchWithdrawalWhitelist': False,
                'reduceMargin': False,
                'repayCrossMargin': False,
                'repayIsolatedMargin': False,
                'setLeverage': True,
                'setMargin': False,
                'setMarginMode': True,
                'setPositionMode': True,
                'signIn': False,
                'transfer': True,
                'watchMyLiquidationsForSymbols': False,
                'withdraw': False,
                'ws': True,
            },
            'urls': {
                'logo': 'https://github.com/user-attachments/assets/bfffb73d-29bd-465d-b75b-98e210491769',
                'api': {
                    'public': 'https://api.bydfi.com/api',
                    'private': 'https://api.bydfi.com/api',
                },
                'www': 'https://bydfi.com/',
                'doc': 'https://developers.bydfi.com/en/',
                'referral': 'https://partner.bydfi.com/j/DilWutCI',
            },
            'fees': {
            },
            'api': {
                'public': {
                    'get': {
                        'v1/public/api_limits': 1,  # https://developers.bydfi.com/en/public#inquiry-into-api-rate-limit-configuration
                        'v1/fapi/market/exchange_info': 1,
                        'v1/fapi/market/depth': 1,
                        'v1/fapi/market/trades': 1,
                        'v1/fapi/market/klines': 1,
                        'v1/fapi/market/ticker/24hr': 1,
                        'v1/fapi/market/ticker/price': 1,  # https://developers.bydfi.com/en/futures/market#latest-price
                        'v1/fapi/market/mark_price': 1,  # https://developers.bydfi.com/en/futures/market#mark-price
                        'v1/fapi/market/funding_rate': 1,
                        'v1/fapi/market/funding_rate_history': 1,
                        'v1/fapi/market/risk_limit': 1,  # https://developers.bydfi.com/en/futures/market#risk-limit
                    },
                },
                'private': {
                    'get': {
                        'v1/account/assets': 1,
                        'v1/account/transfer_records': 1,
                        'v1/spot/deposit_records': 1,
                        'v1/spot/withdraw_records': 1,
                        'v1/fapi/trade/open_order': 1,
                        'v1/fapi/trade/plan_order': 1,
                        'v1/fapi/trade/leverage': 1,
                        'v1/fapi/trade/history_order': 1,
                        'v1/fapi/trade/history_trade': 1,
                        'v1/fapi/trade/position_history': 1,
                        'v1/fapi/trade/positions': 1,
                        'v1/fapi/account/balance': 1,
                        'v1/fapi/user_data/assets_margin': 1,
                        'v1/fapi/user_data/position_side/dual': 1,
                        'v1/agent/teams': 1,  # https://developers.bydfi.com/en/agent/#query-kol-subordinate-team-information
                        'v1/agent/agent_links': 1,  # https://developers.bydfi.com/en/agent/#query-kol-invitation-code-list
                        'v1/agent/regular_overview': 1,  # https://developers.bydfi.com/en/agent/#query-kol-direct-client-data-list
                        'v1/agent/agent_sub_overview': 1,  # https://developers.bydfi.com/en/agent/#query-kol-subordinate-affiliate-list
                        'v1/agent/partener_user_deposit': 1,  # https://developers.bydfi.com/en/agent/#check-the-recharge-amount-of-kol-within-one-year
                        'v1/agent/partener_users_data': 1,  # https://developers.bydfi.com/en/agent/#query-kol-subordinate-deposit-and-trading-data
                        'v1/agent/affiliate_uids': 1,  # https://developers.bydfi.com/en/agent/#get-affiliate-uids
                        'v1/agent/affiliate_commission': 1,  # https://developers.bydfi.com/en/agent/#get-affiliate-commission
                        'v1/agent/internal_withdrawal_status': 1,  # https://developers.bydfi.com/en/agent/#get-internal-withdrawal-status
                    },
                    'post': {
                        'v1/account/transfer': 1,
                        'v1/fapi/trade/place_order': 1,
                        'v1/fapi/trade/batch_place_order': 1,
                        'v1/fapi/trade/edit_order': 1,
                        'v1/fapi/trade/batch_edit_order': 1,
                        'v1/fapi/trade/cancel_all_order': 1,
                        'v1/fapi/trade/leverage': 1,
                        'v1/fapi/trade/batch_leverage_margin': 1,  # https://developers.bydfi.com/en/futures/trade#modify-leverage-and-margin-type-with-one-click
                        'v1/fapi/user_data/margin_type': 1,
                        'v1/fapi/user_data/position_side/dual': 1,
                        'v1/agent/internal_withdrawal': 1,  # https://developers.bydfi.com/en/agent/#internal-withdrawal
                    },
                },
            },
            'features': {
                'spot': None,
                'swap': {
                    'linear': {
                        'sandbox': False,
                        'createOrder': {
                            'marginMode': False,
                            'triggerPrice': False,
                            'triggerPriceType': {
                                'mark': True,
                                'last': True,
                                'index': False,
                            },
                            'stopLossPrice': True,
                            'takeProfitPrice': True,
                            'attachedStopLossTakeProfit': None,  # not supported
                            'timeInForce': {
                                'IOC': True,
                                'FOK': True,
                                'PO': True,
                                'GTD': False,
                            },
                            'hedged': True,
                            'selfTradePrevention': False,
                            'trailing': True,
                            'iceberg': False,
                            'leverage': False,
                            'marketBuyRequiresPrice': False,
                            'marketBuyByCost': False,
                        },
                        'createOrders': {
                            'max': 5,
                        },
                        'fetchMyTrades': {
                            'marginMode': False,
                            'daysBack': 182,  # 6 months
                            'limit': 500,
                            'untilDays': 7,
                            'symbolRequired': False,
                        },
                        'fetchOrder': None,
                        'fetchOpenOrder': {
                            'marginMode': False,
                            'trigger': True,
                            'trailing': False,
                            'symbolRequired': True,
                        },
                        'fetchOpenOrders': {
                            'marginMode': False,
                            'limit': 500,
                            'trigger': True,
                            'trailing': False,
                            'symbolRequired': True,
                        },
                        'fetchOrders': None,
                        'fetchCanceledAndClosedOrders': {
                            'marginMode': False,
                            'limit': 500,
                            'daysBack': 182,  # 6 months
                            'untilDays': 7,
                            'trigger': False,
                            'trailing': False,
                            'symbolRequired': False,
                        },
                        'fetchClosedOrders': None,
                        'fetchOHLCV': {
                            'limit': 500,
                        },
                    },
                    'inverse': None,
                },
                'future': {
                    'linear': None,
                    'inverse': None,
                },
            },
            'timeframes': {
                '1m': '1m',
                '3m': '3m',
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '2h': '2h',
                '4h': '4h',
                '6h': '6h',
                '12h': '12h',
                '1d': '1d',
            },
            'precisionMode': TICK_SIZE,
            'exceptions': {
                'exact': {
                    '101001': AuthenticationError,  # {"code":101001,"message":"Apikey doesn't exist!"}
                    '101103': AuthenticationError,  # {"code":101103,"message":"Invalid API-key, IP, or permissions for action."}
                    '102001': BadRequest,  # {"code":102001,"message":"Unsupported transfer type"}
                    '102002': PermissionDenied,  # {"code":102002,"message":"The current account does not support transfer of self currency"}
                    '401': AuthenticationError,  # 401 Unauthorized – Invalid API Key
                    '500': ExchangeError,  # 500 Internal Error
                    '501': ExchangeError,  # 501 System Busy
                    '506': ExchangeError,  # 506 Unknown Request Origin
                    '510': RateLimitExceeded,  # 510 Requests Too Frequent
                    '511': AuthenticationError,  # 511 Access to the Interface is Forbidden
                    '513': BadRequest,  # 513 Invalid Request
                    '514': BadRequest,  # 514 Duplicate Request
                    '600': BadRequest,  # 600 Parameter Error
                    'Position does not exist': BadRequest,  # {"code":100036,"message":"Position does not exist"}
                    'Requires transaction permissions': PermissionDenied,  # {"code":101107,"message":"Requires transaction permissions"}
                    'Service error': ExchangeError,  # {msg: 'Service error', code: '-1'}
                    'transfer failed': InsufficientFunds,  # {"code":500,"message":"transfer failed","success":false}
                },
                'broad': {
                    'is missing': ArgumentsRequired,  # {"code":600,"message":"The parameter 'startTime' is missing"}
                },
            },
            'commonCurrencies': {
            },
            'options': {
                'networks': {
                    'ERC20': 'ETH',  # todo add more networks
                },
                'timeInForce': {
                    'GTC': 'GTC',  # Good Till Cancelled
                    'FOK': 'FOK',  # Fill Or Kill
                    'IOC': 'IOC',  # Immediate Or Cancel
                    'PO': 'POST_ONLY',  # Post Only
                },
                'accountsByType': {
                    'spot': 'SPOT',
                    'swap': 'UMFUTURE',
                    'funding': 'FUNDING',
                    'inverse': 'CMFUTURE',
                },
                'accountsById': {
                    'SPOT': 'spot',
                    'UMFUTURE': 'swap',
                    'FUNDING': 'funding',
                    'CMFUTURE': 'inverse',
                },
            },
        })

    def fetch_markets(self, params={}) -> List[Market]:
        """
        retrieves data on all markets for bydfi

        https://developers.bydfi.com/en/futures/market#fetching-trading-rules-and-pairs

        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        response = self.publicGetV1FapiMarketExchangeInfo(params)
        #
        #     {
        #         "code": "200",
        #         "message": "success",
        #         "data": [
        #             {
        #                 "symbol": "CLANKER-USDT",
        #                 "baseAsset": "CLANKER",
        #                 "marginAsset": "USDT",
        #                 "quoteAsset": "USDT",
        #                 "contractFactor": "0.01",
        #                 "limitMaxQty": "50000",
        #                 "limitMinQty": "1",
        #                 "marketMaxQty": "10000",
        #                 "marketMinQty": "1",
        #                 "pricePrecision": "8",
        #                 "basePrecision": "8",
        #                 "feeRateTaker": "0.0006",
        #                 "feeRateMaker": "0.0002",
        #                 "liqFeeRate": "0.0006",
        #                 "openBuyLimitRateMax": "0.05",
        #                 "openSellLimitRateMax": "100",
        #                 "openBuyLimitRateMin": "0.98",
        #                 "openSellLimitRateMin": "0.05",
        #                 "priceOrderPrecision": "2",
        #                 "baseShowPrecision": "2",
        #                 "maxLeverageLevel": "20",
        #                 "volumePrecision": "2",
        #                 "maxLimitOrderNum": "200",
        #                 "maxPlanOrderNum": "10",
        #                 "reverse": False,
        #                 "onboardTime": "1763373600000",
        #                 "status": "NORMAL"
        #             },
        #             ...
        #         ],
        #         "success": True
        #     }
        data = self.safe_list(response, 'data', [])
        return self.parse_markets(data)

    def parse_market(self, market: dict) -> Market:
        #
        #     {
        #         "symbol": "CLANKER-USDT",
        #         "baseAsset": "CLANKER",
        #         "marginAsset": "USDT",
        #         "quoteAsset": "USDT",
        #         "contractFactor": "0.01",
        #         "limitMaxQty": "50000",
        #         "limitMinQty": "1",
        #         "marketMaxQty": "10000",
        #         "marketMinQty": "1",
        #         "pricePrecision": "8",
        #         "basePrecision": "8",
        #         "feeRateTaker": "0.0006",
        #         "feeRateMaker": "0.0002",
        #         "liqFeeRate": "0.0006",
        #         "openBuyLimitRateMax": "0.05",
        #         "openSellLimitRateMax": "100",
        #         "openBuyLimitRateMin": "0.98",
        #         "openSellLimitRateMin": "0.05",
        #         "priceOrderPrecision": "2",
        #         "baseShowPrecision": "2",
        #         "maxLeverageLevel": "20",
        #         "volumePrecision": "2",
        #         "maxLimitOrderNum": "200",
        #         "maxPlanOrderNum": "10",
        #         "reverse": False,
        #         "onboardTime": "1763373600000",
        #         "status": "NORMAL"
        #     }
        #
        id = self.safe_string(market, 'symbol')
        baseId = self.safe_string(market, 'baseAsset')
        quoteId = self.safe_string(market, 'quoteAsset')
        settleId = self.safe_string(market, 'marginAsset')
        base = self.safe_currency_code(baseId)
        quote = self.safe_currency_code(quoteId)
        settle = self.safe_currency_code(settleId)
        symbol = base + '/' + quote + ':' + settle
        inverse = self.safe_bool(market, 'reverse')
        limitMaxQty = self.safe_string(market, 'limitMaxQty')
        marketMaxQty = self.safe_string(market, 'marketMaxQty')
        maxAmountString = Precise.string_max(limitMaxQty, marketMaxQty)
        marketMinQty = self.safe_string(market, 'marketMinQty')
        limitMinQty = self.safe_string(market, 'limitMinQty')
        minAmountString = Precise.string_min(marketMinQty, limitMinQty)
        contractSize = self.safe_string(market, 'contractFactor')
        pricePrecision = self.parse_precision(self.safe_string(market, 'priceOrderPrecision'))
        rawAmountPrecision = self.parse_precision(self.safe_string(market, 'volumePrecision'))
        amountPrecision = Precise.string_div(rawAmountPrecision, contractSize)
        basePrecision = self.parse_precision(self.safe_string(market, 'basePrecision'))
        taker = self.safe_number(market, 'feeRateTaker')
        maker = self.safe_number(market, 'feeRateMaker')
        maxLeverage = self.safe_number(market, 'maxLeverageLevel')
        status = self.safe_string(market, 'status')
        return self.safe_market_structure({
            'id': id,
            'symbol': symbol,
            'base': base,
            'quote': quote,
            'settle': settle,
            'baseId': baseId,
            'quoteId': quoteId,
            'settleId': settleId,
            'type': 'swap',
            'spot': False,
            'margin': None,
            'swap': True,
            'future': False,
            'option': False,
            'active': status == 'NORMAL',
            'contract': True,
            'linear': not inverse,
            'inverse': inverse,
            'taker': taker,
            'maker': maker,
            'contractSize': self.parse_number(contractSize),
            'expiry': None,
            'expiryDatetime': None,
            'strike': None,
            'optionType': None,
            'precision': {
                'amount': self.parse_number(amountPrecision),
                'price': self.parse_number(pricePrecision),
                'base': self.parse_number(basePrecision),
            },
            'limits': {
                'leverage': {
                    'min': None,
                    'max': maxLeverage,
                },
                'amount': {
                    'min': self.parse_number(minAmountString),
                    'max': self.parse_number(maxAmountString),
                },
                'price': {
                    'min': None,
                    'max': None,
                },
                'cost': {
                    'min': None,
                    'max': None,
                },
            },
            'created': self.parse8601(self.safe_string(market, 'createdAt')),
            'info': market,
        })

    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

        https://developers.bydfi.com/en/futures/market#depth-information

        :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, could be 5, 10, 20, 50, 100, 500 or 1000(default 500)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.loc]: crypto location, default: us
        :returns dict: A dictionary of `order book structures <https://github.com/ccxt/ccxt/wiki/Manual#order-book-structure>` indexed by market symbols
        """
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['limit'] = self.get_closest_limit(limit)
        response = self.publicGetV1FapiMarketDepth(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "data": {
        #             "lastUpdateId": "221780076",
        #             "symbol": "ETH-USDT",
        #             "asks": [
        #                 {
        #                     "price": "2958.21",
        #                     "amount": "39478"
        #                 },
        #                 ...
        #             ],
        #             "bids": [
        #                 {
        #                     "price": "2958.19",
        #                     "amount": "174498"
        #                 },
        #                 ...
        #             ],
        #             "e": "221780076"
        #         },
        #         "success": True
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        timestamp = self.milliseconds()
        orderBook = self.parse_order_book(data, market['symbol'], timestamp, 'bids', 'asks', 'price', 'amount')
        orderBook['nonce'] = self.safe_integer(data, 'lastUpdateId')
        return orderBook

    def get_closest_limit(self, limit: Int) -> Int:
        limits = [5, 10, 20, 50, 100, 500, 1000]
        result = 1000
        for i in range(0, len(limits)):
            if limit <= limits[i]:
                result = limits[i]
                break
        return result

    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

        https://developers.bydfi.com/en/futures/market#recent-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
        :param int [limit]: the maximum amount of trades to fetch(default 500, max 1000)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.fromId]: retrieve from which trade ID to start. Default to retrieve the most recent trade records
        :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/?id=public-trades>`
        """
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['limit'] = limit
        response = self.publicGetV1FapiMarketTrades(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "data": [
        #             {
        #                 "id": "7407825178362667008",
        #                 "symbol": "ETH-USDT",
        #                 "price": "2970.49",
        #                 "quantity": "63",
        #                 "side": "SELL",
        #                 "time": 1766163153218
        #             }
        #         ],
        #         "success": True
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_trades(data, market, since, limit)

    def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        fetch all trades made by the user

        https://developers.bydfi.com/en/futures/trade#historical-trades-query

        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch trades for
        :param int [limit]: the maximum number of trades structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch trades for
        :param str [params.contractType]: FUTURE or DELIVERY, default is FUTURE
        :param str [params.wallet]: The unique code of a sub-wallet
        :param str [params.orderType]: order type('LIMIT', 'MARKET', 'LIQ', 'LIMIT_CLOSE', 'MARKET_CLOSE', 'STOP', 'TAKE_PROFIT', 'STOP_MARKET', 'TAKE_PROFIT_MARKET' or 'TRAILING_STOP_MARKET')
        :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/?id=trade-structure>`
        """
        self.load_markets()
        paginate = self.safe_bool(params, 'paginate', False)
        if paginate:
            maxLimit = 500
            params = self.omit(params, 'paginate')
            params = self.extend(params, {'paginationDirection': 'backward'})
            paginatedResponse = self.fetch_paginated_call_dynamic('fetchMyTrades', symbol, since, limit, params, maxLimit, True)
            return self.sort_by(paginatedResponse, 'timestamp')
        contractType = 'FUTURE'
        contractType, params = self.handle_option_and_params(params, 'fetchMyTrades', 'contractType', contractType)
        request: dict = {
            'contractType': contractType,
        }
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        params = self.handle_since_and_until('fetchMyTrades', since, params)
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetV1FapiTradeHistoryTrade(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "data": [
        #             {
        #                 "orderId": "7408919189505597440",
        #                 "wallet": "W001",
        #                 "symbol": "ETH-USDC",
        #                 "time": "1766423985842",
        #                 "dealPrice": "3032.45",
        #                 "dealVolume": "1",
        #                 "fee": "0",
        #                 "side": "BUY",
        #                 "type": "2",
        #                 "liqPrice": null,
        #                 "basePrecision": "8",
        #                 "baseShowPrecision": "2",
        #                 "tradePnl": "0",
        #                 "marginType": "CROSS",
        #                 "leverageLevel": 1
        #             }
        #         ],
        #         "success": True
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_trades(data, market, since, limit)

    def parse_trade(self, trade: dict, market: Market = None) -> Trade:
        #
        # fetchTrades
        #     {
        #         "id": "7407825178362667008",
        #         "symbol": "ETH-USDT",
        #         "price": "2970.49",
        #         "quantity": "63",
        #         "side": "SELL",
        #         "time": 1766163153218
        #     }
        #
        # fetchMyTrades
        #     {
        #         "orderId": "7408919189505597440",
        #         "wallet": "W001",
        #         "symbol": "ETH-USDC",
        #         "time": "1766423985842",
        #         "dealPrice": "3032.45",
        #         "dealVolume": "1",
        #         "fee": "0",
        #         "side": "BUY",
        #         "type": "2",
        #         "liqPrice": null,
        #         "basePrecision": "8",
        #         "baseShowPrecision": "2",
        #         "tradePnl": "0",
        #         "marginType": "CROSS",
        #         "leverageLevel": 1
        #     }
        #
        marketId = self.safe_string(trade, 'symbol')
        market = self.safe_market(marketId, market)
        timestamp = self.safe_integer(trade, 'time')
        fee = None
        rawType = self.safe_string(trade, 'type')
        feeCost = self.safe_string(trade, 'fee')
        if feeCost is not None:
            fee = {
                'cost': feeCost,
                'currency': market['settle'],
            }
        orderId = self.safe_string(trade, 'orderId')
        side: Str = None  # fetchMyTrades always returns side BUY
        if orderId is None:
            # from fetchTrades
            side = self.safe_string_lower(trade, 'side')
        return self.safe_trade({
            'info': trade,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': market['symbol'],
            'id': self.safe_string(trade, 'id'),
            'order': orderId,
            'type': self.parse_trade_type(rawType),
            'side': side,
            'takerOrMaker': None,
            'price': self.safe_string_2(trade, 'price', 'dealPrice'),
            'amount': self.safe_string_2(trade, 'quantity', 'dealVolume'),
            'cost': None,
            'fee': fee,
        }, market)

    def parse_trade_type(self, type: Str) -> Str:
        types = {
            '1': 'limit',
            '2': 'market',
            '3': 'liquidation',
        }
        return self.safe_string(types, type, type)

    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

        https://developers.bydfi.com/en/futures/market#candlestick-data

        :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
        :param int [limit]: the maximum amount of candles to fetch(max 500)
        :param dict [params]: extra parameters specific to the bitteam api endpoint
        :param int [params.until]: timestamp in ms of the latest candle to fetch
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        self.load_markets()
        maxLimit = 500  # docs says max 1500, but in practice only 500 works
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchOHLCV', 'paginate')
        if paginate:
            return self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, maxLimit)
        market = self.market(symbol)
        interval = self.safe_string(self.timeframes, timeframe, timeframe)
        request = {
            'symbol': market['id'],
            'interval': interval,
        }
        startTime = since
        numberOfCandles = limit if limit else maxLimit
        until = None
        until, params = self.handle_option_and_params(params, 'fetchOHLCV', 'until')
        now = self.milliseconds()
        duration = self.parse_timeframe(timeframe) * 1000
        timeDelta = duration * numberOfCandles
        if startTime is None and until is None:
            startTime = now - timeDelta
            until = now
        elif until is None:
            until = startTime + timeDelta
            if until > now:
                until = now
        elif startTime is None:
            startTime = until - timeDelta
        request['startTime'] = startTime
        request['endTime'] = until
        if limit is not None:
            request['limit'] = limit
        response = self.publicGetV1FapiMarketKlines(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "data": [
        #             {
        #                 "s": "ETH-USDT",
        #                 "t": "1766166000000",
        #                 "c": "2964.990000000000000000",
        #                 "o": "2967.830000000000000000",
        #                 "h": "2967.830000000000000000",
        #                 "l": "2964.130000000000000000",
        #                 "v": "20358.000000000000000000"
        #             }
        #         ],
        #         "success": True
        #     }
        #
        data = self.safe_list(response, 'data', [])
        result = self.parse_ohlcvs(data, market, timeframe, since, limit)
        return result

    def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
        #
        #     {
        #         "s": "ETH-USDT",
        #         "t": "1766166000000",
        #         "c": "2964.990000000000000000",
        #         "o": "2967.830000000000000000",
        #         "h": "2967.830000000000000000",
        #         "l": "2964.130000000000000000",
        #         "v": "20358.000000000000000000"
        #     }
        #
        return [
            self.safe_integer(ohlcv, 't'),
            self.safe_number(ohlcv, 'o'),
            self.safe_number(ohlcv, 'h'),
            self.safe_number(ohlcv, 'l'),
            self.safe_number(ohlcv, 'c'),
            self.safe_number(ohlcv, 'v'),
        ]

    def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
        """

        https://developers.bydfi.com/en/futures/market#24hr-price-change-statistics

        fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
        :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.com/?id=ticker-structure>`
        """
        self.load_markets()
        response = self.publicGetV1FapiMarketTicker24hr(params)
        #
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "data": [
        #             {
        #                 "symbol": "BTC-USDT",
        #                 "open": "86452.9",
        #                 "high": "89371.2",
        #                 "low": "84418.5",
        #                 "last": "87050.3",
        #                 "vol": "12938783",
        #                 "time": 1766169423872
        #             }
        #         ],
        #         "success": True
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_tickers(data, symbols)

    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

        https://developers.bydfi.com/en/futures/market#24hr-price-change-statistics

        :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.com/?id=ticker-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        response = self.publicGetV1FapiMarketTicker24hr(self.extend(request, params))
        data = self.safe_list(response, 'data', [])
        ticker = self.safe_dict(data, 0, {})
        return self.parse_ticker(ticker, market)

    def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
        #
        # fetchTicker/fetchTickers
        #     {
        #         "symbol": "BTC-USDT",
        #         "open": "86452.9",
        #         "high": "89371.2",
        #         "low": "84418.5",
        #         "last": "87050.3",
        #         "vol": "12938783",
        #         "time": 1766169423872
        #     }
        #
        marketId = self.safe_string_2(ticker, 'symbol', 's')
        market = self.safe_market(marketId, market)
        timestamp = self.safe_integer_2(ticker, 'time', 'E')
        last = self.safe_string_2(ticker, 'last', 'c')
        return self.safe_ticker({
            'symbol': self.safe_symbol(marketId, market),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_string_2(ticker, 'high', 'h'),
            'low': self.safe_string_2(ticker, 'low', 'l'),
            'bid': None,
            'bidVolume': None,
            'ask': None,
            'askVolume': None,
            'vwap': None,
            'open': self.safe_string_2(ticker, 'open', 'o'),
            'close': last,
            'last': last,
            'previousClose': None,
            'change': None,
            'percentage': None,
            'average': None,
            'baseVolume': self.safe_string_2(ticker, 'vol', 'v'),
            'quoteVolume': None,
            'markPrice': None,
            'indexPrice': None,
            'info': ticker,
        }, market)

    def fetch_funding_rate(self, symbol: str, params={}) -> FundingRate:
        """
        fetch the current funding rate

        https://developers.bydfi.com/en/futures/market#recent-funding-rate

        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `funding rate structure <https://docs.ccxt.com/?id=funding-rate-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        response = self.publicGetV1FapiMarketFundingRate(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "data": {
        #             "symbol": "BTC-USDT",
        #             "lastFundingRate": "0.0001",
        #             "nextFundingTime": "1766188800000",
        #             "time": "1766170665007"
        #         },
        #         "success": True
        #     }
        #
        data = self.safe_dict(response, 'data')
        return self.parse_funding_rate(data, market)

    def parse_funding_rate(self, contract, market: Market = None) -> FundingRate:
        #
        #     {
        #         "symbol": "BTC-USDT",
        #         "lastFundingRate": "0.0001",
        #         "nextFundingTime": "1766188800000",
        #         "time": "1766170665007"
        #     }
        #
        marketId = self.safe_string(contract, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        timestamp = self.safe_integer(contract, 'time')
        nextFundingTimestamp = self.safe_integer(contract, 'nextFundingTime')
        return {
            'info': contract,
            'symbol': symbol,
            'markPrice': None,
            'indexPrice': None,
            'interestRate': None,
            'estimatedSettlePrice': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'fundingRate': self.safe_number(contract, 'lastFundingRate'),
            'fundingTimestamp': None,
            'fundingDatetime': None,
            'nextFundingRate': None,
            'nextFundingTimestamp': nextFundingTimestamp,
            'nextFundingDatetime': self.iso8601(nextFundingTimestamp),
            'previousFundingRate': None,
            'previousFundingTimestamp': None,
            'previousFundingDatetime': None,
            'interval': None,
        }

    def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[FundingRateHistory]:
        """
        fetches historical funding rate prices

        https://developers.bydfi.com/en/futures/market#historical-funding-rates

        :param str symbol: unified symbol of the market to fetch the funding rate history for
        :param int [since]: timestamp in ms of the earliest funding rate to fetch
        :param int [limit]: the maximum amount of `funding rate structures <https://docs.ccxt.com/?id=funding-rate-history-structure>` to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms of the latest funding rate to fetch
        :returns dict[]: a list of `funding rate structures <https://docs.ccxt.com/?id=funding-rate-history-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['limit'] = limit
        until = None
        until, params = self.handle_option_and_params(params, 'fetchFundingRateHistory', 'until')
        if until is not None:
            request['endTime'] = until
        response = self.publicGetV1FapiMarketFundingRateHistory(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "data": [
        #             {
        #                 "symbol": "ETH-USDT",
        #                 "fundingRate": "0.00000025",
        #                 "fundingTime": "1765584000000",
        #                 "markPrice": "3083.2"
        #             }
        #         ],
        #         "success": True
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_funding_rate_histories(data, market, since, limit)

    def parse_funding_rate_history(self, contract, market: Market = None):
        #
        #     {
        #         "symbol": "ETH-USDT",
        #         "fundingRate": "0.00000025",
        #         "fundingTime": "1765584000000",
        #         "markPrice": "3083.2"
        #     }
        #
        marketId = self.safe_string(contract, 'symbol')
        timestamp = self.safe_integer(contract, 'fundingTime')
        return {
            'info': contract,
            'symbol': self.safe_symbol(marketId, market),
            'fundingRate': self.safe_number(contract, 'fundingRate'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
        }

    def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}) -> Order:
        """
        create a trade order

        https://developers.bydfi.com/en/futures/trade#placing-an-order

        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :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 fullfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.wallet]: The unique code of a sub-wallet. W001 is the default wallet and the main wallet code of the contract
        :param bool [params.hedged]: True for hedged mode, False for one way mode, default is False
        :param str [params.clientOrderId]: Custom order ID, must be unique for open orders
        :param str [params.timeInForce]: 'GTC'(Good Till Cancelled), 'FOK'(Fill Or Kill), 'IOC'(Immediate Or Cancel), 'PO'(Post Only)
        :param bool [params.postOnly]: True or False, whether the order is post-only
        :param bool [params.reduceOnly]: True or False, True or False whether the order is reduce-only
        :param float [params.stopLossPrice]: The price a stop loss order is triggered at
        :param float [params.takeProfitPrice]: The price a take profit order is triggered at
        :param float [params.trailingTriggerPrice]: the price to activate a trailing order, default uses the price argument or market price if price is not provided
        :param float [params.trailingPercent]: the percent to trail away from the current market price
        :param str [params.triggerPriceType]: 'MARK_PRICE' or 'CONTRACT_PRICE', default is 'CONTRACT_PRICE', the price type used to trigger stop orders
        :param bool [params.closePosition]: True or False, whether to close all positions after triggering, only supported in STOP_MARKET and TAKE_PROFIT_MARKET; not used with quantity
        :returns dict: an `order structure <https://docs.ccxt.com/?id=order-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        orderRequest = self.create_order_request(symbol, type, side, amount, price, params)
        wallet = 'W001'
        wallet, params = self.handle_option_and_params(params, 'createOrder', 'wallet', wallet)
        orderRequest = self.extend(orderRequest, {'wallet': wallet})
        response = self.privatePostV1FapiTradePlaceOrder(orderRequest)
        #
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "data": {
        #             "wallet": "W001",
        #             "symbol": "ETH-USDT",
        #             "orderId": "7408875768086683648",
        #             "clientOrderId": "7408875768086683648",
        #             "price": "1000",
        #             "origQty": "10",
        #             "avgPrice": null,
        #             "executedQty": "0",
        #             "orderType": "LIMIT",
        #             "side": "BUY",
        #             "status": "NEW",
        #             "stopPrice": null,
        #             "activatePrice": null,
        #             "timeInForce": null,
        #             "workingType": "CONTRACT_PRICE",
        #             "positionSide": "BOTH",
        #             "priceProtect": False,
        #             "reduceOnly": False,
        #             "closePosition": False,
        #             "createTime": "1766413633367",
        #             "updateTime": "1766413633367"
        #         },
        #         "success": True
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        return self.parse_order(data, market)

    def create_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
            'side': side.upper(),
            # 'positionSide': STRING Position direction, not required in single position mode, default and can only be BOTH; required in dual position mode, and can only choose LONG or SHORT
            # 'type': STRING Order type LIMIT / MARKET / STOP / TAKE_PROFIT / STOP_MARKET / TAKE_PROFIT_MARKET / TRAILING_STOP_MARKET
            # 'reduceOnly': BOOL True, False; defaults to False in non-dual mode; not accepted in dual mode; not supported when using closePosition.
            # 'quantity': DECIMAL Order quantity, not supported with closePosition.
            # 'price': DECIMAL Order price
            # 'clientOrderId': STRING User-defined order number, must not be repeated in pending orders. If blank, the system will assign automatically
            # 'stopPrice': DECIMAL Trigger price, only required for STOP, STOP_MARKET, TAKE_PROFIT, TAKE_PROFIT_MARKET
            # 'closePosition': BOOL True, False; all positions closed after triggering, only supported in STOP_MARKET and TAKE_PROFIT_MARKET; not used with quantity; has a self-closing effect, not used with reduceOnly
            # 'activationPrice': DECIMAL Trailing stop activation price, required for TRAILING_STOP_MARKET, default to current market price upon order(supports different workingType)
            # 'callbackRate': DECIMAL Trailing stop callback rate, can range from [0.1, 5], where 1 represents 1%, only required for TRAILING_STOP_MARKET
            # 'timeInForce': STRING Validity method GTC / FOK / POST_ONLY / IOC / TRAILING_STOP
            # 'workingType': STRING stopPrice trigger type: MARK_PRICE(marking price), CONTRACT_PRICE(latest contract price). Default CONTRACT_PRICE
        }
        stopLossPrice = self.safe_string(params, 'stopLossPrice')
        isStopLossOrder = (stopLossPrice is not None)
        takeProfitPrice = self.safe_string(params, 'takeProfitPrice')
        isTakeProfitOrder = (takeProfitPrice is not None)
        trailingPercent = self.safe_string(params, 'trailingPercent')
        isTailingStopOrder = (trailingPercent is not None)
        stopPrice = None
        if isStopLossOrder or isTakeProfitOrder:
            stopPrice = stopLossPrice if isStopLossOrder else takeProfitPrice
            params = self.omit(params, ['stopLossPrice', 'takeProfitPrice'])
            request['stopPrice'] = self.price_to_precision(symbol, stopPrice)
        elif isTailingStopOrder:
            params = self.omit(params, ['trailingPercent'])
            request['callbackRate'] = trailingPercent
            trailingTriggerPrice = self.number_to_string(price)
            trailingTriggerPrice, params = self.handle_param_string(params, 'trailingTriggerPrice', trailingTriggerPrice)
            if trailingTriggerPrice is not None:
                request['activationPrice'] = self.price_to_precision(symbol, trailingTriggerPrice)
                params = self.omit(params, ['trailingTriggerPrice'])
        type = type.upper()
        isMarketOrder = ((type == 'MARKET') or (type == 'STOP_MARKET') or (type == 'TAKE_PROFIT_MARKET') or (type == 'TRAILING_STOP_MARKET'))
        if isMarketOrder:
            if type == 'MARKET':
                if isStopLossOrder:
                    type = 'STOP_MARKET'
                elif isTakeProfitOrder:
                    type = 'TAKE_PROFIT_MARKET'
                elif isTailingStopOrder:
                    type = 'TRAILING_STOP_MARKET'
        else:
            if price is None:
                raise ArgumentsRequired(self.id + ' createOrder() requires a price argument for a ' + type + ' order')
            request['price'] = self.price_to_precision(symbol, price)
            if isStopLossOrder:
                type = 'STOP'
            elif isTakeProfitOrder:
                type = 'TAKE_PROFIT'
        request['type'] = type
        hedged = False
        hedged, params = self.handle_option_and_params(params, 'createOrder', 'hedged', hedged)
        reduceOnly = self.safe_bool(params, 'reduceOnly', False)
        if hedged:
            params = self.omit(params, 'reduceOnly')
            if side == 'buy':
                request['positionSide'] = 'SHORT' if reduceOnly else 'LONG'
            elif side == 'sell':
                request['positionSide'] = 'LONG' if reduceOnly else 'SHORT'
        closePosition = self.safe_bool(params, 'closePosition', False)
        if not closePosition:
            params = self.omit(params, 'closePosition')
            request['quantity'] = self.amount_to_precision(symbol, amount)
        elif (type != 'STOP_MARKET') and (type != 'TAKE_PROFIT_MARKET'):
            raise NotSupported(self.id + ' createOrder() closePosition is only supported for stopLoss and takeProfit market orders')
        timeInForce = self.handle_time_in_force(params)
        postOnly = False
        postOnly, params = self.handle_post_only(isMarketOrder, timeInForce == 'POST_ONLY', params)
        if postOnly:
            timeInForce = 'POST_ONLY'
        if timeInForce is not None:
            request['timeInForce'] = timeInForce
            params = self.omit(params, 'timeInForce')
        if isStopLossOrder or isTakeProfitOrder or isTailingStopOrder:
            workingType = 'CONTRACT_PRICE'
            workingType, params = self.handle_option_and_params(params, 'createOrder', 'triggerPriceType', workingType)
            request['workingType'] = self.encode_working_type(workingType)
        return self.extend(request, params)

    def encode_working_type(self, workingType: Str) -> Str:
        types = {
            'markPrice': 'MARK_PRICE',
            'mark': 'MARK_PRICE',
            'contractPrice': 'CONTRACT_PRICE',
            'contract': 'CONTRACT_PRICE',
            'last': 'CONTRACT_PRICE',
        }
        return self.safe_string(types, workingType, workingType)

    def create_orders(self, orders: List[OrderRequest], params={}):
        """
        create a list of trade orders

        https://developers.bydfi.com/en/futures/trade#batch-order-placement

        :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 str [params.wallet]: The unique code of a sub-wallet. W001 is the default wallet and the main wallet code of the contract
        :returns dict: an `order structure <https://docs.ccxt.com/?id=order-structure>`
        """
        self.load_markets()
        length = len(orders)
        if length > 5:
            raise BadRequest(self.id + ' createOrders() accepts a maximum of 5 orders')
        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)
        wallet = 'W001'
        wallet, params = self.handle_option_and_params(params, 'createOrder', 'wallet', wallet)
        request: dict = {
            'wallet': wallet,
            'orders': ordersRequests,
        }
        response = self.privatePostV1FapiTradeBatchPlaceOrder(self.extend(request, params))
        data = self.safe_list(response, 'data', [])
        return self.parse_orders(data)

    def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}) -> Order:
        """
        edit a trade order

        https://developers.bydfi.com/en/futures/trade#order-modification

        :param str id: order id(mandatory if params.clientOrderId is not provided)
        :param str [symbol]: unified symbol of the market to create an order in
        :param str [type]: not used by bydfi editOrder
        :param str [side]: 'buy' or 'sell'
        :param float [amount]: how much of the currency you want to trade in units of the base currency
        :param float [price]: the price for the order, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.clientOrderId]: a unique identifier for the order(could be alternative to id)
        :param str [params.wallet]: The unique code of a sub-wallet. W001 is the default wallet and the main wallet code of the contract
        :returns dict: an `order structure <https://docs.ccxt.com/?id=order-structure>`
        """
        self.load_markets()
        request = self.create_edit_order_request(id, symbol, 'limit', side, amount, price, params)
        wallet = 'W001'
        wallet, params = self.handle_option_and_params(params, 'editOrder', 'wallet', wallet)
        request['wallet'] = wallet
        response = self.privatePostV1FapiTradeEditOrder(request)
        data = self.safe_dict(response, 'data', {})
        return self.parse_order(data)

    def edit_orders(self, orders: List[OrderRequest], params={}) -> List[Order]:
        """
        edit a list of trade orders

        https://developers.bydfi.com/en/futures/trade#batch-order-modification

        :param Array orders: list of orders to edit, each object should contain the parameters required by editOrder, namely id, symbol, amount, price and params
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.wallet]: The unique code of a sub-wallet. W001 is the default wallet and the main wallet code of the contract
        :returns dict: an `order structure <https://docs.ccxt.com/?id=order-structure>`
        """
        self.load_markets()
        length = len(orders)
        if length > 5:
            raise BadRequest(self.id + ' editOrders() accepts a maximum of 5 orders')
        ordersRequests = []
        for i in range(0, len(orders)):
            rawOrder = orders[i]
            id = self.safe_string(rawOrder, 'id')
            symbol = self.safe_string(rawOrder, 'symbol')
            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_edit_order_request(id, symbol, 'limit', side, amount, price, orderParams)
            ordersRequests.append(orderRequest)
        wallet = 'W001'
        wallet, params = self.handle_option_and_params(params, 'editOrder', 'wallet', wallet)
        request: dict = {
            'wallet': wallet,
            'editOrders': ordersRequests,
        }
        response = self.privatePostV1FapiTradeBatchEditOrder(self.extend(request, params))
        data = self.safe_list(response, 'data', [])
        return self.parse_orders(data)

    def create_edit_order_request(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
        clientOrderId = self.safe_string(params, 'clientOrderId')
        request: dict = {}
        if (id is None) and (clientOrderId is None):
            raise ArgumentsRequired(self.id + ' editOrder() requires an id argument or a clientOrderId parameter')
        elif id is not None:
            request['orderId'] = id
        market = self.market(symbol)
        request['symbol'] = market['id']
        if side is not None:
            request['side'] = side.upper()
        if amount is not None:
            request['quantity'] = self.amount_to_precision(symbol, amount)
        if price is not None:
            request['price'] = self.price_to_precision(symbol, price)
        return self.extend(request, params)

    def cancel_all_orders(self, symbol: Str = None, params={}) -> List[Order]:
        """
        cancel all open orders in a market

        https://developers.bydfi.com/en/futures/trade#complete-order-cancellation

        :param str symbol: unified market symbol of the market to cancel orders in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.wallet]: The unique code of a sub-wallet. W001 is the default wallet and the main wallet code of the contract
        :returns dict[]: a list of `order structures <https://docs.ccxt.com/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelAllOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        wallet = 'W001'
        wallet, params = self.handle_option_and_params(params, 'cancelAllOrders', 'wallet', wallet)
        request: dict = {
            'symbol': market['id'],
            'wallet': wallet,
        }
        response = self.privatePostV1FapiTradeCancelAllOrder(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "data": [
        #             {
        #                 "wallet": "W001",
        #                 "symbol": "ETH-USDT",
        #                 "orderId": "7408875768086683648",
        #                 "clientOrderId": "7408875768086683648",
        #                 "price": "1000",
        #                 "origQty": "10",
        #                 "avgPrice": "0",
        #                 "executedQty": "0",
        #                 "orderType": "LIMIT",
        #                 "side": "BUY",
        #                 "status": "CANCELED",
        #                 "stopPrice": null,
        #                 "activatePrice": null,
        #                 "timeInForce": null,
        #                 "workingType": "CONTRACT_PRICE",
        #                 "positionSide": "BOTH",
        #                 "priceProtect": False,
        #                 "reduceOnly": False,
        #                 "closePosition": False,
        #                 "createTime": "1766413633367",
        #                 "updateTime": "1766413633370"
        #             }
        #         ],
        #         "success": True
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_orders(data, market)

    def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetch all unfilled currently open orders

        https://developers.bydfi.com/en/futures/trade#pending-order-query
        https://developers.bydfi.com/en/futures/trade#planned-order-query

        :param str symbol: unified market symbol of the market orders were made in
        :param int [since]: the earliest time in ms to fetch orders for
        :param int [limit]: the maximum number of order structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param bool [params.trigger]: True or False, whether to fetch conditional orders only
        :param str [params.wallet]: The unique code of a sub-wallet. W001 is the default wallet and the main wallet code of the contract
        :returns Order[]: a list of `order structures <https://docs.ccxt.com/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOpenOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        wallet = 'W001'
        wallet, params = self.handle_option_and_params(params, 'fetchOpenOrders', 'wallet', wallet)
        request: dict = {
            'symbol': market['id'],
            'wallet': wallet,
        }
        response = None
        trigger = False
        trigger, params = self.handle_option_and_params(params, 'fetchOpenOrders', 'trigger', trigger)
        if not trigger:
            #
            #     {
            #         "code": 200,
            #         "message": "success",
            #         "data": [
            #             {
            #                 "wallet": "W001",
            #                 "symbol": "ETH-USDC",
            #                 "orderId": "7408896083240091648",
            #                 "clientOrderId": "7408896083240091648",
            #                 "price": "999",
            #                 "origQty": "1",
            #                 "avgPrice": "0",
            #                 "executedQty": "0",
            #                 "orderType": "LIMIT",
            #                 "side": "BUY",
            #                 "status": "NEW",
            #                 "stopPrice": null,
            #                 "activatePrice": null,
            #                 "timeInForce": null,
            #                 "workingType": "CONTRACT_PRICE",
            #                 "positionSide": "BOTH",
            #                 "priceProtect": False,
            #                 "reduceOnly": False,
            #                 "closePosition": False,
            #                 "createTime": "1766418476877",
            #                 "updateTime": "1766418476880"
            #             }
            #         ],
            #         "success": True
            #     }
            #
            response = self.privateGetV1FapiTradeOpenOrder(self.extend(request, params))
        else:
            response = self.privateGetV1FapiTradePlanOrder(self.extend(request, params))
        data = self.safe_list(response, 'data', [])
        return self.parse_orders(data, market, since, limit)

    def fetch_open_order(self, id: str, symbol: Str = None, params={}):
        """
        fetch an open order by the id

        https://developers.bydfi.com/en/futures/trade#pending-order-query
        https://developers.bydfi.com/en/futures/trade#planned-order-query

        :param str id: order id(mandatory if params.clientOrderId is not provided)
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param bool [params.trigger]: True or False, whether to fetch conditional orders only
        :param str [params.clientOrderId]: a unique identifier for the order(could be alternative to id)
        :param str [params.wallet]: The unique code of a sub-wallet. W001 is the default wallet and the main wallet code of the contract
        :returns dict: an `order structure <https://docs.ccxt.com/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOpenOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        clientOrderId = self.safe_string(params, 'clientOrderId')
        if (id is None) and (clientOrderId is None):
            raise ArgumentsRequired(self.id + ' fetchOpenOrder() requires an id argument or a clientOrderId parameter')
        elif id is not None:
            request['orderId'] = id
        wallet = 'W001'
        wallet, params = self.handle_option_and_params(params, 'fetchOpenOrder', 'wallet', wallet)
        request['wallet'] = wallet
        response = None
        trigger = False
        trigger, params = self.handle_option_and_params(params, 'fetchOpenOrder', 'trigger', trigger)
        if not trigger:
            response = self.privateGetV1FapiTradeOpenOrder(self.extend(request, params))
        else:
            response = self.privateGetV1FapiTradePlanOrder(self.extend(request, params))
        data = self.safe_list(response, 'data', [])
        order = self.safe_dict(data, 0, {})
        return self.parse_order(order, market)

    def fetch_canceled_and_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetches information on multiple canceled and closed orders made by the user

        https://developers.bydfi.com/en/futures/trade#historical-orders-query

        :param str symbol: unified market symbol of the closed orders
        :param int [since]: timestamp in ms of the earliest order
        :param int [limit]: the max number of closed orders to return
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms of the latest order
        :param str [params.contractType]: FUTURE or DELIVERY, default is FUTURE
        :param str [params.wallet]: The unique code of a sub-wallet
        :param str [params.orderType]: order type('LIMIT', 'MARKET', 'LIQ', 'LIMIT_CLOSE', 'MARKET_CLOSE', 'STOP', 'TAKE_PROFIT', 'STOP_MARKET', 'TAKE_PROFIT_MARKET' or 'TRAILING_STOP_MARKET')
        :returns dict[]: a list of `order structures <https://docs.ccxt.com/?id=order-structure>`
        """
        self.load_markets()
        paginate = self.safe_bool(params, 'paginate', False)
        if paginate:
            maxLimit = 500
            params = self.omit(params, 'paginate')
            params = self.extend(params, {'paginationDirection': 'backward'})
            paginatedResponse = self.fetch_paginated_call_dynamic('fetchCanceledAndClosedOrders', symbol, since, limit, params, maxLimit, True)
            return self.sort_by(paginatedResponse, 'timestamp')
        contractType = 'FUTURE'
        contractType, params = self.handle_option_and_params(params, 'fetchCanceledAndClosedOrders', 'contractType', contractType)
        request: dict = {
            'contractType': contractType,
        }
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        params = self.handle_since_and_until('fetchCanceledAndClosedOrders', since, params)
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetV1FapiTradeHistoryOrder(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "data": [
        #             {
        #                 "orderId": "7408919189505597440",
        #                 "orderType": "MARKET",
        #                 "symbol": "ETH-USDC",
        #                 "origQty": "1",
        #                 "side": "BUY",
        #                 "positionSide": "BOTH",
        #                 "positionAvgPrice": null,
        #                 "positionVolume": null,
        #                 "positionType": null,
        #                 "reduceOnly": False,
        #                 "closePosition": False,
        #                 "action": null,
        #                 "price": "3032.45",
        #                 "avgPrice": "3032.45",
        #                 "brkPrice": null,
        #                 "dealVolume": null,
        #                 "status": "2",
        #                 "wallet": "W001",
        #                 "alias": null,
        #                 "contractId": null,
        #                 "mtime": "1766423985842",
        #                 "ctime": "1766423985840",
        #                 "fixedPrice": null,
        #                 "direction": null,
        #                 "triggerPrice": null,
        #                 "priceType": null,
        #                 "basePrecision": "8",
        #                 "baseShowPrecision": "2",
        #                 "strategyType": null,
        #                 "leverageLevel": 1,
        #                 "marginType": "CROSS",
        #                 "remark": null,
        #                 "callbackRate": null,
        #                 "activationPrice": null
        #             }
        #         ],
        #         "success": True
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_orders(data, market, since, limit)

    def handle_since_and_until(self, methodName: str, since: Int = None, params={}) -> dict:
        until = None
        until, params = self.handle_option_and_params_2(params, methodName, 'until', 'endTime')
        now = self.milliseconds()
        sevenDays = 7 * 24 * 60 * 60 * 1000  # the maximum range is 7 days
        startTime = since
        if startTime is None:
            if until is None:
                # both since and until are None
                startTime = now - sevenDays
                until = now
            else:
                # since is None but until is defined
                startTime = until - sevenDays
        elif until is None:
            # until is None but since is defined
            delta = now - startTime
            if delta > sevenDays:
                until = startTime + sevenDays
            else:
                until = now
        request: dict = {
            'startTime': startTime,
            'endTime': until,
        }
        return self.extend(request, params)

    def parse_order(self, order: dict, market: Market = None) -> Order:
        #
        # createOrder, fetchOpenOrders, fetchOpenOrder
        #     {
        #         "wallet": "W001",
        #         "symbol": "ETH-USDT",
        #         "orderId": "7408875768086683648",
        #         "clientOrderId": "7408875768086683648",
        #         "price": "1000",
        #         "origQty": "10",
        #         "avgPrice": "0",
        #         "executedQty": "0",
        #         "orderType": "LIMIT",
        #         "side": "BUY",
        #         "status": "CANCELED",
        #         "stopPrice": null,
        #         "activatePrice": null,
        #         "timeInForce": null,
        #         "workingType": "CONTRACT_PRICE",
        #         "positionSide": "BOTH",
        #         "priceProtect": False,
        #         "reduceOnly": False,
        #         "closePosition": False,
        #         "createTime": "1766413633367",
        #         "updateTime": "1766413633370"
        #     }
        #
        # fetchCanceledAndClosedOrders
        #     {
        #         "orderId": "7408919189505597440",
        #         "orderType": "MARKET",
        #         "symbol": "ETH-USDC",
        #         "origQty": "1",
        #         "side": "BUY",
        #         "positionSide": "BOTH",
        #         "positionAvgPrice": null,
        #         "positionVolume": null,
        #         "positionType": null,
        #         "reduceOnly": False,
        #         "closePosition": False,
        #         "action": null,
        #         "price": "3032.45",
        #         "avgPrice": "3032.45",
        #         "brkPrice": null,
        #         "dealVolume": null,
        #         "status": "2",
        #         "wallet": "W001",
        #         "alias": null,
        #         "contractId": null,
        #         "mtime": "1766423985842",
        #         "ctime": "1766423985840",
        #         "fixedPrice": null,
        #         "direction": null,
        #         "triggerPrice": null,
        #         "priceType": null,
        #         "basePrecision": "8",
        #         "baseShowPrecision": "2",
        #         "strategyType": null,
        #         "leverageLevel": 1,
        #         "marginType": "CROSS",
        #         "remark": null,
        #         "callbackRate": null,
        #         "activationPrice": null
        #     }
        #
        marketId = self.safe_string(order, 'symbol')
        market = self.safe_market(marketId, market)
        timestamp = self.safe_integer_2(order, 'createTime', 'ctime')
        rawType = self.safe_string(order, 'orderType')
        stopPrice = self.safe_string_n(order, ['stopPrice', 'activatePrice', 'triggerPrice'])
        isStopLossOrder = (rawType == 'STOP') or (rawType == 'STOP_MARKET') or (rawType == 'TRAILING_STOP_MARKET')
        isTakeProfitOrder = (rawType == 'TAKE_PROFIT') or (rawType == 'TAKE_PROFIT_MARKET')
        rawTimeInForce = self.safe_string(order, 'timeInForce')
        timeInForce = self.parse_order_time_in_force(rawTimeInForce)
        postOnly = None
        if timeInForce == 'PO':
            postOnly = True
        rawStatus = self.safe_string(order, 'status')
        fee = {}
        quoteFee = self.safe_number(order, 'quoteFee')
        if quoteFee is not None:
            fee['cost'] = quoteFee
            fee['currency'] = market['quote']
        return self.safe_order({
            'info': order,
            'id': self.safe_string(order, 'orderId'),
            'clientOrderId': self.safe_string(order, 'clientOrderId'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'lastUpdateTimestamp': self.safe_integer_2(order, 'updateTime', 'mtime'),
            'status': self.parse_order_status(rawStatus),
            'symbol': market['symbol'],
            'type': self.parse_order_type(rawType),
            'timeInForce': timeInForce,
            'postOnly': postOnly,
            'reduceOnly': self.safe_bool(order, 'reduceOnly'),
            'side': self.safe_string_lower(order, 'side'),
            'price': self.safe_string(order, 'price'),
            'triggerPrice': stopPrice,
            'stopLossPrice': stopPrice if isStopLossOrder else None,
            'takeProfitPrice': stopPrice if isTakeProfitOrder else None,
            'amount': self.safe_string(order, 'origQty'),
            'filled': self.safe_string(order, 'executedQty'),
            'remaining': None,
            'cost': None,
            'trades': None,
            'fee': fee,
            'average': self.omit_zero(self.safe_string(order, 'avgPrice')),
        }, market)

    def parse_order_type(self, type: Str) -> Str:
        types = {
            'LIMIT': 'limit',
            'MARKET': 'market',
            'STOP': 'limit',
            'STOP_MARKET': 'market',
            'TAKE_PROFIT': 'limit',
            'TAKE_PROFIT_MARKET': 'market',
            'TRAILING_STOP_MARKET': 'market',
        }
        return self.safe_string(types, type, type)

    def parse_order_time_in_force(self, timeInForce: Str) -> Str:
        timeInForces = {
            'GTC': 'GTC',
            'FOK': 'FOK',
            'IOC': 'IOC',
            'POST_ONLY': 'PO',
            'TRAILING_STOP': 'IOC',
        }
        return self.safe_string(timeInForces, timeInForce, timeInForce)

    def parse_order_status(self, status: Str) -> Str:
        statuses = {
            'NEW': 'open',
            'PARTIALLY_FILLED': 'open',
            'FILLED': 'closed',
            'EXPIRED': 'canceled',
            'PART_FILLED_CANCELLED': 'canceled',
            'CANCELED': 'canceled',
            '2': 'closed',
            '4': 'canceled',
        }
        return self.safe_string(statuses, status, status)

    def set_leverage(self, leverage: int, symbol: Str = None, params={}):
        """
        set the level of leverage for a market

        https://developers.bydfi.com/en/futures/trade#set-leverage-for-single-trading-pair

        :param float leverage: the rate of leverage
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.wallet]: The unique code of a sub-wallet. W001 is the default wallet and the main wallet code of the contract
        :returns dict: response from the exchange
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        wallet = 'W001'
        wallet, params = self.handle_option_and_params(params, 'setLeverage', 'wallet', wallet)
        request: dict = {
            'symbol': market['id'],
            'leverage': leverage,
            'wallet': wallet,
        }
        response = self.privatePostV1FapiTradeLeverage(self.extend(request, params))
        data = self.safe_dict(response, 'data', {})
        return data

    def fetch_leverage(self, symbol: str, params={}) -> Leverage:
        """
        fetch the set leverage for a market

        https://developers.bydfi.com/en/futures/trade#get-leverage-for-single-trading-pair

        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.wallet]: The unique code of a sub-wallet. W001 is the default wallet and the main wallet code of the contract
        :returns dict: a `leverage structure <https://docs.ccxt.com/?id=leverage-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchLeverage() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        wallet = 'W001'
        wallet, params = self.handle_option_and_params(params, 'fetchLeverage', 'wallet', wallet)
        request: dict = {
            'symbol': market['id'],
            'wallet': wallet,
        }
        response = self.privateGetV1FapiTradeLeverage(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "data": {
        #             "symbol": "ETH-USDC",
        #             "leverage": 1,
        #             "maxNotionalValue": "100000000"
        #         },
        #         "success": True
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        return self.parse_leverage(data, market)

    def parse_leverage(self, leverage: dict, market: Market = None) -> Leverage:
        marketId = self.safe_string(leverage, 'symbol')
        return {
            'info': leverage,
            'symbol': self.safe_symbol(marketId, market),
            'marginMode': None,
            'longLeverage': self.safe_integer(leverage, 'leverage'),
            'shortLeverage': self.safe_integer(leverage, 'leverage'),
        }

    def fetch_positions(self, symbols: Strings = None, params={}) -> List[Position]:
        """
        fetch all open positions

        https://developers.bydfi.com/en/futures/trade#positions-query

        :param str[] [symbols]: list of unified market symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.contractType]: FUTURE or DELIVERY, default is FUTURE
        :param str [params.settleCoin]: the settlement currency(USDT or USDC or USD)
        :returns dict[]: a list of `position structure <https://docs.ccxt.com/?id=position-structure>`
        """
        self.load_markets()
        contractType = 'FUTURE'
        contractType, params = self.handle_option_and_params(params, 'fetchPositions', 'contractType', contractType)
        request: dict = {
            'contractType': contractType,
        }
        response = self.privateGetV1FapiTradePositions(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "data": [
        #             {
        #                 "symbol": "ETH-USDC",
        #                 "side": "BUY",
        #                 "volume": "0.001",
        #                 "avgPrice": "3032.45",
        #                 "liqPrice": "0",
        #                 "markPrice": "3032.37",
        #                 "unPnl": "-0.00008",
        #                 "positionMargin": "0",
        #                 "settleCoin": "USDC",
        #                 "im": "3.03245",
        #                 "mm": "0.007581125"
        #             }
        #         ],
        #         "success": True
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_positions(data, symbols)

    def fetch_positions_for_symbol(self, symbol: str, params={}) -> List[Position]:
        """
        fetch open positions for a single market

        https://developers.bydfi.com/en/futures/trade#positions-query

        fetch all open positions for specific symbol
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.contractType]: FUTURE or DELIVERY, default is FUTURE
        :returns dict[]: a list of `position structure <https://docs.ccxt.com/?id=position-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        contractType = 'FUTURE'
        contractType, params = self.handle_option_and_params(params, 'fetchPositions', 'contractType', contractType)
        request: dict = {
            'contractType': contractType,
            'symbol': market['id'],
        }
        response = self.privateGetV1FapiTradePositions(self.extend(request, params))
        data = self.safe_list(response, 'data', [])
        return self.parse_positions(data, [market['symbol']])

    def parse_position(self, position: dict, market: Market = None):
        #
        # fetchPositions, fetchPositionsForSymbol
        #     {
        #         "symbol": "ETH-USDC",
        #         "side": "BUY",
        #         "volume": "0.001",
        #         "avgPrice": "3032.45",
        #         "liqPrice": "0",
        #         "markPrice": "3032.37",
        #         "unPnl": "-0.00008",
        #         "positionMargin": "0",
        #         "settleCoin": "USDC",
        #         "im": "3.03245",
        #         "mm": "0.007581125"
        #     }
        #
        # fetchPositionsHistory
        #     {
        #         "id": "16788366",
        #         "wallet": "W001",
        #         "currency": "USDC",
        #         "symbol": "ETH-USDC",
        #         "side": "BUY",
        #         "positionSide": "BOTH",
        #         "leverage": 1,
        #         "avgOpenPositionPrice": "3032.45",
        #         "openPositionVolume": "1",
        #         "openCount": 1,
        #         "highPrice": "3032.45",
        #         "lowPrice": "2953.67",
        #         "avgClosePositionPrice": "2953.67",
        #         "closePositionVolume": "1",
        #         "closePositionCost": "2.95367",
        #         "closeCount": 1,
        #         "positionProfits": "-0.07878",
        #         "lossBonus": "0",
        #         "capitalFeeTotal": "-0.00026361",
        #         "capitalFeeOutCash": "-0.00026361",
        #         "capitalFeeInCash": "0",
        #         "capitalFeeBonus": "0",
        #         "openFeeTotal": "-0.00181947",
        #         "openFeeBonus": "0",
        #         "closeFeeTotal": "-0.00177221",
        #         "closeFeeBonus": "0",
        #         "liqLoss": "0",
        #         "liqClosed": False,
        #         "sequence": "53685341336",
        #         "updateTime": "1766494929423",
        #         "createTime": "1766423985842"
        #     }
        #
        marketId = self.safe_string(position, 'symbol')
        market = self.safe_market(marketId, market)
        buyOrSell = self.safe_string(position, 'side')
        rawPositionSide = self.safe_string_lower(position, 'positionSide')
        positionSide = self.parse_position_side(buyOrSell)
        hedged = None
        isFetchPositionsHistory = False
        if rawPositionSide is not None:
            isFetchPositionsHistory = True
            if rawPositionSide != 'both':
                positionSide = rawPositionSide
                hedged = True
            else:
                hedged = False
        contractSize = self.safe_string(market, 'contractSize')
        contracts = self.safe_string_2(position, 'volume', 'openPositionVolume')
        if not isFetchPositionsHistory:
            # in fetchPositions, the 'volume' is in base currency units, need to convert to contracts
            contracts = Precise.string_div(contracts, contractSize)
        timestamp = self.safe_integer(position, 'createTime')
        return self.safe_position({
            'info': position,
            'id': self.safe_string(position, 'id'),
            'symbol': market['symbol'],
            'entryPrice': self.parse_number(self.safe_string_2(position, 'avgOpenPositionPrice', 'avgPrice')),
            'markPrice': self.parse_number(self.safe_string(position, 'markPrice')),
            'lastPrice': self.parse_number(self.safe_string(position, 'avgClosePositionPrice')),
            'notional': self.parse_number(self.safe_string(position, 'closePositionCost')),
            'collateral': None,
            'unrealizedPnl': self.parse_number(self.safe_string(position, 'unPnl')),
            'realizedPnl': self.parse_number(self.safe_string(position, 'positionProfits')),
            'side': positionSide,
            'contracts': self.parse_number(contracts),
            'contractSize': self.parse_number(contractSize),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastUpdateTimestamp': self.safe_integer(position, 'updateTime'),
            'hedged': hedged,
            'maintenanceMargin': self.parse_number(self.safe_string(position, 'mm')),
            'maintenanceMarginPercentage': None,
            'initialMargin': self.parse_number(self.safe_string(position, 'im')),
            'initialMarginPercentage': None,
            'leverage': self.parse_number(self.safe_string(position, 'leverage')),
            'liquidationPrice': self.parse_number(self.safe_string(position, 'liqPrice')),
            'marginRatio': None,
            'marginMode': None,
            'percentage': None,
        })

    def parse_position_side(self, side: Str) -> Str:
        sides = {
            'BUY': 'long',
            'SELL': 'short',
        }
        return self.safe_string(sides, side, side)

    def fetch_position_history(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Position]:
        """
        fetches historical positions

        https://developers.bydfi.com/en/futures/trade#query-historical-position-profit-and-loss-records

        :param str symbol: a unified market symbol
        :param int [since]: timestamp in ms of the earliest position to fetch , params["until"] - since <= 7 days
        :param int [limit]: the maximum amount of records to fetch(default 500, max 500)
        :param dict params: extra parameters specific to the exchange api endpoint
        :param int [params.until]: timestamp in ms of the latest position to fetch , params["until"] - since <= 7 days
        :param str [params.contractType]: FUTURE or DELIVERY, default is FUTURE
        :param str [params.wallet]: The unique code of a sub-wallet. W001 is the default wallet and the main wallet code of the contract
        :returns dict[]: a list of `position structures <https://docs.ccxt.com/?id=position-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        contractType = 'FUTURE'
        contractType, params = self.handle_option_and_params(params, 'fetchPositionsHistory', 'contractType', contractType)
        request: dict = {
            'symbol': market['id'],
            'contractType': contractType,
        }
        params = self.handle_since_and_until('fetchPositionsHistory', since, params)
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetV1FapiTradePositionHistory(self.extend(request, params))
        #
        #
        data = self.safe_list(response, 'data', [])
        positions = self.parse_positions(data)
        return self.filter_by_since_limit(positions, since, limit)

    def fetch_positions_history(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]:
        """
        fetches historical positions

        https://developers.bydfi.com/en/futures/trade#query-historical-position-profit-and-loss-records

        :param str[] symbols: a list of unified market symbols
        :param int [since]: timestamp in ms of the earliest position to fetch , params["until"] - since <= 7 days
        :param int [limit]: the maximum amount of records to fetch(default 500, max 500)
        :param dict params: extra parameters specific to the exchange api endpoint
        :param int [params.until]: timestamp in ms of the latest position to fetch , params["until"] - since <= 7 days
        :param str [params.contractType]: FUTURE or DELIVERY, default is FUTURE
        :param str [params.wallet]: The unique code of a sub-wallet. W001 is the default wallet and the main wallet code of the contract
        :returns dict[]: a list of `position structures <https://docs.ccxt.com/?id=position-structure>`
        """
        self.load_markets()
        contractType = 'FUTURE'
        contractType, params = self.handle_option_and_params(params, 'fetchPositionsHistory', 'contractType', contractType)
        request: dict = {
            'contractType': contractType,
        }
        params = self.handle_since_and_until('fetchPositionsHistory', since, params)
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetV1FapiTradePositionHistory(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "data": [
        #             {
        #                 "id": "16788366",
        #                 "wallet": "W001",
        #                 "currency": "USDC",
        #                 "symbol": "ETH-USDC",
        #                 "side": "BUY",
        #                 "positionSide": "BOTH",
        #                 "leverage": 1,
        #                 "avgOpenPositionPrice": "3032.45",
        #                 "openPositionVolume": "1",
        #                 "openCount": 1,
        #                 "highPrice": "3032.45",
        #                 "lowPrice": "2953.67",
        #                 "avgClosePositionPrice": "2953.67",
        #                 "closePositionVolume": "1",
        #                 "closePositionCost": "2.95367",
        #                 "closeCount": 1,
        #                 "positionProfits": "-0.07878",
        #                 "lossBonus": "0",
        #                 "capitalFeeTotal": "-0.00026361",
        #                 "capitalFeeOutCash": "-0.00026361",
        #                 "capitalFeeInCash": "0",
        #                 "capitalFeeBonus": "0",
        #                 "openFeeTotal": "-0.00181947",
        #                 "openFeeBonus": "0",
        #                 "closeFeeTotal": "-0.00177221",
        #                 "closeFeeBonus": "0",
        #                 "liqLoss": "0",
        #                 "liqClosed": False,
        #                 "sequence": "53685341336",
        #                 "updateTime": "1766494929423",
        #                 "createTime": "1766423985842"
        #             }
        #         ],
        #         "success": True
        #     }
        #
        data = self.safe_list(response, 'data', [])
        positions = self.parse_positions(data, symbols)
        return self.filter_by_since_limit(positions, since, limit)

    def fetch_margin_mode(self, symbol: str, params={}) -> MarginMode:
        """
        fetches the margin mode of a trading pair

        https://developers.bydfi.com/en/futures/user#margin-mode-query

        :param str symbol: unified symbol of the market to fetch the margin mode for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.contractType]: FUTURE or DELIVERY, default is FUTURE
        :param str [params.wallet]: The unique code of a sub-wallet. W001 is the default wallet and the main wallet code of the contract
        :returns dict: a `margin mode structure <https://docs.ccxt.com/?id=margin-mode-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        contractType = 'FUTURE'
        contractType, params = self.handle_option_and_params(params, 'fetchMarginMode', 'contractType', contractType)
        wallet = 'W001'
        wallet, params = self.handle_option_and_params(params, 'fetchMarginMode', 'wallet', wallet)
        request: dict = {
            'contractType': contractType,
            'symbol': market['id'],
            'wallet': wallet,
        }
        response = self.privateGetV1FapiUserDataAssetsMargin(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "data": {
        #             "wallet": "W001",
        #             "symbol": "ETH-USDC",
        #             "marginType": "CROSS"
        #         },
        #         "success": True
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        return self.parse_margin_mode(data, market)

    def parse_margin_mode(self, marginMode: dict, market: Market = None) -> MarginMode:
        marketId = self.safe_string(marginMode, 'symbol')
        return {
            'info': marginMode,
            'symbol': self.safe_symbol(marketId, market),
            'marginMode': self.safe_string_lower(marginMode, 'marginType'),
        }

    def set_margin_mode(self, marginMode: str, symbol: Str = None, params={}):
        """
        set margin mode to 'cross' or 'isolated'

        https://developers.bydfi.com/en/futures/user#change-margin-type-cross-margin

        :param str marginMode: 'cross' or 'isolated'
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.contractType]: FUTURE or DELIVERY, default is FUTURE
        :param str [params.wallet]: The unique code of a sub-wallet. W001 is the default wallet and the main wallet code of the contract
        :returns dict: response from the exchange
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setMarginMode() requires a symbol argument')
        marginMode = marginMode.lower()
        if marginMode != 'isolated' and marginMode != 'cross':
            raise BadRequest(self.id + ' setMarginMode() marginMode argument should be isolated or cross')
        self.load_markets()
        market = self.market(symbol)
        contractType = 'FUTURE'
        contractType, params = self.handle_option_and_params(params, 'fetchMarginMode', 'contractType', contractType)
        wallet = 'W001'
        wallet, params = self.handle_option_and_params(params, 'fetchMarginMode', 'wallet', wallet)
        request: dict = {
            'contractType': contractType,
            'symbol': market['id'],
            'marginType': marginMode.upper(),
            'wallet': wallet,
        }
        return self.privatePostV1FapiUserDataMarginType(self.extend(request, params))

    def set_position_mode(self, hedged: bool, symbol: Str = None, params={}):
        """
        set hedged to True or False for a market, hedged for bydfi is set identically for all markets with same settle currency

        https://developers.bydfi.com/en/futures/user#change-position-mode-dual

        :param bool hedged: set to True to use dualSidePosition
        :param str [symbol]: not used by bydfi setPositionMode()
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.contractType]: FUTURE or DELIVERY, default is FUTURE
        :param str [params.wallet]: The unique code of a sub-wallet. W001 is the default wallet and the main wallet code of the contract
        :param str [params.settleCoin]: The settlement currency - USDT or USDC or USD(default is USDT)
        :returns dict: response from the exchange
        """
        if symbol is not None:
            raise NotSupported(self.id + ' setPositionMode() does not support a symbol argument. The position mode is set identically for all markets with same settle currency')
        self.load_markets()
        positionType = 'HEDGE' if hedged else 'ONEWAY'
        wallet = 'W001'
        wallet, params = self.handle_option_and_params(params, 'setPositionMode', 'wallet', wallet)
        contractType = 'FUTURE'
        contractType, params = self.handle_option_and_params(params, 'setPositionMode', 'contractType', contractType)
        settleCoin = 'USDT'
        settleCoin, params = self.handle_option_and_params(params, 'setPositionMode', 'settleCoin', settleCoin)
        request: dict = {
            'contractType': contractType,
            'wallet': wallet,
            'positionType': positionType,
            'settleCoin': settleCoin,
        }
        #
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "success": True
        #     }
        #
        return self.privatePostV1FapiUserDataPositionSideDual(self.extend(request, params))

    def fetch_position_mode(self, symbol: Str = None, params={}):
        """
        fetchs the position mode, hedged or one way, hedged for bydfi is set identically for all markets with same settle currency

        https://developers.bydfi.com/en/futures/user#get-position-mode

        :param str [symbol]: unified symbol of the market to fetch the order book for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.contractType]: FUTURE or DELIVERY, default is FUTURE
        :param str [params.wallet]: The unique code of a sub-wallet. W001 is the default wallet and the main wallet code of the contract
        :param str [params.settleCoin]: The settlement currency - USDT or USDC or USD(default is USDT or settle currency of the market if market is provided)
        :returns dict: an object detailing whether the market is in hedged or one-way mode
        """
        self.load_markets()
        wallet = 'W001'
        wallet, params = self.handle_option_and_params(params, 'fetchPositionMode', 'wallet', wallet)
        contractType = 'FUTURE'
        contractType, params = self.handle_option_and_params(params, 'fetchPositionMode', 'contractType', contractType)
        settleCoin = 'USDT'
        if symbol is None:
            settleCoin, params = self.handle_option_and_params(params, 'fetchPositionMode', 'settleCoin', settleCoin)
        else:
            market = self.market(symbol)
            settleCoin = market['settleId']
        request: dict = {
            'contractType': contractType,
            'settleCoin': settleCoin,
            'wallet': wallet,
        }
        response = self.privateGetV1FapiUserDataPositionSideDual(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "data": {
        #             "wallet": "W001",
        #             "contractType": "FUTURE",
        #             "settleCoin": "USDT",
        #             "positionType": "HEDGE",
        #             "unitModel": 2,
        #             "pricingModel": "FLAG",
        #             "priceProtection": "CLOSE",
        #             "totalWallet": 2
        #         },
        #         "success": True
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        hedged = self.safe_string(data, 'positionType') == 'HEDGE'
        return {
            'info': response,
            'hedged': hedged,
        }

    def fetch_balance(self, params={}) -> Balances:
        """
        query for balance and get the amount of funds available for trading or funds locked in orders

        https://developers.bydfi.com/en/account#asset-inquiry
        https://developers.bydfi.com/en/futures/user#asset-query

        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.account]: the type of account to fetch the balance for, either 'SPOT' or 'UMFUTURE'  or 'CMFUTURE'  or 'COPY'  or 'GRID'  or 'FUNDING'(default is 'SPOT')
        :param str [params.wallet]: *swap only* The unique code of a sub-wallet. W001 is the default wallet and the main wallet code of the contract
        :param str [params.asset]: currency id for the balance to fetch
        :returns dict: a `balance structure <https://docs.ccxt.com/?id=balance-structure>`
        """
        self.load_markets()
        type = None
        type, params = self.handle_market_type_and_params('fetchBalance', None, params)
        wallet = None
        wallet, params = self.handle_option_and_params(params, 'fetchBalance', 'wallet')
        request: dict = {}
        response = None
        if wallet is None:
            options = self.safe_dict(self.options, 'accountsByType', {})
            parsedAccountType = self.safe_string_upper(options, type, type)
            request['walletType'] = parsedAccountType
            #
            #     {
            #         "code": 200,
            #         "message": "success",
            #         "data": [
            #             {
            #                 "walletType": "spot",
            #                 "asset": "USDC",
            #                 "total": "100",
            #                 "available": "100",
            #                 "frozen": "0"
            #             }
            #         ],
            #         "success": True
            #     }
            #
            response = self.privateGetV1AccountAssets(self.extend(request, params))
        else:
            request['wallet'] = wallet
            #
            #     {
            #         "code": 200,
            #         "message": "success",
            #         "data": [
            #             {
            #                 "wallet": "W001",
            #                 "asset": "USDT",
            #                 "balance": "0",
            #                 "frozen": "0",
            #                 "positionMargin": "0",
            #                 "availableBalance": "0",
            #                 "canWithdrawAmount": "0",
            #                 "bonusAmount": "0"
            #             },
            #             {
            #                 "wallet": "W001",
            #                 "asset": "USDC",
            #                 "balance": "99.99505828",
            #                 "frozen": "4.0024",
            #                 "positionMargin": "2.95342",
            #                 "availableBalance": "92.96020828",
            #                 "canWithdrawAmount": "92.96020828",
            #                 "bonusAmount": "0"
            #             }
            #         ],
            #         "success": True
            #     }
            response = self.privateGetV1FapiAccountBalance(self.extend(request, params))
        data = self.safe_list(response, 'data', [])
        return self.parse_balance(data)

    def parse_balance(self, response) -> Balances:
        timestamp = self.milliseconds()
        result: dict = {
            'info': response,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
        }
        for i in range(0, len(response)):
            balance = response[i]
            symbol = self.safe_string(balance, 'asset')
            code = self.safe_currency_code(symbol)
            account = self.account()
            account['total'] = self.safe_string_2(balance, 'total', 'balance')
            account['free'] = self.safe_string_2(balance, 'available', 'availableBalance')
            result[code] = account
        return self.safe_balance(result)

    def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
        """
        transfer currency internally between wallets on the same account

        https://developers.bydfi.com/en/account#asset-transfer-between-accounts

        :param str code: unified currency code
        :param float amount: amount to transfer
        :param str fromAccount: 'spot', 'funding', or 'swap'
        :param str toAccount: 'spot', 'funding', or 'swap'
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `transfer structure <https://docs.ccxt.com/?id=transfer-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        accountsByType = self.safe_dict(self.options, 'accountsByType', {})
        fromId = self.safe_string(accountsByType, fromAccount, fromAccount)
        toId = self.safe_string(accountsByType, toAccount, toAccount)
        request: dict = {
            'asset': currency['id'],
            'amount': self.currency_to_precision(code, amount),
            'fromType': fromId,
            'toType': toId,
        }
        response = self.privatePostV1AccountTransfer(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "success": True
        #     }
        #
        transfer = self.parse_transfer(response, currency)
        transferOptions = self.safe_dict(self.options, 'transfer', {})
        fillResponseFromRequest = self.safe_bool(transferOptions, 'fillResponseFromRequest', True)
        if fillResponseFromRequest:
            timestamp = self.milliseconds()
            transfer['timestamp'] = timestamp
            transfer['datetime'] = self.iso8601(timestamp)
            transfer['currency'] = code
            transfer['fromAccount'] = fromAccount
            transfer['toAccount'] = toAccount
            transfer['amount'] = amount
        return transfer

    def fetch_transfers(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[TransferEntry]:
        """
        fetch a history of internal transfers made on an account

        https://developers.bydfi.com/en/account#query-wallet-transfer-records

        :param str code: unified currency code of the currency transferred
        :param int [since]: the earliest time in ms to fetch transfers for
        :param int [limit]: the maximum number of transfers structures to retrieve(default 10)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch entries for
        :returns dict[]: a list of `transfer structures <https://docs.ccxt.com/?id=transfer-structure>`
        """
        if code is None:
            raise ArgumentsRequired(self.id + ' fetchTransfers() requires a code argument')
        self.load_markets()
        currency = self.currency(code)
        paginate = self.safe_bool(params, 'paginate', False)
        if paginate:
            maxLimit = 50
            params = self.omit(params, 'paginate')
            params = self.extend(params, {'paginationDirection': 'backward'})
            paginatedResponse = self.fetch_paginated_call_dynamic('fetchTransfers', currency['code'], since, limit, params, maxLimit, True)
            return self.sort_by(paginatedResponse, 'timestamp')
        request: dict = {
            'asset': currency['id'],
        }
        until = None
        until, params = self.handle_option_and_params_2(params, 'fetchTransfers', 'until', 'endTime')
        if until is None:
            until = self.milliseconds()  # exchange requires endTime
        if since is None:
            since = 1  # exchange requires startTime but allows any value
        request['startTime'] = since
        request['endTime'] = until
        if limit is not None:
            request['rows'] = limit
        response = self.privateGetV1AccountTransferRecords(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "data": [
        #             {
        #                 "orderId": "1209991065294581760",
        #                 "txId": "6km5fRK83Gwdp43HA479DW1Colh2pKyS",
        #                 "sourceWallet": "SPOT",
        #                 "targetWallet": "SWAP",
        #                 "asset": "USDC",
        #                 "amount": "100",
        #                 "status": "SUCCESS",
        #                 "timestamp": 1766413950000
        #             }
        #         ],
        #         "success": True
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_transfers(data, currency, since, limit)

    def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
        #
        # transfer
        #     {
        #         "code": 200,
        #         "message": "success",
        #         "success": True
        #     }
        #
        # fetchTransfers
        #     {
        #         "orderId": "1209991065294581760",
        #         "txId": "6km5fRK83Gwdp43HA479DW1Colh2pKyS",
        #         "sourceWallet": "SPOT",
        #         "targetWallet": "SWAP",
        #         "asset": "USDC",
        #         "amount": "100",
        #         "status": "SUCCESS",
        #         "timestamp": 1766413950000
        #     }
        #
        status = self.safe_string_upper_2(transfer, 'message', 'status')
        accountsById = self.safe_dict(self.options, 'accountsById', {})
        fromId = self.safe_string_upper(transfer, 'sourceWallet')
        toId = self.safe_string_upper(transfer, 'targetWallet')
        fromAccount = self.safe_string(accountsById, fromId, fromId)
        toAccount = self.safe_string(accountsById, toId, toId)
        timestamp = self.safe_integer(transfer, 'timestamp')
        currencyId = self.safe_string(transfer, 'asset')
        return {
            'info': transfer,
            'id': self.safe_string(transfer, 'txId'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'currency': self.safe_currency_code(currencyId, currency),
            'amount': self.safe_number(transfer, 'amount'),
            'fromAccount': fromAccount,
            'toAccount': toAccount,
            'status': self.parase_transfer_status(status),
        }

    def parase_transfer_status(self, status: Str) -> Str:
        statuses = {
            'SUCCESS': 'ok',
            'WAIT': 'pending',
            'FAILED': 'failed',
        }
        return self.safe_string(statuses, status, status)

    def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all deposits made to an account

        https://developers.bydfi.com/en/spot/account#query-deposit-records

        :param str code: unified currency code(mandatory)
        :param int [since]: the earliest time in ms to fetch deposits for
        :param int [limit]: the maximum number of deposits structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/?id=transaction-structure>`
        """
        return self.fetch_transactions_helper('deposit', code, since, limit, params)

    def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all withdrawals made from an account

        https://developers.bydfi.com/en/spot/account#query-withdrawal-records

        :param str code: unified currency code(mandatory)
        :param int [since]: the earliest time in ms to fetch withdrawals for
        :param int [limit]: the maximum number of withdrawal structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/?id=transaction-structure>`
        """
        return self.fetch_transactions_helper('withdrawal', code, since, limit, params)

    def fetch_transactions_helper(self, type, code, since, limit, params):
        methodName = 'fetchDeposits' if (type == 'deposit') else 'fetchWithdrawals'
        if code is None:
            raise ArgumentsRequired(self.id + ' ' + methodName + '() requires a code argument')
        self.load_markets()
        currency = self.currency(code)
        paginate = self.safe_bool(params, 'paginate', False)
        if paginate:
            maxLimit = 50
            params = self.omit(params, 'paginate')
            params = self.extend(params, {'paginationDirection': 'backward'})
            paginatedResponse = self.fetch_paginated_call_dynamic(methodName, currency['code'], since, limit, params, maxLimit, True)
            return self.sort_by(paginatedResponse, 'timestamp')
        request: dict = {
            'asset': currency['id'],
        }
        until = None
        until, params = self.handle_option_and_params_2(params, 'fetchTransfers', 'until', 'endTime')
        now = self.milliseconds()
        sevenDays = 7 * 24 * 60 * 60 * 1000  # the maximum range is 7 days
        startTime = since
        if startTime is None:
            if until is None:
                # both since and until are None
                startTime = now - sevenDays
                until = now
            else:
                # since is None but until is defined
                startTime = until - sevenDays
        elif until is None:
            # until is None but since is defined
            delta = now - startTime
            if delta > sevenDays:
                until = startTime + sevenDays
            else:
                until = now
        request['startTime'] = startTime
        request['endTime'] = until
        if limit is not None:
            request['limit'] = limit
        response = None
        if type == 'deposit':
            #
            #     {
            #         "code": 200,
            #         "message": "success",
            #         "data": [
            #             {
            #                 "orderId": "1208864446987255809",
            #                 "asset": "USDC",
            #                 "amount": "200",
            #                 "status": "SUCCESS",
            #                 "txId": "0xd059a82a55ffc737722bd23c1ef3db2884ce8525b72ff0b3c038b430ce0c8ca5",
            #                 "network": "ETH",
            #                 "address": "0x8346b46f6aa9843c09f79f1c170a37aca83c8fcd",
            #                 "addressTag": null,
            #                 "finishTime": 1766145475000,
            #                 "createTime": 1766145344000
            #             }
            #         ],
            #         "success": True
            #     }
            #
            response = self.privateGetV1SpotDepositRecords(self.extend(request, params))
        else:
            #
            # todo check after withdrawal
            #
            response = self.privateGetV1SpotWithdrawRecords(self.extend(request, params))
        data = self.safe_list(response, 'data', [])
        transactionParams: dict = {
            'type': type,
        }
        params = self.extend(params, transactionParams)
        return self.parse_transactions(data, currency, since, limit, params)

    def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
        #
        # fetchDeposits
        #     {
        #         "orderId": "1208864446987255809",
        #         "asset": "USDC",
        #         "amount": "200",
        #         "status": "SUCCESS",
        #         "txId": "0xd059a82a55ffc737722bd23c1ef3db2884ce8525b72ff0b3c038b430ce0c8ca5",
        #         "network": "ETH",
        #         "address": "0x8346b46f6aa9843c09f79f1c170a37aca83c8fcd",
        #         "addressTag": null,
        #         "finishTime": 1766145475000,
        #         "createTime": 1766145344000
        #     }
        #
        currencyId = self.safe_string(transaction, 'asset')
        code = self.safe_currency_code(currencyId, currency)
        rawStatus = self.safe_string_lower(transaction, 'status')
        timestamp = self.safe_integer(transaction, 'createTime')
        fee = None
        feeCost = self.safe_number(transaction, 'fee')
        if feeCost is not None:
            fee = {
                'cost': feeCost,
                'currency': None,
            }
        return {
            'info': transaction,
            'id': self.safe_string(transaction, 'orderId'),
            'txid': self.safe_string(transaction, 'txId'),
            'type': None,
            'currency': code,
            'network': self.network_id_to_code(self.safe_string(transaction, 'network')),
            'amount': self.safe_number(transaction, 'amount'),
            'status': self.parse_transaction_status(rawStatus),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'address': self.safe_string(transaction, 'address'),
            'addressFrom': None,
            'addressTo': None,
            'tag': self.safe_string(transaction, 'addressTag'),
            'tagFrom': None,
            'tagTo': None,
            'updated': self.safe_integer(transaction, 'finishTime'),
            'comment': None,
            'fee': fee,
            'internal': False,
        }

    def parse_transaction_status(self, status: Str) -> Str:
        statuses = {
            'success': 'ok',
            'wait': 'pending',
            'failed': 'failed',
        }
        return self.safe_string(statuses, status, status)

    def sign(self, path, api: Any = 'public', method='GET', params={}, headers: Any = None, body: Any = None):
        url = self.urls['api'][api]
        endpoint = '/' + path
        query = ''
        sortedParams = self.keysort(params)
        if method == 'GET':
            query = self.urlencode(sortedParams)
            if len(query) != 0:
                endpoint += '?' + query
        if api == 'private':
            self.check_required_credentials()
            timestamp = str(self.milliseconds())
            if method == 'GET':
                payload = self.apiKey + timestamp + query
                signature = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha256, 'hex')
                headers = {
                    'X-API-KEY': self.apiKey,
                    'X-API-TIMESTAMP': timestamp,
                    'X-API-SIGNATURE': signature,
                }
            else:
                body = self.json(sortedParams)
                payload = self.apiKey + timestamp + body
                signature = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha256, 'hex')
                headers = {
                    'Content-Type': 'application/json',
                    'X-API-KEY': self.apiKey,
                    'X-API-TIMESTAMP': timestamp,
                    'X-API-SIGNATURE': signature,
                }
        url += endpoint
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, httpCode: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
        if response is None:
            return None  # fallback to default error handler
        #
        #     {
        #         "code": 101107,
        #         "message": "Requires transaction permissions"
        #     }
        #
        code = self.safe_string(response, 'code')
        message = self.safe_string(response, 'message')
        if code != '200':
            feedback = self.id + ' ' + body
            self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
            self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
            self.throw_exactly_matched_exception(self.exceptions['exact'], code, feedback)
            raise ExchangeError(feedback)  # unknown message
        return None
