2073 lines
84 KiB
Python
2073 lines
84 KiB
Python
# -*- 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.async_support.base.exchange import Exchange
|
||
from ccxt.abstract.defx import ImplicitAPI
|
||
import asyncio
|
||
import hashlib
|
||
from ccxt.base.types import Any, Balances, Currency, Int, LedgerEntry, Leverage, Market, Num, Order, OrderBook, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, FundingRate, Trade, Transaction
|
||
from typing import List
|
||
from ccxt.base.errors import ExchangeError
|
||
from ccxt.base.errors import AuthenticationError
|
||
from ccxt.base.errors import ArgumentsRequired
|
||
from ccxt.base.errors import BadRequest
|
||
from ccxt.base.errors import InvalidOrder
|
||
from ccxt.base.errors import NotSupported
|
||
from ccxt.base.decimal_to_precision import TICK_SIZE
|
||
from ccxt.base.precise import Precise
|
||
|
||
|
||
class defx(Exchange, ImplicitAPI):
|
||
|
||
def describe(self) -> Any:
|
||
return self.deep_extend(super(defx, self).describe(), {
|
||
'id': 'defx',
|
||
'name': 'Defx X',
|
||
# 'countries': [''],
|
||
'rateLimit': 100,
|
||
'version': 'v1',
|
||
'certified': False,
|
||
'pro': False,
|
||
'hostname': 'defx.com',
|
||
'dex': True,
|
||
'has': {
|
||
'CORS': None,
|
||
'spot': False,
|
||
'margin': False,
|
||
'swap': True,
|
||
'future': False,
|
||
'option': False,
|
||
'addMargin': True,
|
||
'cancelAllOrders': True,
|
||
'cancelAllOrdersAfter': False,
|
||
'cancelOrder': True,
|
||
'cancelWithdraw': False,
|
||
'closeAllPositions': True,
|
||
'closePosition': True,
|
||
'createConvertTrade': False,
|
||
'createDepositAddress': False,
|
||
'createMarketBuyOrderWithCost': False,
|
||
'createMarketOrder': False,
|
||
'createMarketOrderWithCost': False,
|
||
'createMarketSellOrderWithCost': False,
|
||
'createOrder': True,
|
||
'createOrderWithTakeProfitAndStopLoss': True,
|
||
'createReduceOnlyOrder': True,
|
||
'createStopLimitOrder': False,
|
||
'createStopLossOrder': False,
|
||
'createStopMarketOrder': False,
|
||
'createStopOrder': False,
|
||
'createTakeProfitOrder': True,
|
||
'createTrailingAmountOrder': False,
|
||
'createTrailingPercentOrder': False,
|
||
'createTriggerOrder': True,
|
||
'fetchAccounts': False,
|
||
'fetchBalance': True,
|
||
'fetchCanceledOrders': True,
|
||
'fetchClosedOrder': False,
|
||
'fetchClosedOrders': True,
|
||
'fetchConvertCurrencies': False,
|
||
'fetchConvertQuote': False,
|
||
'fetchConvertTrade': False,
|
||
'fetchConvertTradeHistory': False,
|
||
'fetchCurrencies': False,
|
||
'fetchDepositAddress': False,
|
||
'fetchDepositAddresses': False,
|
||
'fetchDepositAddressesByNetwork': False,
|
||
'fetchDeposits': False,
|
||
'fetchDepositsWithdrawals': False,
|
||
'fetchFundingHistory': False,
|
||
'fetchFundingInterval': False,
|
||
'fetchFundingIntervals': False,
|
||
'fetchFundingRate': True,
|
||
'fetchFundingRateHistory': False,
|
||
'fetchFundingRates': False,
|
||
'fetchIndexOHLCV': False,
|
||
'fetchLedger': True,
|
||
'fetchLeverage': False,
|
||
'fetchMarginAdjustmentHistory': False,
|
||
'fetchMarginMode': False,
|
||
'fetchMarkets': True,
|
||
'fetchMarkOHLCV': False,
|
||
'fetchMarkPrice': False,
|
||
'fetchMarkPrices': False,
|
||
'fetchMyTrades': True,
|
||
'fetchOHLCV': True,
|
||
'fetchOpenInterestHistory': False,
|
||
'fetchOpenOrder': False,
|
||
'fetchOpenOrders': True,
|
||
'fetchOrder': True,
|
||
'fetchOrderBook': True,
|
||
'fetchOrders': True,
|
||
'fetchOrderTrades': False,
|
||
'fetchPosition': True,
|
||
'fetchPositionHistory': False,
|
||
'fetchPositionMode': False,
|
||
'fetchPositions': True,
|
||
'fetchPositionsHistory': False,
|
||
'fetchPremiumIndexOHLCV': False,
|
||
'fetchStatus': True,
|
||
'fetchTicker': True,
|
||
'fetchTickers': True,
|
||
'fetchTime': True,
|
||
'fetchTrades': True,
|
||
'fetchTradingFee': False,
|
||
'fetchTradingFees': False,
|
||
'fetchTransactions': False,
|
||
'fetchTransfers': False,
|
||
'fetchWithdrawals': False,
|
||
'reduceMargin': False,
|
||
'sandbox': True,
|
||
'setLeverage': True,
|
||
'setMargin': False,
|
||
'setPositionMode': False,
|
||
'transfer': False,
|
||
'withdraw': True,
|
||
},
|
||
'timeframes': {
|
||
'1m': '1m',
|
||
'3m': '3m',
|
||
'5m': '5m',
|
||
'15m': '15m',
|
||
'30m': '30m',
|
||
'1h': '1h',
|
||
'2h': '2h',
|
||
'4h': '4h',
|
||
'12h': '12h',
|
||
'1d': '1d',
|
||
'1w': '1w',
|
||
'1M': '1M',
|
||
},
|
||
'urls': {
|
||
'logo': 'https://github.com/user-attachments/assets/4e92bace-d7a9-45ea-92be-122168dc87e4',
|
||
'api': {
|
||
'public': 'https://api.{hostname}',
|
||
'private': 'https://api.{hostname}',
|
||
},
|
||
'test': {
|
||
'public': 'https://api.testnet.{hostname}',
|
||
'private': 'https://api.testnet.{hostname}',
|
||
},
|
||
'www': 'https://defx.com/home',
|
||
'doc': [
|
||
'https://docs.defx.com/docs',
|
||
'https://api-docs.defx.com/',
|
||
],
|
||
'fees': [
|
||
'',
|
||
],
|
||
'referral': {
|
||
'url': 'https://app.defx.com/join/6I2CZ7',
|
||
},
|
||
},
|
||
'api': {
|
||
'v1': {
|
||
'public': {
|
||
'get': {
|
||
'healthcheck/ping': 1,
|
||
'symbols/{symbol}/ohlc': 1,
|
||
'symbols/{symbol}/trades': 1,
|
||
'symbols/{symbol}/prices': 1,
|
||
'symbols/{symbol}/ticker/24hr': 1,
|
||
'symbols/{symbol}/depth/{level}/{slab}': 1,
|
||
'ticker/24HrAgg': 1,
|
||
'c/markets': 1,
|
||
'c/markets/metadata': 1,
|
||
'analytics/market/stats/newUsers': 1,
|
||
'analytics/market/stats/tvl': 1,
|
||
'analytics/market/stats/volumeByInstrument': 1,
|
||
'analytics/market/stats/liquidation': 1,
|
||
'analytics/market/stats/totalVolume': 1,
|
||
'analytics/market/stats/openInterest': 1,
|
||
'analytics/market/stats/totalTrades': 1,
|
||
'analytics/market/stats/basis': 1,
|
||
'analytics/market/stats/insuranceFund': 1,
|
||
'analytics/market/stats/longAndShortRatio': 1,
|
||
'analytics/market/stats/fundingRate': 1,
|
||
'analytics/market/overview': 1,
|
||
'explorer/search': 1,
|
||
'explorer/transactions': 1,
|
||
'explorer/blocks': 1,
|
||
},
|
||
},
|
||
'private': {
|
||
'get': {
|
||
'api/order/{orderId}': 1,
|
||
'api/orders': 1,
|
||
'api/orders/oco/{parentOrderId}': 1,
|
||
'api/trades': 1,
|
||
'api/position/active': 1,
|
||
'api/users/metadata/leverage': 1,
|
||
'api/users/metadata/feeMultiplier': 1,
|
||
'api/users/metadata/slippage': 1,
|
||
'api/users/referral': 1,
|
||
'api/users/apikeys': 1,
|
||
'connection-signature-message/evm': 1,
|
||
'api/users/profile/wallets': 1,
|
||
'api/notifications': 1,
|
||
'api/wallet/balance': 1,
|
||
'api/wallet/transactions': 1,
|
||
'api/analytics/user/overview': 1,
|
||
'api/analytics/user/pnl': 1,
|
||
'api/analytics/points/overview': 1,
|
||
'api/analytics/points/history': 1,
|
||
},
|
||
'post': {
|
||
'api/order': 1,
|
||
'api/position/oco': 1,
|
||
'api/users/socket/listenKeys': 1,
|
||
'api/users/metadata/leverage': 1,
|
||
'api/users/metadata/feeMultiplier': 1,
|
||
'api/users/metadata/slippage': 1,
|
||
'api/users/referral/recordReferralSignup': 1,
|
||
'api/users/apikeys': 1,
|
||
'api/users/profile/wallets': 1,
|
||
'api/transfers/withdrawal': 1,
|
||
'api/transfers/bridge/withdrawal': 1,
|
||
},
|
||
'put': {
|
||
'api/position/updatePositionMargin': 1,
|
||
'api/users/socket/listenKeys/{listenKey}': 1,
|
||
'api/users/apikeys/{accessKey}/status': 1,
|
||
'api/users/referral': 1,
|
||
},
|
||
'patch': {
|
||
'api/users/apikeys/{accessKey}': 1,
|
||
},
|
||
'delete': {
|
||
'api/orders/allOpen': 1,
|
||
'api/order/{orderId}': 1,
|
||
'api/position/{positionId}': 1,
|
||
'api/position/all': 1,
|
||
'api/users/socket/listenKeys/{listenKey}': 1,
|
||
'api/users/apikeys/{accessKey}': 1,
|
||
},
|
||
},
|
||
},
|
||
},
|
||
'fees': {
|
||
'trading': {
|
||
'tierBased': True,
|
||
'percentage': True,
|
||
'maker': self.parse_number('0.0002'),
|
||
'taker': self.parse_number('0.0005'),
|
||
},
|
||
},
|
||
'options': {
|
||
'sandboxMode': False,
|
||
},
|
||
'features': {
|
||
'spot': None,
|
||
'forDerivatives': {
|
||
'sandbox': True,
|
||
'createOrder': {
|
||
'marginMode': False,
|
||
'triggerPrice': True,
|
||
# todo implement
|
||
'triggerPriceType': {
|
||
'last': True,
|
||
'mark': True,
|
||
'index': False,
|
||
},
|
||
'triggerDirection': False,
|
||
'stopLossPrice': False, # todo
|
||
'takeProfitPrice': False, # todo
|
||
'attachedStopLossTakeProfit': None,
|
||
'timeInForce': {
|
||
'IOC': True,
|
||
'FOK': True,
|
||
'PO': True,
|
||
'GTD': False,
|
||
},
|
||
'hedged': False,
|
||
'selfTradePrevention': False,
|
||
'trailing': False,
|
||
'iceberg': False,
|
||
'leverage': False,
|
||
'marketBuyByCost': False,
|
||
'marketBuyRequiresPrice': False,
|
||
},
|
||
'createOrders': None,
|
||
'fetchMyTrades': {
|
||
'marginMode': False,
|
||
'limit': 1000,
|
||
'daysBack': None,
|
||
'untilDays': None,
|
||
'symbolRequired': False,
|
||
},
|
||
'fetchOrder': {
|
||
'marginMode': False,
|
||
'trigger': False,
|
||
'trailing': False,
|
||
'symbolRequired': False,
|
||
},
|
||
'fetchOpenOrders': {
|
||
'marginMode': True,
|
||
'limit': 100,
|
||
'trigger': False,
|
||
'trailing': False,
|
||
'symbolRequired': False,
|
||
},
|
||
'fetchOrders': {
|
||
'marginMode': False,
|
||
'limit': 500,
|
||
'daysBack': 100000,
|
||
'untilDays': 100000,
|
||
'trigger': False,
|
||
'trailing': False,
|
||
'symbolRequired': False,
|
||
},
|
||
'fetchClosedOrders': {
|
||
'marginMode': False,
|
||
'limit': 500,
|
||
'daysBack': 100000,
|
||
'daysBackCanceled': 1,
|
||
'untilDays': 100000,
|
||
'trigger': False,
|
||
'trailing': False,
|
||
'symbolRequired': False,
|
||
},
|
||
'fetchOHLCV': {
|
||
'limit': 1000,
|
||
},
|
||
},
|
||
'swap': {
|
||
'linear': {
|
||
'extends': 'forDerivatives',
|
||
},
|
||
'inverse': None,
|
||
},
|
||
'future': {
|
||
'linear': None,
|
||
'inverse': None,
|
||
},
|
||
},
|
||
'commonCurrencies': {},
|
||
'exceptions': {
|
||
'exact': {
|
||
'404': BadRequest, # {"errorCode":404,"errorMessage":"Not Found"}
|
||
'missing_auth_signature': AuthenticationError, # {"msg":"Missing auth signature","code":"missing_auth_signature"}
|
||
'order_rejected': InvalidOrder, # {"success":false,"err":{"msg":"Order has already been rejected","code":"order_rejected"}}
|
||
'invalid_order_id': InvalidOrder, # {"success":false,"err":{"msg":"Invalid order id","code":"invalid_order_id"}}
|
||
'filter_lotsize_maxqty': InvalidOrder, # {"errorCode":"filter_lotsize_maxqty","errorMessage":"LOT_SIZE filter failed, quantity more than maxQty","errorData":{"maxQty":"5000.00"}}
|
||
'filter_notional_min': InvalidOrder, # {"errorCode":"filter_notional_min","errorMessage":"NOTIONAL filter failed, Notional value of quote asset less than minNotional","errorData":{"minNotional":"100.00000000"}}
|
||
'failed_index_price_up_multiplier_filter': InvalidOrder, # {"errorCode":"failed_index_price_up_multiplier_filter","errorMessage":"failed_index_price_up_multiplier_filter","errorData":{"maxPrice":"307.81241042"}}
|
||
'no_open_orders': InvalidOrder, # {"errorMessage":"No open orders found","errorCode":"no_open_orders"}
|
||
'active_position_not_found': InvalidOrder, # {"errorCode":"active_position_not_found","errorMessage":"Active position not found"}
|
||
'position_inactive': InvalidOrder, # {"errorCode":"position_inactive","errorMessage":"Position is already inactive"}
|
||
'invalid_position_id': InvalidOrder, # {"errorCode":"invalid_position_id","errorMessage":"Position id is invalid"}
|
||
'Internal server error': ExchangeError, # {"msg":"Internal server error","code":"internal_server_error"}
|
||
},
|
||
'broad': {
|
||
'Bad Request': BadRequest, # {"errorMessage":"Bad Request","data":[{"param":"symbol","message":"\"symbol\" must be one of [ETH_USDC, BTC_USDC, BNB_USDC, SOL_USDC, DOGE_USDC, TON_USDC, AVAX_USDC, WIF_USDC, KPEPE_USDC, KSHIB_USDC, KBONK_USDC, MOODENG_USDC, POPCAT_USDC, MOTHER_USDC]"}]}
|
||
},
|
||
},
|
||
'precisionMode': TICK_SIZE,
|
||
})
|
||
|
||
async def fetch_status(self, params={}):
|
||
"""
|
||
the latest known information on the availability of the exchange API
|
||
|
||
https://api-docs.defx.com/#4b03bb3b-a0fa-4dfb-b96c-237bde0ce9e6
|
||
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: a `status structure <https://docs.ccxt.com/#/?id=exchange-status-structure>`
|
||
"""
|
||
response = await self.v1PublicGetHealthcheckPing(params)
|
||
#
|
||
# {
|
||
# "success": True,
|
||
# "t": 1709705048323,
|
||
# "v": "0.0.7",
|
||
# "msg": "A programmer’s wife tells him, “While you’re at the grocery store, buy some eggs.” He never comes back."
|
||
# }
|
||
#
|
||
status = None
|
||
success = self.safe_bool(response, 'success')
|
||
if success:
|
||
status = 'ok'
|
||
else:
|
||
status = 'error'
|
||
return {
|
||
'status': status,
|
||
'updated': None,
|
||
'eta': None,
|
||
'url': None,
|
||
'info': response,
|
||
}
|
||
|
||
async def fetch_time(self, params={}) -> Int:
|
||
"""
|
||
fetches the current integer timestamp in milliseconds from the exchange server
|
||
|
||
https://api-docs.defx.com/#4b03bb3b-a0fa-4dfb-b96c-237bde0ce9e6
|
||
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns int: the current integer timestamp in milliseconds from the exchange server
|
||
"""
|
||
response = await self.v1PublicGetHealthcheckPing(params)
|
||
#
|
||
# {
|
||
# "success": True,
|
||
# "t": 1709705048323,
|
||
# "v": "0.0.7",
|
||
# "msg": "A programmer’s wife tells him, “While you’re at the grocery store, buy some eggs.” He never comes back."
|
||
# }
|
||
#
|
||
return self.safe_integer(response, 't')
|
||
|
||
async def fetch_markets(self, params={}) -> List[Market]:
|
||
"""
|
||
retrieves data on all markets for defx
|
||
|
||
https://api-docs.defx.com/#73cce0c8-f842-4891-9145-01bb6d61324d
|
||
https://api-docs.defx.com/#24fd4e5b-840e-451e-99e0-7fea47c7f371
|
||
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict[]: an array of objects representing market data
|
||
"""
|
||
request = {
|
||
'type': 'perps',
|
||
}
|
||
promises = [
|
||
self.v1PublicGetCMarkets(self.extend(request, params)),
|
||
self.v1PublicGetCMarketsMetadata(self.extend(request, params)),
|
||
]
|
||
responses = await asyncio.gather(*promises)
|
||
#
|
||
# {
|
||
# "data": [
|
||
# {
|
||
# "market": "DOGE_USDC",
|
||
# "candleWindows": [
|
||
# "1m",
|
||
# "3m",
|
||
# "5m",
|
||
# "15m",
|
||
# "30m",
|
||
# "1h",
|
||
# "2h",
|
||
# "4h",
|
||
# "12h",
|
||
# "1d",
|
||
# "1w",
|
||
# "1M"
|
||
# ],
|
||
# "depthSlabs": [
|
||
# "0.00001",
|
||
# "0.00005",
|
||
# "0.0001",
|
||
# "0.001",
|
||
# "0.01"
|
||
# ],
|
||
# "filters": [
|
||
# {
|
||
# "filterType": "LOT_SIZE",
|
||
# "minQty": "1.00000",
|
||
# "maxQty": "1500000.00000",
|
||
# "stepSize": "1.00000"
|
||
# },
|
||
# {
|
||
# "filterType": "MARKET_LOT_SIZE",
|
||
# "minQty": "1.00000",
|
||
# "maxQty": "750000.00000",
|
||
# "stepSize": "1.00000"
|
||
# },
|
||
# {
|
||
# "filterType": "PRICE_FILTER",
|
||
# "minPrice": "0.00244000",
|
||
# "maxPrice": "30.00000000",
|
||
# "tickSize": "0.00001"
|
||
# },
|
||
# {
|
||
# "filterType": "NOTIONAL",
|
||
# "minNotional": "100.00000000"
|
||
# },
|
||
# {
|
||
# "filterType": "PERCENT_PRICE_BY_SIDE",
|
||
# "bidMultiplierUp": "1.5",
|
||
# "bidMultiplierDown": "0.5",
|
||
# "askMultiplierUp": "1.5",
|
||
# "askMultiplierDown": "0.5"
|
||
# },
|
||
# {
|
||
# "filterType": "INDEX_PRICE_FILTER",
|
||
# "multiplierUp": "1.3",
|
||
# "multiplierDown": "0.7"
|
||
# }
|
||
# ],
|
||
# "cappedLeverage": "25",
|
||
# "maintenanceMarginTiers": [
|
||
# {
|
||
# "tier": "1",
|
||
# "minMaintenanceMargin": "0",
|
||
# "maxMaintenanceMargin": "2500",
|
||
# "leverage": "25"
|
||
# },
|
||
# {
|
||
# "tier": "2",
|
||
# "minMaintenanceMargin": "2500",
|
||
# "maxMaintenanceMargin": "12500",
|
||
# "leverage": "20"
|
||
# },
|
||
# {
|
||
# "tier": "3",
|
||
# "minMaintenanceMargin": "12500",
|
||
# "maxMaintenanceMargin": "25000",
|
||
# "leverage": "15"
|
||
# },
|
||
# {
|
||
# "tier": "4",
|
||
# "minMaintenanceMargin": "25000",
|
||
# "maxMaintenanceMargin": "50000",
|
||
# "leverage": "10"
|
||
# },
|
||
# {
|
||
# "tier": "5",
|
||
# "minMaintenanceMargin": "50000",
|
||
# "maxMaintenanceMargin": "75000",
|
||
# "leverage": "8"
|
||
# },
|
||
# {
|
||
# "tier": "6",
|
||
# "minMaintenanceMargin": "75000",
|
||
# "maxMaintenanceMargin": "125000",
|
||
# "leverage": "7"
|
||
# },
|
||
# {
|
||
# "tier": "7",
|
||
# "minMaintenanceMargin": "125000",
|
||
# "maxMaintenanceMargin": "187500",
|
||
# "leverage": "5"
|
||
# },
|
||
# {
|
||
# "tier": "8",
|
||
# "minMaintenanceMargin": "187500",
|
||
# "maxMaintenanceMargin": "250000",
|
||
# "leverage": "3"
|
||
# },
|
||
# {
|
||
# "tier": "9",
|
||
# "minMaintenanceMargin": "250000",
|
||
# "maxMaintenanceMargin": "375000",
|
||
# "leverage": "2"
|
||
# },
|
||
# {
|
||
# "tier": "10",
|
||
# "minMaintenanceMargin": "375000",
|
||
# "maxMaintenanceMargin": "500000",
|
||
# "leverage": "1"
|
||
# }
|
||
# ],
|
||
# "fees": {
|
||
# "maker": "0.08",
|
||
# "taker": "0.1"
|
||
# }
|
||
# },
|
||
# ]
|
||
# }
|
||
#
|
||
activeMarkets = self.safe_list(responses[0], 'data')
|
||
activeMarketsByType = self.index_by(activeMarkets, 'market')
|
||
marketMetadatas = self.safe_list(responses[1], 'data')
|
||
for i in range(0, len(marketMetadatas)):
|
||
marketId = marketMetadatas[i]['market']
|
||
status = None
|
||
if marketId in activeMarketsByType:
|
||
status = activeMarketsByType[marketId]['status']
|
||
marketMetadatas[i]['status'] = status
|
||
return self.parse_markets(marketMetadatas)
|
||
|
||
def parse_market(self, market: dict) -> Market:
|
||
marketId = self.safe_string(market, 'market')
|
||
parts = marketId.split('_')
|
||
baseId = self.safe_string(parts, 0)
|
||
quoteId = self.safe_string(parts, 1)
|
||
base = self.safe_currency_code(baseId)
|
||
quote = self.safe_currency_code(quoteId)
|
||
symbol = base + '/' + quote + ':' + quote
|
||
filters = self.safe_list(market, 'filters', [])
|
||
fees = self.safe_dict(market, 'fees', {})
|
||
filtersByType = self.index_by(filters, 'filterType')
|
||
priceFilter = self.safe_dict(filtersByType, 'PRICE_FILTER', {})
|
||
lotFilter = self.safe_dict(filtersByType, 'LOT_SIZE', {})
|
||
marketLotFilter = self.safe_dict(filtersByType, 'MARKET_LOT_SIZE', {})
|
||
notionalFilter = self.safe_dict(filtersByType, 'NOTIONAL', {})
|
||
return {
|
||
'id': marketId,
|
||
'symbol': symbol,
|
||
'base': base,
|
||
'quote': quote,
|
||
'settle': quote,
|
||
'baseId': baseId,
|
||
'quoteId': quoteId,
|
||
'settleId': quoteId,
|
||
'type': 'swap',
|
||
'spot': False,
|
||
'margin': False,
|
||
'swap': True,
|
||
'future': False,
|
||
'option': False,
|
||
'active': self.safe_string(market, 'status', '') == 'active',
|
||
'contract': True,
|
||
'linear': True,
|
||
'inverse': False,
|
||
'taker': self.safe_number(fees, 'taker'),
|
||
'maker': self.safe_number(fees, 'maker'),
|
||
'contractSize': self.parse_number('1'),
|
||
'expiry': None,
|
||
'expiryDatetime': None,
|
||
'strike': None,
|
||
'optionType': None,
|
||
'precision': {
|
||
'amount': self.safe_number(lotFilter, 'stepSize'),
|
||
'price': self.safe_number(priceFilter, 'tickSize'),
|
||
},
|
||
'limits': {
|
||
'leverage': {
|
||
'min': None,
|
||
'max': self.safe_number(market, 'cappedLeverage'),
|
||
},
|
||
'amount': {
|
||
'min': self.safe_number(lotFilter, 'minQty'),
|
||
'max': self.safe_number(lotFilter, 'maxQty'),
|
||
},
|
||
'price': {
|
||
'min': self.safe_number(priceFilter, 'minPrice'),
|
||
'max': self.safe_number(priceFilter, 'maxPrice'),
|
||
},
|
||
'cost': {
|
||
'min': self.safe_number(notionalFilter, 'minNotional'),
|
||
'max': None,
|
||
},
|
||
'market': {
|
||
'min': self.safe_number(marketLotFilter, 'minQty'),
|
||
'max': self.safe_number(marketLotFilter, 'maxQty'),
|
||
},
|
||
},
|
||
'created': None,
|
||
'info': market,
|
||
}
|
||
|
||
async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
|
||
"""
|
||
fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
|
||
|
||
https://api-docs.defx.com/#fe6f81d0-2f3a-4eee-976f-c8fc8f4c5d56
|
||
|
||
: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>`
|
||
"""
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
}
|
||
response = await self.v1PublicGetSymbolsSymbolTicker24hr(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "symbol": "BTC_USDC",
|
||
# "priceChange": "0",
|
||
# "priceChangePercent": "0",
|
||
# "weightedAvgPrice": "0",
|
||
# "lastPrice": "2.00",
|
||
# "lastQty": "10.000",
|
||
# "bestBidPrice": "1646.00",
|
||
# "bestBidQty": "10.000",
|
||
# "bestAskPrice": "1646.00",
|
||
# "bestAskQty": "10.000",
|
||
# "openPrice": "0.00",
|
||
# "highPrice": "0.00",
|
||
# "lowPrice": "0.00",
|
||
# "volume": "0.000",
|
||
# "quoteVolume": "0.00",
|
||
# "openTime": 1700142658697,
|
||
# "closeTime": 1700142658697,
|
||
# "openInterestBase": "1.000",
|
||
# "openInterestQuote": "0.43112300"
|
||
# }
|
||
#
|
||
return self.parse_ticker(response, market)
|
||
|
||
async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
|
||
"""
|
||
fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
|
||
|
||
https://api-docs.defx.com/#8c61cfbd-40d9-410e-b014-f5b36eba51d1
|
||
|
||
: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>`
|
||
"""
|
||
await self.load_markets()
|
||
market = None
|
||
if symbols is not None:
|
||
symbols = self.market_symbols(symbols)
|
||
firstSymbol = self.safe_string(symbols, 0)
|
||
if firstSymbol is not None:
|
||
market = self.market(firstSymbol)
|
||
type = None
|
||
type, params = self.handle_market_type_and_params('fetchTickers', market, params)
|
||
if type == 'spot':
|
||
raise NotSupported(self.id + ' fetchTickers() is not supported for ' + type + ' markets')
|
||
response = await self.v1PublicGetTicker24HrAgg(params)
|
||
#
|
||
# {
|
||
# "ETH_USDC": {
|
||
# "priceChange": "0",
|
||
# "priceChangePercent": "0",
|
||
# "openPrice": "1646.15",
|
||
# "highPrice": "1646.15",
|
||
# "lowPrice": "1646.15",
|
||
# "lastPrice": "1646.15",
|
||
# "quoteVolume": "13.17",
|
||
# "volume": "0.008",
|
||
# "markPrice": "1645.15"
|
||
# }
|
||
# }
|
||
#
|
||
return self.parse_tickers(response, symbols)
|
||
|
||
def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
|
||
#
|
||
# fetchTicker
|
||
#
|
||
# {
|
||
# "symbol": "BTC_USDC",
|
||
# "priceChange": "0",
|
||
# "priceChangePercent": "0",
|
||
# "weightedAvgPrice": "0",
|
||
# "lastPrice": "2.00",
|
||
# "lastQty": "10.000",
|
||
# "bestBidPrice": "1646.00",
|
||
# "bestBidQty": "10.000",
|
||
# "bestAskPrice": "1646.00",
|
||
# "bestAskQty": "10.000",
|
||
# "openPrice": "0.00",
|
||
# "highPrice": "0.00",
|
||
# "lowPrice": "0.00",
|
||
# "volume": "0.000",
|
||
# "quoteVolume": "0.00",
|
||
# "openTime": 1700142658697,
|
||
# "closeTime": 1700142658697,
|
||
# "openInterestBase": "1.000",
|
||
# "openInterestQuote": "0.43112300"
|
||
# }
|
||
#
|
||
# fetchTickers
|
||
#
|
||
# "ETH_USDC": {
|
||
# "priceChange": "0",
|
||
# "priceChangePercent": "0",
|
||
# "openPrice": "1646.15",
|
||
# "highPrice": "1646.15",
|
||
# "lowPrice": "1646.15",
|
||
# "lastPrice": "1646.15",
|
||
# "quoteVolume": "13.17",
|
||
# "volume": "0.008",
|
||
# "markPrice": "1645.15"
|
||
# }
|
||
#
|
||
# fetchMarkPrice
|
||
#
|
||
# {
|
||
# "markPrice": "100.00",
|
||
# "indexPrice": "100.00",
|
||
# "ltp": "101.34",
|
||
# "movingFundingRate": "0.08",
|
||
# "payoutFundingRate": "-0.03",
|
||
# "nextFundingPayout": 1711555532146
|
||
# }
|
||
#
|
||
marketId = self.safe_string(ticker, 'symbol')
|
||
if marketId is not None:
|
||
market = self.market(marketId)
|
||
symbol = market['symbol']
|
||
open = self.safe_string(ticker, 'openPrice')
|
||
high = self.safe_string(ticker, 'highPrice')
|
||
low = self.safe_string(ticker, 'lowPrice')
|
||
close = self.safe_string(ticker, 'lastPrice')
|
||
quoteVolume = self.safe_string(ticker, 'quoteVolume')
|
||
baseVolume = self.safe_string(ticker, 'volume')
|
||
percentage = self.safe_string(ticker, 'priceChangePercent')
|
||
change = self.safe_string(ticker, 'priceChange')
|
||
ts = self.safe_integer(ticker, 'closeTime')
|
||
if ts == 0:
|
||
ts = None
|
||
datetime = self.iso8601(ts)
|
||
bid = self.safe_string(ticker, 'bestBidPrice')
|
||
bidVolume = self.safe_string(ticker, 'bestBidQty')
|
||
ask = self.safe_string(ticker, 'bestAskPrice')
|
||
askVolume = self.safe_string(ticker, 'bestAskQty')
|
||
return self.safe_ticker({
|
||
'symbol': symbol,
|
||
'timestamp': ts,
|
||
'datetime': datetime,
|
||
'high': high,
|
||
'low': low,
|
||
'bid': bid,
|
||
'bidVolume': bidVolume,
|
||
'ask': ask,
|
||
'askVolume': askVolume,
|
||
'vwap': None,
|
||
'open': open,
|
||
'close': close,
|
||
'last': None,
|
||
'previousClose': None,
|
||
'change': change,
|
||
'percentage': percentage,
|
||
'average': None,
|
||
'baseVolume': baseVolume,
|
||
'quoteVolume': quoteVolume,
|
||
'markPrice': self.safe_string(ticker, 'markPrice'),
|
||
'indexPrice': self.safe_string(ticker, 'indexPrice'),
|
||
'info': ticker,
|
||
}, market)
|
||
|
||
async def fetch_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
||
"""
|
||
|
||
https://api-docs.defx.com/#54b71951-1472-4670-b5af-4c2dc41e73d0
|
||
|
||
fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
||
: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]: max=1000, max=100 when since is defined and is less than(now - (999 * (timeframe in ms)))
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:param int [params.until]: the latest time in ms to fetch orders for
|
||
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
||
"""
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
maxLimit = 1000
|
||
if limit is None:
|
||
limit = maxLimit
|
||
limit = min(maxLimit, limit)
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
'interval': self.safe_string(self.timeframes, timeframe, timeframe),
|
||
'limit': limit,
|
||
}
|
||
until = self.safe_integer_2(params, 'until', 'till')
|
||
params = self.omit(params, ['until', 'till'])
|
||
request['endTime'] = self.milliseconds() if (until is None) else until
|
||
if since is None:
|
||
request['startTime'] = 0
|
||
else:
|
||
request['startTime'] = since
|
||
if until is None:
|
||
timeframeInSeconds = self.parse_timeframe(timeframe)
|
||
timeframeInMilliseconds = timeframeInSeconds * 1000
|
||
totalTimeframeInMilliseconds = limit * timeframeInMilliseconds
|
||
request['endTime'] = self.sum(since, totalTimeframeInMilliseconds)
|
||
response = await self.v1PublicGetSymbolsSymbolOhlc(self.extend(request, params))
|
||
#
|
||
# [
|
||
# {
|
||
# "symbol": "BTC_USDC",
|
||
# "open": "0.00",
|
||
# "high": "0.00",
|
||
# "low": "0.00",
|
||
# "close": "0.00",
|
||
# "volume": "0.000",
|
||
# "quoteAssetVolume": "0.00",
|
||
# "takerBuyAssetVolume": "0.000",
|
||
# "takerBuyQuoteAssetVolume": "0.00",
|
||
# "numberOfTrades": 0,
|
||
# "start": 1702453663894,
|
||
# "end": 1702453663894,
|
||
# "isClosed": True
|
||
# }
|
||
# ]
|
||
#
|
||
return self.parse_ohlcvs(response, market, timeframe, since, limit)
|
||
|
||
def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
|
||
# example response in fetchOHLCV
|
||
return [
|
||
self.safe_integer(ohlcv, 'start'),
|
||
self.safe_number(ohlcv, 'open'),
|
||
self.safe_number(ohlcv, 'high'),
|
||
self.safe_number(ohlcv, 'low'),
|
||
self.safe_number(ohlcv, 'close'),
|
||
self.safe_number(ohlcv, 'volume'),
|
||
]
|
||
|
||
async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
||
"""
|
||
get the list of most recent trades for a particular symbol
|
||
|
||
https://api-docs.defx.com/#5865452f-ea32-4f13-bfbc-03af5f5574fd
|
||
|
||
: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
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
|
||
"""
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
maxLimit = 50
|
||
if limit is None:
|
||
limit = maxLimit
|
||
limit = min(maxLimit, limit)
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
'limit': limit,
|
||
}
|
||
response = await self.v1PublicGetSymbolsSymbolTrades(self.extend(request, params))
|
||
#
|
||
# [
|
||
# {
|
||
# "buyerMaker": "false",
|
||
# "price": "2.0000",
|
||
# "qty": "10.0000",
|
||
# "symbol": "BTC_USDC",
|
||
# "timestamp": "1702453663894"
|
||
# }
|
||
# ]
|
||
#
|
||
return self.parse_trades(response, market, since, limit)
|
||
|
||
async 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://api-docs.defx.com/#06b5b33c-2fc6-48de-896c-fc316f5871a7
|
||
|
||
: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
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
|
||
"""
|
||
await self.load_markets()
|
||
request: dict = {}
|
||
if symbol is not None:
|
||
market = self.market(symbol)
|
||
request['symbols'] = market['id']
|
||
if limit is not None:
|
||
maxLimit = 100
|
||
limit = min(maxLimit, limit)
|
||
request['pageSize'] = limit
|
||
response = await self.v1PrivateGetApiTrades(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "data": [
|
||
# {
|
||
# "id": "0192f665-c05b-7ba0-a080-8b6c99083489",
|
||
# "orderId": "757730811259651728",
|
||
# "time": "2024-11-04T08:58:36.474Z",
|
||
# "symbol": "SOL_USDC",
|
||
# "side": "SELL",
|
||
# "price": "160.43600000",
|
||
# "qty": "1.00",
|
||
# "fee": "0.08823980",
|
||
# "role": "TAKER",
|
||
# "pnl": "0.00000000"
|
||
# }
|
||
# ]
|
||
# }
|
||
#
|
||
data = self.safe_list(response, 'data', [])
|
||
return self.parse_trades(data, None, since, limit)
|
||
|
||
def parse_trade(self, trade: dict, market: Market = None) -> Trade:
|
||
#
|
||
# fetchTrades
|
||
# {
|
||
# "buyerMaker": "false",
|
||
# "price": "2.0000",
|
||
# "qty": "10.0000",
|
||
# "symbol": "BTC_USDC",
|
||
# "timestamp": "1702453663894"
|
||
# }
|
||
#
|
||
# fetchMyTrades
|
||
# {
|
||
# "id": "0192f665-c05b-7ba0-a080-8b6c99083489",
|
||
# "orderId": "757730811259651728",
|
||
# "time": "2024-11-04T08:58:36.474Z",
|
||
# "symbol": "SOL_USDC",
|
||
# "side": "SELL",
|
||
# "price": "160.43600000",
|
||
# "qty": "1.00",
|
||
# "fee": "0.08823980",
|
||
# "role": "TAKER",
|
||
# "pnl": "0.00000000"
|
||
# }
|
||
#
|
||
time = self.safe_string(trade, 'time')
|
||
timestamp = self.safe_integer(trade, 'timestamp', self.parse8601(time))
|
||
marketId = self.safe_string(trade, 'symbol')
|
||
market = self.safe_market(marketId, market)
|
||
symbol = market['symbol']
|
||
price = self.safe_string(trade, 'price')
|
||
amount = self.safe_string(trade, 'qty')
|
||
id = self.safe_string(trade, 'id')
|
||
oid = self.safe_string(trade, 'orderId')
|
||
takerOrMaker = self.safe_string_lower(trade, 'role')
|
||
buyerMaker = self.safe_bool(trade, 'buyerMaker')
|
||
side = self.safe_string_lower(trade, 'side')
|
||
if buyerMaker is not None:
|
||
if buyerMaker:
|
||
side = 'sell'
|
||
else:
|
||
side = 'buy'
|
||
return self.safe_trade({
|
||
'id': id,
|
||
'timestamp': timestamp,
|
||
'datetime': self.iso8601(timestamp),
|
||
'symbol': symbol,
|
||
'side': side,
|
||
'price': price,
|
||
'amount': amount,
|
||
'cost': None,
|
||
'order': oid,
|
||
'takerOrMaker': takerOrMaker,
|
||
'type': None,
|
||
'fee': {
|
||
'cost': self.safe_string(trade, 'fee'),
|
||
'currency': 'USDC',
|
||
},
|
||
'info': trade,
|
||
}, market)
|
||
|
||
async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
|
||
"""
|
||
fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
||
|
||
https://api-docs.defx.com/#6c1a2971-8325-4e7d-9962-e0bfcaacf9c4
|
||
|
||
: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
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:param str [params.slab]: slab from market.info.depthSlabs
|
||
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
|
||
"""
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
if limit is None:
|
||
limit = 10 # limit must be one of [5, 10, 20]
|
||
marketInfo = self.safe_dict(market, 'info', {})
|
||
slab = self.safe_list(marketInfo, 'depthSlabs', [])
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
'level': limit,
|
||
'slab': self.safe_string(slab, 0),
|
||
}
|
||
response = await self.v1PublicGetSymbolsSymbolDepthLevelSlab(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "symbol": "ETH_USDC",
|
||
# "level": "5",
|
||
# "slab": "1",
|
||
# "lastTradeTimestamp": "1708313446812",
|
||
# "timestamp": "1708313446812",
|
||
# "bids": [
|
||
# {
|
||
# "price": "1646.16",
|
||
# "qty": "0.001"
|
||
# }
|
||
# ],
|
||
# "asks": [
|
||
# {
|
||
# "price": "1646.16",
|
||
# "qty": "0.001"
|
||
# }
|
||
# ]
|
||
# }
|
||
#
|
||
timestamp = self.safe_integer(response, 'timestamp')
|
||
return self.parse_order_book(response, symbol, timestamp, 'bids', 'asks', 'price', 'qty')
|
||
|
||
async def fetch_mark_price(self, symbol: str, params={}) -> Ticker:
|
||
"""
|
||
fetches mark price for the market
|
||
|
||
https://api-docs.defx.com/#12168192-4e7b-4458-a001-e8b80961f0b7
|
||
|
||
:param str symbol: unified symbol of the market to fetch the ticker for
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:param str [params.subType]: "linear" or "inverse"
|
||
:returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
request = {
|
||
'symbol': market['id'],
|
||
}
|
||
response = await self.v1PublicGetSymbolsSymbolPrices(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "markPrice": "100.00",
|
||
# "indexPrice": "100.00",
|
||
# "ltp": "101.34",
|
||
# "movingFundingRate": "0.08",
|
||
# "payoutFundingRate": "-0.03",
|
||
# "nextFundingPayout": 1711555532146
|
||
# }
|
||
#
|
||
return self.parse_ticker(response, market)
|
||
|
||
async def fetch_funding_rate(self, symbol: str, params={}) -> FundingRate:
|
||
"""
|
||
fetch the current funding rate
|
||
|
||
https://api-docs.defx.com/#12168192-4e7b-4458-a001-e8b80961f0b7
|
||
|
||
: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>`
|
||
"""
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
request = {
|
||
'symbol': market['id'],
|
||
}
|
||
response = await self.v1PublicGetSymbolsSymbolPrices(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "markPrice": "100.00",
|
||
# "indexPrice": "100.00",
|
||
# "ltp": "101.34",
|
||
# "movingFundingRate": "0.08",
|
||
# "payoutFundingRate": "-0.03",
|
||
# "nextFundingPayout": 1711555532146
|
||
# }
|
||
#
|
||
return self.parse_funding_rate(response, market)
|
||
|
||
def parse_funding_rate(self, contract, market: Market = None) -> FundingRate:
|
||
#
|
||
# {
|
||
# "markPrice": "100.00",
|
||
# "indexPrice": "100.00",
|
||
# "ltp": "101.34",
|
||
# "movingFundingRate": "0.08",
|
||
# "payoutFundingRate": "-0.03",
|
||
# "nextFundingPayout": 1711555532146
|
||
# }
|
||
#
|
||
markPrice = self.safe_number(contract, 'markPrice')
|
||
indexPrice = self.safe_number(contract, 'indexPrice')
|
||
fundingRateRaw = self.safe_string(contract, 'payoutFundingRate')
|
||
fundingRate = Precise.string_div(fundingRateRaw, '100')
|
||
fundingTime = self.safe_integer(contract, 'nextFundingPayout')
|
||
return {
|
||
'info': contract,
|
||
'symbol': market['symbol'],
|
||
'markPrice': markPrice,
|
||
'indexPrice': indexPrice,
|
||
'interestRate': None,
|
||
'estimatedSettlePrice': None,
|
||
'timestamp': None,
|
||
'datetime': None,
|
||
'fundingRate': self.parse_number(fundingRate),
|
||
'fundingTimestamp': fundingTime,
|
||
'fundingDatetime': self.iso8601(fundingTime),
|
||
'nextFundingRate': None,
|
||
'nextFundingTimestamp': None,
|
||
'nextFundingDatetime': None,
|
||
'previousFundingRate': None,
|
||
'previousFundingTimestamp': None,
|
||
'previousFundingDatetime': None,
|
||
'interval': None,
|
||
}
|
||
|
||
async def fetch_balance(self, params={}) -> Balances:
|
||
"""
|
||
query for balance and get the amount of funds available for trading or funds locked in orders
|
||
|
||
https://api-docs.defx.com/#26414338-14f7-40a1-b246-f8ea8571493f
|
||
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
response = await self.v1PrivateGetApiWalletBalance(params)
|
||
#
|
||
# {
|
||
# "assets": [
|
||
# {
|
||
# "asset": "USDC",
|
||
# "balance": "0.000"
|
||
# }
|
||
# ]
|
||
# }
|
||
#
|
||
data = self.safe_list(response, 'assets')
|
||
return self.parse_balance(data)
|
||
|
||
def parse_balance(self, balances) -> Balances:
|
||
result: dict = {
|
||
'info': balances,
|
||
}
|
||
for i in range(0, len(balances)):
|
||
balance = balances[i]
|
||
code = self.safe_currency_code(self.safe_string(balance, 'asset'))
|
||
account = self.account()
|
||
account['total'] = self.safe_string(balance, 'balance')
|
||
result[code] = account
|
||
return self.safe_balance(result)
|
||
|
||
async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
||
"""
|
||
create a trade order
|
||
|
||
https://api-docs.defx.com/#ba222d88-8856-4d3c-87a9-7cec07bb2622
|
||
|
||
: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 fulfilled, in units of the quote currency, ignored in market orders
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:param float [params.triggerPrice]: The price a trigger order is triggered at
|
||
:param str [params.reduceOnly]: for swap and future reduceOnly is a string 'true' or 'false' that cant be sent with close position set to True or in hedge mode. For spot margin and option reduceOnly is a boolean.
|
||
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
reduceOnly = self.safe_bool_2(params, 'reduceOnly', 'reduce_only')
|
||
params = self.omit(params, ['reduceOnly', 'reduce_only'])
|
||
orderType = type.upper()
|
||
orderSide = side.upper()
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
'side': orderSide,
|
||
'type': orderType,
|
||
}
|
||
takeProfitPrice = self.safe_string(params, 'takeProfitPrice')
|
||
triggerPrice = self.safe_string_2(params, 'stopPrice', 'triggerPrice')
|
||
isMarket = orderType == 'MARKET'
|
||
isLimit = orderType == 'LIMIT'
|
||
timeInForce = self.safe_string_upper(params, 'timeInForce')
|
||
if timeInForce is not None:
|
||
# GTC, IOC, FOK, AON
|
||
request['timeInForce'] = timeInForce
|
||
else:
|
||
if isLimit:
|
||
request['timeInForce'] = 'GTC'
|
||
if reduceOnly:
|
||
request['reduceOnly'] = reduceOnly
|
||
clientOrderId = self.safe_string(params, 'clientOrderId')
|
||
if clientOrderId is not None:
|
||
request['newClientOrderId'] = clientOrderId
|
||
if triggerPrice is not None or takeProfitPrice is not None:
|
||
request['workingType'] = 'MARK_PRICE'
|
||
if takeProfitPrice is not None:
|
||
request['stopPrice'] = self.price_to_precision(symbol, takeProfitPrice)
|
||
if isMarket:
|
||
request['type'] = 'TAKE_PROFIT_MARKET'
|
||
else:
|
||
request['type'] = 'TAKE_PROFIT_LIMIT'
|
||
else:
|
||
request['stopPrice'] = self.price_to_precision(symbol, triggerPrice)
|
||
if isMarket:
|
||
request['type'] = 'STOP_MARKET'
|
||
else:
|
||
request['type'] = 'STOP_LIMIT'
|
||
if isLimit and price is not None:
|
||
request['price'] = self.price_to_precision(symbol, price)
|
||
request['quantity'] = self.amount_to_precision(symbol, amount)
|
||
params = self.omit(params, ['clOrdID', 'clientOrderId', 'client_order_id', 'postOnly', 'timeInForce', 'stopPrice', 'triggerPrice', 'takeProfitPrice'])
|
||
response = await self.v1PrivatePostApiOrder(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "success": True,
|
||
# "data": {
|
||
# "orderId": "",
|
||
# "clientOrderId": "",
|
||
# "cumulativeQty": "",
|
||
# "cumulativeQuote": "",
|
||
# "executedQty": "",
|
||
# "avgPrice": "",
|
||
# "origQty": "",
|
||
# "price": "",
|
||
# "reduceOnly": True,
|
||
# "side": "",
|
||
# "status": "",
|
||
# "symbol": "",
|
||
# "timeInForce": "",
|
||
# "type": "",
|
||
# "workingType": ""
|
||
# }
|
||
# }
|
||
#
|
||
data = self.safe_dict(response, 'data')
|
||
return self.parse_order(data, market)
|
||
|
||
def parse_order_status(self, status: Str):
|
||
if status is not None:
|
||
statuses: dict = {
|
||
'NEW': 'open',
|
||
'OPEN': 'open',
|
||
'CANCELLED': 'canceled',
|
||
'REJECTED': 'rejected',
|
||
'FILLED': 'closed',
|
||
}
|
||
return self.safe_string(statuses, status, status)
|
||
return status
|
||
|
||
def parse_order(self, order: dict, market: Market = None) -> Order:
|
||
#
|
||
# {
|
||
# "orderId": "746472647227344528",
|
||
# "createdAt": "2024-10-25T16:49:31.077Z",
|
||
# "updatedAt": "2024-10-25T16:49:31.378Z",
|
||
# "clientOrderId": "0192c495-49c3-71ee-b3d3-7442a2090807",
|
||
# "reduceOnly": False,
|
||
# "side": "SELL",
|
||
# "status": "FILLED",
|
||
# "symbol": "SOL_USDC",
|
||
# "timeInForce": "GTC",
|
||
# "type": "MARKET",
|
||
# "origQty": "0.80",
|
||
# "executedQty": "0.80",
|
||
# "cumulativeQuote": "137.87440000",
|
||
# "avgPrice": "172.34300000",
|
||
# "totalPnL": "0.00000000",
|
||
# "totalFee": "0.07583092",
|
||
# "workingType": null,
|
||
# "postOnly": False,
|
||
# "linkedOrderParentType": null,
|
||
# "isTriggered": False,
|
||
# "slippagePercentage": "5"
|
||
# }
|
||
#
|
||
orderId = self.safe_string(order, 'orderId')
|
||
clientOrderId = self.safe_string(order, 'clientOrderId')
|
||
marketId = self.safe_string(order, 'symbol')
|
||
market = self.safe_market(marketId, market)
|
||
symbol = market['symbol']
|
||
price = self.safe_string(order, 'price')
|
||
amount = self.safe_string(order, 'origQty')
|
||
orderType = self.safe_string_lower(order, 'type')
|
||
status = self.safe_string(order, 'status')
|
||
side = self.safe_string_lower(order, 'side')
|
||
filled = self.omit_zero(self.safe_string(order, 'executedQty'))
|
||
average = self.omit_zero(self.safe_string(order, 'avgPrice'))
|
||
timeInForce = self.safe_string_lower(order, 'timeInForce')
|
||
takeProfitPrice: Str = None
|
||
triggerPrice: Str = None
|
||
if orderType is not None:
|
||
if orderType.find('take_profit') >= 0:
|
||
takeProfitPrice = self.safe_string(order, 'stopPrice')
|
||
else:
|
||
triggerPrice = self.safe_string(order, 'stopPrice')
|
||
timestamp = self.parse8601(self.safe_string(order, 'createdAt'))
|
||
lastTradeTimestamp = self.parse8601(self.safe_string(order, 'updatedAt'))
|
||
return self.safe_order({
|
||
'id': orderId,
|
||
'clientOrderId': clientOrderId,
|
||
'timestamp': timestamp,
|
||
'datetime': self.iso8601(timestamp),
|
||
'lastTradeTimestamp': lastTradeTimestamp,
|
||
'lastUpdateTimestamp': lastTradeTimestamp,
|
||
'status': self.parse_order_status(status),
|
||
'symbol': symbol,
|
||
'type': orderType,
|
||
'timeInForce': timeInForce,
|
||
'postOnly': self.safe_bool(order, 'postOnly'),
|
||
'reduceOnly': self.safe_bool(order, 'reduceOnly'),
|
||
'side': side,
|
||
'price': price,
|
||
'triggerPrice': triggerPrice,
|
||
'takeProfitPrice': takeProfitPrice,
|
||
'stopLossPrice': None,
|
||
'average': average,
|
||
'amount': amount,
|
||
'filled': filled,
|
||
'remaining': None,
|
||
'cost': None,
|
||
'trades': None,
|
||
'fee': {
|
||
'cost': self.safe_string(order, 'totalFee'),
|
||
'currency': 'USDC',
|
||
},
|
||
'info': order,
|
||
}, market)
|
||
|
||
async def cancel_order(self, id: str, symbol: Str = None, params={}):
|
||
"""
|
||
|
||
https://api-docs.defx.com/#09186f23-f8d1-4993-acf4-9974d8a6ddb0
|
||
|
||
cancels an open order
|
||
:param str id: order id
|
||
:param str symbol: unified symbol of the market the order was made in
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
request: dict = {
|
||
'orderId': id,
|
||
'idType': 'orderId',
|
||
}
|
||
clientOrderId = self.safe_string_n(params, ['clOrdID', 'clientOrderId', 'client_order_id'])
|
||
isByClientOrder = clientOrderId is not None
|
||
if isByClientOrder:
|
||
if symbol is None:
|
||
raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
|
||
market = self.market(symbol)
|
||
request['orderId'] = clientOrderId
|
||
request['idType'] = 'clientOrderId'
|
||
request['symbol'] = market['id']
|
||
params = self.omit(params, ['clOrdID', 'clientOrderId', 'client_order_id'])
|
||
response = await self.v1PrivateDeleteApiOrderOrderId(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "success": True
|
||
# }
|
||
#
|
||
extendParams: dict = {'symbol': symbol}
|
||
if isByClientOrder:
|
||
extendParams['clientOrderId'] = clientOrderId
|
||
else:
|
||
extendParams['id'] = id
|
||
return self.extend(self.parse_order(response), extendParams)
|
||
|
||
async def cancel_all_orders(self, symbol: Str = None, params={}):
|
||
"""
|
||
cancel all open orders
|
||
|
||
https://api-docs.defx.com/#db5531da-3692-4a53-841f-6ad6495f823a
|
||
|
||
:param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
request: dict = {
|
||
'symbols': [market['id']],
|
||
}
|
||
response = await self.v1PrivateDeleteApiOrdersAllOpen(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "data": {
|
||
# "msg": "The operation of cancel all open order is done."
|
||
# }
|
||
# }
|
||
#
|
||
return [self.safe_order({'info': response})]
|
||
|
||
async def fetch_position(self, symbol: str, params={}):
|
||
"""
|
||
fetch data on a single open contract trade position
|
||
|
||
https://api-docs.defx.com/#d89dbb86-9aba-4f59-ac5d-a97ff25ea80e
|
||
|
||
:param str symbol: unified market symbol of the market the position is held in, default is None
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: a `position structure <https://docs.ccxt.com/#/?id=position-structure>`
|
||
"""
|
||
if symbol is None:
|
||
raise ArgumentsRequired(self.id + ' fetchPosition() requires a symbol argument')
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
}
|
||
response = await self.v1PrivateGetApiPositionActive(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "data": [
|
||
# {
|
||
# "positionId": "0192c495-4a68-70ee-9081-9d368bd16dfc",
|
||
# "symbol": "SOL_USDC",
|
||
# "positionSide": "SHORT",
|
||
# "entryPrice": "172.34300000",
|
||
# "quantity": "0.80",
|
||
# "marginAmount": "20.11561173",
|
||
# "marginAsset": "USDC",
|
||
# "pnl": "0.00000000"
|
||
# }
|
||
# ]
|
||
# }
|
||
#
|
||
data = self.safe_list(response, 'data', [])
|
||
first = self.safe_dict(data, 0, {})
|
||
return self.parse_position(first, market)
|
||
|
||
async def fetch_positions(self, symbols: Strings = None, params={}) -> List[Position]:
|
||
"""
|
||
fetch all open positions
|
||
|
||
https://api-docs.defx.com/#d89dbb86-9aba-4f59-ac5d-a97ff25ea80e
|
||
|
||
:param str[] [symbols]: list of unified market symbols
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
response = await self.v1PrivateGetApiPositionActive(params)
|
||
#
|
||
# {
|
||
# "data": [
|
||
# {
|
||
# "positionId": "0192c495-4a68-70ee-9081-9d368bd16dfc",
|
||
# "symbol": "SOL_USDC",
|
||
# "positionSide": "SHORT",
|
||
# "entryPrice": "172.34300000",
|
||
# "quantity": "0.80",
|
||
# "marginAmount": "20.11561173",
|
||
# "marginAsset": "USDC",
|
||
# "pnl": "0.00000000"
|
||
# }
|
||
# ]
|
||
# }
|
||
#
|
||
positions = self.safe_list(response, 'data', [])
|
||
return self.parse_positions(positions, symbols)
|
||
|
||
def parse_position(self, position: dict, market: Market = None):
|
||
#
|
||
# {
|
||
# "positionId": "0192c495-4a68-70ee-9081-9d368bd16dfc",
|
||
# "symbol": "SOL_USDC",
|
||
# "positionSide": "SHORT",
|
||
# "entryPrice": "172.34300000",
|
||
# "quantity": "0.80",
|
||
# "marginAmount": "20.11561173",
|
||
# "marginAsset": "USDC",
|
||
# "pnl": "0.00000000"
|
||
# }
|
||
#
|
||
marketId = self.safe_string(position, 'symbol')
|
||
market = self.safe_market(marketId, market)
|
||
size = Precise.string_abs(self.safe_string(position, 'quantity'))
|
||
side = self.safe_string_lower(position, 'positionSide')
|
||
unrealisedPnl = self.omit_zero(self.safe_string(position, 'pnl'))
|
||
entryPrice = self.omit_zero(self.safe_string(position, 'entryPrice'))
|
||
initialMargin = self.safe_string(position, 'marginAmount')
|
||
return self.safe_position({
|
||
'info': position,
|
||
'id': self.safe_string(position, 'positionId'),
|
||
'symbol': market['symbol'],
|
||
'timestamp': None,
|
||
'datetime': None,
|
||
'lastUpdateTimestamp': None,
|
||
'initialMargin': self.parse_number(initialMargin),
|
||
'initialMarginPercentage': None,
|
||
'maintenanceMargin': None,
|
||
'maintenanceMarginPercentage': None,
|
||
'entryPrice': self.parse_number(entryPrice),
|
||
'notional': None,
|
||
'leverage': None,
|
||
'unrealizedPnl': self.parse_number(unrealisedPnl),
|
||
'realizedPnl': None,
|
||
'contracts': self.parse_number(size),
|
||
'contractSize': self.safe_number(market, 'contractSize'),
|
||
'marginRatio': None,
|
||
'liquidationPrice': None,
|
||
'markPrice': None,
|
||
'lastPrice': None,
|
||
'collateral': None,
|
||
'marginMode': None,
|
||
'side': side,
|
||
'percentage': None,
|
||
'stopLossPrice': None,
|
||
'takeProfitPrice': None,
|
||
'hedged': None,
|
||
})
|
||
|
||
async def fetch_order(self, id: str, symbol: Str = None, params={}):
|
||
"""
|
||
fetches information on an order made by the user
|
||
|
||
https://api-docs.defx.com/#44f82dd5-26b3-4e1f-b4aa-88ceddd65237
|
||
|
||
:param str id: the order id
|
||
:param str symbol: unified symbol of the market the order was made in
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
request: dict = {
|
||
'orderId': id,
|
||
'idType': 'orderId',
|
||
}
|
||
clientOrderId = self.safe_string_n(params, ['clOrdID', 'clientOrderId', 'client_order_id'])
|
||
params = self.omit(params, ['clOrdID', 'clientOrderId', 'client_order_id'])
|
||
if clientOrderId is not None:
|
||
if symbol is None:
|
||
raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument')
|
||
market = self.market(symbol)
|
||
request['orderId'] = clientOrderId
|
||
request['idType'] = 'clientOrderId'
|
||
request['symbol'] = market['id']
|
||
response = await self.v1PrivateGetApiOrderOrderId(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "success": True,
|
||
# "data": {
|
||
# "orderId": "555068654076559792",
|
||
# "createdAt": "2024-05-08T05:45:42.148Z",
|
||
# "updatedAt": "2024-05-08T05:45:42.166Z",
|
||
# "clientOrderId": "dummyClientOrderId",
|
||
# "reduceOnly": False,
|
||
# "side": "SELL",
|
||
# "status": "REJECTED",
|
||
# "symbol": "BTC_USDC",
|
||
# "timeInForce": "GTC",
|
||
# "type": "TAKE_PROFIT_MARKET",
|
||
# "origQty": "1.000",
|
||
# "executedQty": "0.000",
|
||
# "cumulativeQuote": "0.00",
|
||
# "avgPrice": "0.00",
|
||
# "stopPrice": "65000.00",
|
||
# "totalPnL": "0.00",
|
||
# "workingType": "MARK_PRICE",
|
||
# "postOnly": False
|
||
# }
|
||
# }
|
||
#
|
||
data = self.safe_dict(response, 'data')
|
||
return self.parse_order(data)
|
||
|
||
async def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
||
"""
|
||
fetches information on multiple orders made by the user
|
||
|
||
https://api-docs.defx.com/#ab200038-8acb-4170-b05e-4fcb4cc13751
|
||
|
||
:param str symbol: unified market symbol
|
||
:param int [since]: the earliest time in ms to fetch open orders for
|
||
:param int [limit]: the maximum number of open order 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 orders for
|
||
:returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
request: dict = {}
|
||
if symbol is not None:
|
||
market = self.market(symbol)
|
||
request['symbols'] = market['id']
|
||
until = self.safe_integer(params, 'until')
|
||
if until is not None:
|
||
params = self.omit(params, 'until')
|
||
request['end'] = self.iso8601(until)
|
||
if since is not None:
|
||
request['start'] = self.iso8601(since)
|
||
if limit is not None:
|
||
maxLimit = 100
|
||
limit = min(maxLimit, limit)
|
||
request['pageSize'] = limit
|
||
response = await self.v1PrivateGetApiOrders(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "data": [
|
||
# {
|
||
# "orderId": "746472647227344528",
|
||
# "createdAt": "2024-10-25T16:49:31.077Z",
|
||
# "updatedAt": "2024-10-25T16:49:31.378Z",
|
||
# "clientOrderId": "0192c495-49c3-71ee-b3d3-7442a2090807",
|
||
# "reduceOnly": False,
|
||
# "side": "SELL",
|
||
# "status": "FILLED",
|
||
# "symbol": "SOL_USDC",
|
||
# "timeInForce": "GTC",
|
||
# "type": "MARKET",
|
||
# "origQty": "0.80",
|
||
# "executedQty": "0.80",
|
||
# "cumulativeQuote": "137.87440000",
|
||
# "avgPrice": "172.34300000",
|
||
# "totalPnL": "0.00000000",
|
||
# "totalFee": "0.07583092",
|
||
# "workingType": null,
|
||
# "postOnly": False,
|
||
# "linkedOrderParentType": null,
|
||
# "isTriggered": False,
|
||
# "slippagePercentage": 5
|
||
# }
|
||
# ]
|
||
# }
|
||
#
|
||
data = self.safe_list(response, 'data', [])
|
||
return self.parse_orders(data, None, since, limit)
|
||
|
||
async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
||
"""
|
||
fetch all unfilled currently open orders
|
||
|
||
https://api-docs.defx.com/#ab200038-8acb-4170-b05e-4fcb4cc13751
|
||
|
||
:param str symbol: unified market symbol
|
||
:param int [since]: the earliest time in ms to fetch open orders for
|
||
:param int [limit]: the maximum number of open order 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 orders for
|
||
:returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
req = {
|
||
'statuses': 'OPEN',
|
||
}
|
||
return await self.fetch_orders(symbol, since, limit, self.extend(req, params))
|
||
|
||
async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
||
"""
|
||
fetches information on multiple closed orders made by the user
|
||
|
||
https://api-docs.defx.com/#ab200038-8acb-4170-b05e-4fcb4cc13751
|
||
|
||
:param str symbol: unified market symbol
|
||
:param int [since]: the earliest time in ms to fetch open orders for
|
||
:param int [limit]: the maximum number of open order 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 orders for
|
||
:returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
req = {
|
||
'statuses': 'FILLED',
|
||
}
|
||
return await self.fetch_orders(symbol, since, limit, self.extend(req, params))
|
||
|
||
async def fetch_canceled_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
||
"""
|
||
fetches information on multiple canceled orders made by the user
|
||
|
||
https://api-docs.defx.com/#ab200038-8acb-4170-b05e-4fcb4cc13751
|
||
|
||
:param str symbol: unified market symbol
|
||
:param int [since]: the earliest time in ms to fetch open orders for
|
||
:param int [limit]: the maximum number of open order 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 orders for
|
||
:returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
req = {
|
||
'statuses': 'CANCELED',
|
||
}
|
||
return await self.fetch_orders(symbol, since, limit, self.extend(req, params))
|
||
|
||
async def close_position(self, symbol: str, side: OrderSide = None, params={}) -> Order:
|
||
"""
|
||
closes an open position for a market
|
||
|
||
https://api-docs.defx.com/#b2c08074-c4d9-4e50-b637-0d6c498fa29e
|
||
|
||
:param str symbol: unified CCXT market symbol
|
||
:param str [side]: one-way mode: 'buy' or 'sell', hedge-mode: 'long' or 'short'
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:param str [params.positionId]: the position id you want to close
|
||
:param str [params.type]: 'MARKET' or 'LIMIT'
|
||
:param str [params.quantity]: how much of currency you want to trade in units of base currency
|
||
:param str [params.price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
|
||
:returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
positionId = self.safe_string(params, 'positionId')
|
||
if positionId is None:
|
||
raise ArgumentsRequired(self.id + ' closePosition() requires a positionId')
|
||
type = self.safe_string_upper(params, 'type')
|
||
if type is None:
|
||
raise ArgumentsRequired(self.id + ' closePosition() requires a type')
|
||
quantity = self.safe_string(params, 'quantity')
|
||
if quantity is None:
|
||
raise ArgumentsRequired(self.id + ' closePosition() requires a quantity')
|
||
request: dict = {
|
||
'positionId': positionId,
|
||
'type': type,
|
||
'quantity': quantity,
|
||
}
|
||
if type != 'MARKET':
|
||
price = self.safe_string(params, 'price')
|
||
if price is None:
|
||
raise ArgumentsRequired(self.id + ' closePosition() requires a price')
|
||
request['price'] = price
|
||
params = self.omit(params, ['positionId', 'type', 'quantity', 'price'])
|
||
response = await self.v1PrivateDeleteApiPositionPositionId(self.extend(request, params))
|
||
#
|
||
# {}
|
||
#
|
||
return response
|
||
|
||
async def close_all_positions(self, params={}) -> List[Position]:
|
||
"""
|
||
closes all open positions for a market type
|
||
|
||
https://api-docs.defx.com/#d6f63b43-100e-47a9-998c-8b6c0c72d204
|
||
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict[]: A list of `position structures <https://docs.ccxt.com/#/?id=position-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
response = await self.v1PrivateDeleteApiPositionAll(params)
|
||
#
|
||
# {
|
||
# "data": [
|
||
# {
|
||
# "positionId": "d6ca1a27-28ad-47ae-b244-0bda5ac37b2b",
|
||
# "success": True
|
||
# }
|
||
# ]
|
||
# }
|
||
#
|
||
data = self.safe_list(response, 'data', [])
|
||
return self.parse_positions(data, None, params)
|
||
|
||
async def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[LedgerEntry]:
|
||
"""
|
||
fetch the history of changes, actions done by the user or operations that altered the balance of the user
|
||
|
||
https://api-docs.defx.com/#38cc8974-794f-48c0-b959-db045a0ee565
|
||
|
||
:param str [code]: unified currency code
|
||
:param int [since]: timestamp in ms of the earliest ledger entry
|
||
:param int [limit]: max number of ledger entries to return
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:param int [params.until]: timestamp in ms of the latest ledger entry
|
||
:param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
|
||
:returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger>`
|
||
"""
|
||
await self.load_markets()
|
||
paginate = False
|
||
paginate, params = self.handle_option_and_params(params, 'fetchLedger', 'paginate')
|
||
if paginate:
|
||
return await self.fetch_paginated_call_dynamic('fetchLedger', code, since, limit, params)
|
||
request: dict = {}
|
||
if since is not None:
|
||
request['start'] = since
|
||
else:
|
||
request['start'] = 0
|
||
until = self.safe_integer(params, 'until')
|
||
if until is not None:
|
||
params = self.omit(params, 'until')
|
||
request['end'] = until
|
||
else:
|
||
request['end'] = self.milliseconds()
|
||
response = await self.v1PrivateGetApiWalletTransactions(self.extend(request, params))
|
||
data = self.safe_list(response, 'transactions', [])
|
||
return self.parse_ledger(data, None, since, limit)
|
||
|
||
def parse_ledger_entry(self, item: dict, currency: Currency = None) -> LedgerEntry:
|
||
#
|
||
# {
|
||
# "id": "01JCSZS6H5VQND3GF5P98SJ29C",
|
||
# "timestamp": 1731744012054,
|
||
# "type": "FundingFee",
|
||
# "amount": "0.02189287",
|
||
# "asset": "USDC",
|
||
# "operation": "CREDIT"
|
||
# }
|
||
#
|
||
amount = self.safe_string(item, 'amount')
|
||
currencyId = self.safe_string(item, 'asset')
|
||
code = self.safe_currency_code(currencyId, currency)
|
||
currency = self.safe_currency(currencyId, currency)
|
||
timestamp = self.safe_integer(item, 'timestamp')
|
||
type = self.safe_string(item, 'type')
|
||
return self.safe_ledger_entry({
|
||
'info': item,
|
||
'id': self.safe_string(item, 'id'),
|
||
'direction': None,
|
||
'account': None,
|
||
'referenceAccount': None,
|
||
'referenceId': None,
|
||
'type': self.parse_ledger_entry_type(type),
|
||
'currency': code,
|
||
'amount': self.parse_number(amount),
|
||
'timestamp': timestamp,
|
||
'datetime': self.iso8601(timestamp),
|
||
'before': None,
|
||
'after': None,
|
||
'status': None,
|
||
'fee': None,
|
||
}, currency)
|
||
|
||
def parse_ledger_entry_type(self, type):
|
||
ledgerType: dict = {
|
||
'FundingFee': 'fee',
|
||
'FeeRebate': 'fee',
|
||
'FeeKickback': 'fee',
|
||
'RealizedPnl': 'trade',
|
||
'LiquidationClearance': 'trade',
|
||
'Transfer': 'transfer',
|
||
'ReferralPayout': 'referral',
|
||
'Commission': 'commission',
|
||
}
|
||
return self.safe_string(ledgerType, type, type)
|
||
|
||
async def withdraw(self, code: str, amount: float, address: str, tag: Str = None, params={}) -> Transaction:
|
||
"""
|
||
make a withdrawal
|
||
|
||
https://api-docs.defx.com/#2600f503-63ed-4672-b8f6-69ea5f03203b
|
||
|
||
:param str code: unified currency code
|
||
:param float amount: the amount to withdraw
|
||
:param str address: the address to withdraw to
|
||
:param str tag:
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
currency = self.currency(code)
|
||
request: dict = {
|
||
'amount': self.currency_to_precision(code, amount),
|
||
'asset': currency['id'],
|
||
# 'network': 'ARB_SEPOLIA',
|
||
# 'chainId': '421614',
|
||
}
|
||
response = await self.v1PrivatePostApiTransfersBridgeWithdrawal(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "transactionId": "0x301e5851e5aefa733abfbc8b30817ca3b61601e0ddf1df8c59656fb888b0bc9c"
|
||
# }
|
||
#
|
||
return self.parse_transaction(response, currency)
|
||
|
||
def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
|
||
#
|
||
# withdraw
|
||
#
|
||
# {
|
||
# "transactionId": "0x301e5851e5aefa733abfbc8b30817ca3b61601e0ddf1df8c59656fb888b0bc9c"
|
||
# }
|
||
#
|
||
txid = self.safe_string(transaction, 'transactionId')
|
||
return {
|
||
'info': transaction,
|
||
'id': None,
|
||
'txid': txid,
|
||
'timestamp': None,
|
||
'datetime': None,
|
||
'network': None,
|
||
'address': None,
|
||
'addressTo': None,
|
||
'addressFrom': None,
|
||
'tag': None,
|
||
'tagTo': None,
|
||
'tagFrom': None,
|
||
'type': None,
|
||
'amount': None,
|
||
'currency': self.safe_currency_code(None, currency),
|
||
'status': None,
|
||
'updated': None,
|
||
'internal': None,
|
||
'comment': None,
|
||
'fee': None,
|
||
}
|
||
|
||
async def set_leverage(self, leverage: int, symbol: Str = None, params={}):
|
||
"""
|
||
set the level of leverage for a market
|
||
|
||
https://api-docs.defx.com/#4cb4ecc4-6c61-4194-8353-be67faaf7ca7
|
||
|
||
:param float leverage: the rate of leverage
|
||
:param str symbol: unified market symbol
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: response from the exchange
|
||
"""
|
||
if symbol is None:
|
||
raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument')
|
||
await self.load_markets()
|
||
request: dict = {
|
||
'leverage': self.number_to_string(leverage),
|
||
}
|
||
market = self.market(symbol)
|
||
request['symbol'] = market['id']
|
||
response = await self.v1PrivatePostApiUsersMetadataLeverage(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "success": True,
|
||
# "data": {
|
||
# "leverage": "11",
|
||
# "symbol": "BTC_USDC"
|
||
# }
|
||
# }
|
||
#
|
||
data = self.safe_dict(response, 'data', {})
|
||
return self.parse_leverage(data, market)
|
||
|
||
def parse_leverage(self, leverage: dict, market: Market = None) -> Leverage:
|
||
#
|
||
# "data": {
|
||
# "leverage": "11",
|
||
# "symbol": "BTC_USDC"
|
||
# }
|
||
#
|
||
marketId = self.safe_string(leverage, 'symbol')
|
||
leverageValue = self.safe_integer(leverage, 'leverage')
|
||
return {
|
||
'info': leverage,
|
||
'symbol': self.safe_symbol(marketId, market),
|
||
'marginMode': None,
|
||
'longLeverage': leverageValue,
|
||
'shortLeverage': leverageValue,
|
||
}
|
||
|
||
def nonce(self):
|
||
return self.milliseconds()
|
||
|
||
def sign(self, path, section='public', method='GET', params={}, headers=None, body=None):
|
||
version = section[0]
|
||
access = section[1]
|
||
pathWithParams = self.implode_params(path, params)
|
||
url = self.implode_hostname(self.urls['api'][access])
|
||
url += '/' + version + '/'
|
||
params = self.omit(params, self.extract_params(path))
|
||
params = self.keysort(params)
|
||
if access == 'public':
|
||
url += 'open/' + pathWithParams
|
||
if params:
|
||
url += '?' + self.rawencode(params)
|
||
else:
|
||
self.check_required_credentials()
|
||
headers = {'X-DEFX-SOURCE': 'ccxt'}
|
||
url += 'auth/' + pathWithParams
|
||
nonce = str(self.milliseconds())
|
||
payload = nonce
|
||
if method == 'GET' or path == 'api/order/{orderId}':
|
||
payload += self.rawencode(params)
|
||
if params:
|
||
url += '?' + self.rawencode(params)
|
||
else:
|
||
if params is not None:
|
||
body = self.json(params)
|
||
payload += body
|
||
headers['Content-Type'] = 'application/json'
|
||
signature = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha256)
|
||
headers['X-DEFX-APIKEY'] = self.apiKey
|
||
headers['X-DEFX-TIMESTAMP'] = nonce
|
||
headers['X-DEFX-SIGNATURE'] = signature
|
||
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 not response:
|
||
return None # fallback to default error handler
|
||
# {"errorCode":404,"errorMessage":"Not Found"}
|
||
# {"msg":"Missing auth signature","code":"missing_auth_signature"}
|
||
# {"success":false,"err":{"msg":"Invalid order id","code":"invalid_order_id"}}
|
||
success = self.safe_bool(response, 'success')
|
||
err = self.safe_dict(response, 'err', response)
|
||
errorCode = self.safe_string_2(err, 'errorCode', 'code')
|
||
if not success:
|
||
feedback = self.id + ' ' + self.json(response)
|
||
self.throw_broadly_matched_exception(self.exceptions['broad'], body, feedback)
|
||
self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
|
||
return None
|
||
|
||
def default_network_code_for_currency(self, code):
|
||
currencyItem = self.currency(code)
|
||
networks = currencyItem['networks']
|
||
networkKeys = list(networks.keys())
|
||
for i in range(0, len(networkKeys)):
|
||
network = networkKeys[i]
|
||
if network == 'ETH':
|
||
return network
|
||
# if it was not returned according to above options, then return the first network of currency
|
||
return self.safe_value(networkKeys, 0)
|
||
|
||
def set_sandbox_mode(self, enable: bool):
|
||
super(defx, self).set_sandbox_mode(enable)
|
||
self.options['sandboxMode'] = enable
|