Files
beast-trader/dashboard/venv/lib/python3.12/site-packages/ccxt/bydfi.py

2805 lines
126 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- 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'] = min(limit, 1000)
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