3630 lines
167 KiB
Python
3630 lines
167 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.base.exchange import Exchange
|
|
from ccxt.abstract.weex import ImplicitAPI
|
|
import hashlib
|
|
from ccxt.base.types import Any, Balances, Currencies, Currency, Int, LedgerEntry, Leverage, Leverages, MarginMode, MarginModes, MarginModification, Market, Num, Order, OrderBook, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade, TradingFeeInterface, 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 BadSymbol
|
|
from ccxt.base.errors import InsufficientFunds
|
|
from ccxt.base.errors import InvalidOrder
|
|
from ccxt.base.errors import OrderNotFound
|
|
from ccxt.base.errors import NotSupported
|
|
from ccxt.base.decimal_to_precision import TICK_SIZE
|
|
from ccxt.base.precise import Precise
|
|
|
|
|
|
class weex(Exchange, ImplicitAPI):
|
|
|
|
def describe(self) -> Any:
|
|
return self.deep_extend(super(weex, self).describe(), {
|
|
'id': 'weex',
|
|
'name': 'Weex',
|
|
'countries': ['SG'], # Singapore
|
|
'rateLimit': 20, # 10 requests per second for public endpoints, 500 requests per 10 seconds for private endpoints
|
|
'version': 'v3',
|
|
'certified': False,
|
|
'pro': True,
|
|
'has': {
|
|
'CORS': None,
|
|
'spot': True,
|
|
'margin': False,
|
|
'swap': True,
|
|
'future': False,
|
|
'option': False,
|
|
'addMargin': True,
|
|
'borrowCrossMargin': False,
|
|
'borrowIsolatedMargin': False,
|
|
'borrowMargin': False,
|
|
'cancelAllOrders': True,
|
|
'cancelOrder': True,
|
|
'cancelOrders': True,
|
|
'cancelOrdersWithClientOrderId': True,
|
|
'cancelOrderWithClientOrderId': True,
|
|
'closeAllPositions': True,
|
|
'closePosition': True,
|
|
'createDepositAddress': False,
|
|
'createLimitBuyOrder': True,
|
|
'createLimitOrder': True,
|
|
'createLimitSellOrder': True,
|
|
'createMarketBuyOrder': True,
|
|
'createMarketBuyOrderWithCost': False,
|
|
'createMarketOrder': True,
|
|
'createMarketOrderWithCost': False,
|
|
'createMarketSellOrder': True,
|
|
'createMarketSellOrderWithCost': False,
|
|
'createOrder': True,
|
|
'createOrders': False,
|
|
'createOrderWithTakeProfitAndStopLoss': True,
|
|
'createPostOnlyOrder': False,
|
|
'createReduceOnlyOrder': True,
|
|
'createStopLimitOrder': False,
|
|
'createStopLossOrder': True,
|
|
'createTakeProfitOrder': True,
|
|
'createTrailingAmountOrder': False,
|
|
'createTrailingPercentOrder': False,
|
|
'createTriggerOrder': False,
|
|
'deposit': False,
|
|
'editOrder': False,
|
|
'editOrders': False,
|
|
'editOrderWithClientOrderId': False,
|
|
'fetchAccounts': False,
|
|
'fetchADLRank': False,
|
|
'fetchBalance': True,
|
|
'fetchBidsAsks': True,
|
|
'fetchBorrowInterest': False,
|
|
'fetchBorrowRate': False,
|
|
'fetchBorrowRateHistories': False,
|
|
'fetchBorrowRateHistory': False,
|
|
'fetchBorrowRates': False,
|
|
'fetchBorrowRatesPerSymbol': False,
|
|
'fetchCanceledAndClosedOrders': True, # contracts only
|
|
'fetchCanceledOrders': True,
|
|
'fetchClosedOrder': False,
|
|
'fetchClosedOrders': True,
|
|
'fetchConvertCurrencies': False,
|
|
'fetchConvertQuote': False,
|
|
'fetchConvertTrade': False,
|
|
'fetchConvertTradeHistory': False,
|
|
'fetchCrossBorrowRate': False,
|
|
'fetchCrossBorrowRates': False,
|
|
'fetchCurrencies': True,
|
|
'fetchDeposit': False,
|
|
'fetchDepositAddress': False,
|
|
'fetchDepositAddresses': False,
|
|
'fetchDepositAddressesByNetwork': False,
|
|
'fetchDeposits': False,
|
|
'fetchDepositsWithdrawals': False,
|
|
'fetchDepositWithdrawFee': False,
|
|
'fetchDepositWithdrawFees': False,
|
|
'fetchFundingHistory': False,
|
|
'fetchFundingInterval': False,
|
|
'fetchFundingIntervals': False,
|
|
'fetchFundingRate': True,
|
|
'fetchFundingRateHistory': True,
|
|
'fetchFundingRates': True,
|
|
'fetchGreeks': False,
|
|
'fetchIndexOHLCV': True,
|
|
'fetchIsolatedBorrowRate': False,
|
|
'fetchIsolatedBorrowRates': False,
|
|
'fetchIsolatedPositions': False,
|
|
'fetchL2OrderBook': False,
|
|
'fetchL3OrderBook': False,
|
|
'fetchLastPrices': False,
|
|
'fetchLedger': True,
|
|
'fetchLedgerEntry': False,
|
|
'fetchLeverage': True,
|
|
'fetchLeverages': True,
|
|
'fetchLeverageTiers': False,
|
|
'fetchLiquidations': False,
|
|
'fetchLongShortRatio': False,
|
|
'fetchLongShortRatioHistory': False,
|
|
'fetchMarginAdjustmentHistory': False,
|
|
'fetchMarginMode': True,
|
|
'fetchMarginModes': True,
|
|
'fetchMarketLeverageTiers': False,
|
|
'fetchMarkets': True,
|
|
'fetchMarkOHLCV': True,
|
|
'fetchMarkPrices': False,
|
|
'fetchMyLiquidations': False,
|
|
'fetchMySettlementHistory': False,
|
|
'fetchMyTrades': True,
|
|
'fetchOHLCV': True,
|
|
'fetchOpenInterest': True,
|
|
'fetchOpenInterestHistory': False,
|
|
'fetchOpenInterests': False,
|
|
'fetchOpenOrder': False,
|
|
'fetchOpenOrders': True,
|
|
'fetchOption': False,
|
|
'fetchOptionChain': False,
|
|
'fetchOrder': True,
|
|
'fetchOrderBook': True,
|
|
'fetchOrderBooks': False,
|
|
'fetchOrders': True, # spot only
|
|
'fetchOrdersByStatus': False,
|
|
'fetchOrderTrades': True,
|
|
'fetchOrderWithClientOrderId': True, # spot only
|
|
'fetchPosition': True,
|
|
'fetchPositionADLRank': False,
|
|
'fetchPositionHistory': False,
|
|
'fetchPositionMode': True,
|
|
'fetchPositions': True,
|
|
'fetchPositionsADLRank': False,
|
|
'fetchPositionsForSymbol': True,
|
|
'fetchPositionsHistory': False,
|
|
'fetchPositionsRisk': False,
|
|
'fetchPremiumIndexOHLCV': False,
|
|
'fetchSettlementHistory': False,
|
|
'fetchStatus': True,
|
|
'fetchTicker': True,
|
|
'fetchTickers': True,
|
|
'fetchTime': True,
|
|
'fetchTrades': True,
|
|
'fetchTradingFee': True,
|
|
'fetchTradingFees': False,
|
|
'fetchTradingLimits': False,
|
|
'fetchTransactionFee': False,
|
|
'fetchTransactionFees': False,
|
|
'fetchTransactions': False,
|
|
'fetchTransfer': False,
|
|
'fetchTransfers': True,
|
|
'fetchUnderlyingAssets': False,
|
|
'fetchVolatilityHistory': False,
|
|
'fetchWithdrawAddresses': False,
|
|
'fetchWithdrawal': False,
|
|
'fetchWithdrawals': False,
|
|
'fetchWithdrawalWhitelist': False,
|
|
'privateAPI': False,
|
|
'publicAPI': False,
|
|
'reduceMargin': True,
|
|
'repayCrossMargin': False,
|
|
'repayIsolatedMargin': False,
|
|
'sandbox': False,
|
|
'setLeverage': True,
|
|
'setMargin': False,
|
|
'setMarginMode': True,
|
|
'setPositionMode': True,
|
|
'signIn': False,
|
|
'transfer': False,
|
|
'withdraw': False,
|
|
},
|
|
'urls': {
|
|
'logo': 'https://github.com/user-attachments/assets/ccbadb2d-5035-403d-898f-dce831bdc936', # todo
|
|
'api': {
|
|
'public': 'https://api-spot.weex.com',
|
|
'private': 'https://api-spot.weex.com',
|
|
'contract': 'https://api-contract.weex.com',
|
|
'contractPrivate': 'https://api-contract.weex.com',
|
|
},
|
|
'www': 'https://www.weex.com',
|
|
'doc': [
|
|
'https://www.weex.com/api-doc',
|
|
],
|
|
'referral': 'https://www.weex.com/register?vipCode=qfyh',
|
|
},
|
|
'api': {
|
|
'public': {
|
|
# multiply public endpoints weight by 5
|
|
'get': {
|
|
'api/v3/time': 5, # done
|
|
'api/v3/coins': 25, # done
|
|
'api/v3/exchangeInfo': 100, # done
|
|
'api/v3/ping': 5, # done
|
|
'api/v3/apiTradingSymbols': 25, # not unified
|
|
'api/v3/market/ticker/price': 20, # not unified
|
|
'api/v3/market/ticker/24hr': 10, # done
|
|
'api/v3/market/trades': 125, # done
|
|
'api/v3/market/klines': 10, # done
|
|
'api/v3/market/depth': 25, # done
|
|
'api/v3/market/ticker/bookTicker': 20, # done
|
|
},
|
|
},
|
|
'private': {
|
|
'get': {
|
|
'api/v3/account/': 5, # done
|
|
'api/v3/account/transferRecords': 3, # done
|
|
'api/v3/order': 2, # done
|
|
'api/v3/openOrders': 3, # done
|
|
'api/v3/allOrders': 10, # done
|
|
'api/v3/myTrades': 5, # done
|
|
'api/v3/rebate/affiliate/getAffiliateUIDs': 20, # not unified
|
|
'api/v3/rebate/affiliate/getChannelUserTradeAndAsset': 20, # not unified
|
|
'api/v3/rebate/affiliate/getAffiliateCommission': 20, # not unified
|
|
'api/v3/rebate/affiliate/getInternalWithdrawalStatus': 100, # not unified
|
|
'api/v3/rebate/affiliate/querySubChannelTransactions': 10, # not unified
|
|
'api/v3/agency/verifyReferrals': 20, # not unified
|
|
'api/v3/agency/getAssert': 20, # not unified
|
|
'api/v3/agency/getDealData': 20, # not unified
|
|
},
|
|
'post': {
|
|
'api/v3/account/bills': 5, # done
|
|
'api/v3/account/fundingBills': 5, # done
|
|
'api/v3/order': 5, # done
|
|
'api/v3/order/batch': 50, # not supported, returns {"code":-1150,"msg":"Request method 'POST' not supported"}
|
|
'api/v3/rebate/affiliate/internalWithdrawal': 100, # not unified
|
|
},
|
|
'delete': {
|
|
'api/v3/order': 1, # done
|
|
'api/v3/openOrders': 1, # done
|
|
'api/v3/order/batch': 10, # done
|
|
},
|
|
},
|
|
'contract': {
|
|
# multiply public endpoints weight by 5
|
|
'get': {
|
|
'capi/v3/market/time': 5, # done
|
|
'capi/v3/market/exchangeInfo': 5, # done
|
|
'capi/v3/market/depth': 5, # done
|
|
'capi/v3/market/ticker/24hr': 200, # done
|
|
'capi/v3/market/ticker/bookTicker': 5, # done
|
|
'capi/v3/market/trades': 25, # done
|
|
'capi/v3/market/klines': 5, # done
|
|
'capi/v3/market/indexPriceKlines': 5, # done
|
|
'capi/v3/market/markPriceKlines': 5, # done
|
|
'capi/v3/market/historyKlines': 25, # done
|
|
'capi/v3/market/symbolPrice': 5, # not unified
|
|
'capi/v3/market/openInterest': 10, # done
|
|
'capi/v3/market/premiumIndex': 5, # done
|
|
'capi/v3/market/fundingRate': 25, # done
|
|
'capi/v3/market/apiTradingSymbols': 25, # not unified
|
|
},
|
|
},
|
|
'contractPrivate': {
|
|
'get': {
|
|
'capi/v3/account/balance': 10, # done
|
|
'capi/v3/account/commissionRate': 10, # done
|
|
'capi/v3/account/accountConfig': 10, # not unified
|
|
'capi/v3/account/symbolConfig': 10, # done
|
|
'capi/v3/account/position/allPosition': 15, # done
|
|
'capi/v3/account/position/singlePosition': 3, # done
|
|
'capi/v3/order': 3, # done
|
|
'capi/v3/openOrders': 5, # done
|
|
'capi/v3/order/history': 10, # done
|
|
'capi/v3/userTrades': 5, # done
|
|
'capi/v3/openAlgoOrders': 3, # done
|
|
'capi/v3/allAlgoOrders': 10, # not unified - capi/v3/order/history returns both regular and algo orders
|
|
},
|
|
'post': {
|
|
'capi/v3/account/income': 5, # done
|
|
'capi/v3/account/marginType': 50, # done
|
|
'capi/v3/account/leverage': 20, # done
|
|
'capi/v3/account/positionMargin': 30, # done
|
|
'capi/v3/account/modifyAutoAppendMargin': 30, # not unified
|
|
'capi/v3/order': 5, # done
|
|
'capi/v3/batchOrders': 10, # not supported, returns {"code":-1150,"msg":"Request method 'POST' not supported"}
|
|
'capi/v3/closePositions': 50, # done
|
|
'capi/v3/algoOrder': 5, # done
|
|
'capi/v3/placeTpSlOrder': 5, # not unified
|
|
'capi/v3/modifyTpSlOrder': 5, # not unified
|
|
},
|
|
'delete': {
|
|
'capi/v3/order': 3, # done
|
|
'capi/v3/batchOrders': 10, # done
|
|
'capi/v3/allOpenOrders': 10, # done
|
|
'capi/v3/algoOrder': 3, # done
|
|
'capi/v3/algoOpenOrders': 10, # done
|
|
},
|
|
},
|
|
},
|
|
'requiredCredentials': {
|
|
'apiKey': True,
|
|
'secret': True,
|
|
'password': True,
|
|
},
|
|
'timeframes': {
|
|
'1m': '1m',
|
|
'5m': '5m',
|
|
'15m': '15m',
|
|
'30m': '30m',
|
|
'1h': '1h',
|
|
'2h': '2h',
|
|
'4h': '4h',
|
|
'6h': '6h',
|
|
'8h': '8h',
|
|
'12h': '12h',
|
|
'1d': '1d',
|
|
'1w': '1w',
|
|
'1M': '1M',
|
|
},
|
|
'precisionMode': TICK_SIZE,
|
|
'exceptions': {
|
|
'exact': {
|
|
'-1000': ExchangeError, # UNKNOWN_ERROR An unknown error occurred.
|
|
'-1054': ExchangeError, # SYSTEM_ERROR System error, please retry later.
|
|
'-1040': AuthenticationError, # ACCESS_KEY_EMPTY ACCESS_KEY header is required.
|
|
'-1041': AuthenticationError, # ACCESS_SIGN_EMPTY ACCESS_SIGN header is required.
|
|
'-1042': AuthenticationError, # ACCESS_TIMESTAMP_EMPTY ACCESS_TIMESTAMP header is required.
|
|
'-1043': AuthenticationError, # INVALID_ACCESS_TIMESTAMP Invalid ACCESS_TIMESTAMP.
|
|
'-1044': AuthenticationError, # INVALID_ACCESS_KEY Invalid ACCESS_KEY.
|
|
'-1045': BadRequest, # INVALID_CONTENT_TYPE Invalid Content-Type, please use application/json.
|
|
'-1046': BadRequest, # ACCESS_TIMESTAMP_EXPIRED Request timestamp expired.
|
|
'-1047': AuthenticationError, # API_AUTH_ERROR API authentication failed.
|
|
'-1049': AuthenticationError, # API_KEY_OR_PASSPHRASE_INCORRECT API key or passphrase incorrect.
|
|
'-1050': PermissionDenied, # USER_STATUS_FORBIDDEN User status is abnormal.
|
|
'-1051': PermissionDenied, # PERMISSION_DENIED Permission denied.
|
|
'-1052': PermissionDenied, # INSUFFICIENT_PERMISSIONS Insufficient permissions for self action.
|
|
'-1053': PermissionDenied, # PERMISSION_VALIDATION_FAILED Permission validation failed.
|
|
'-1055': PermissionDenied, # USER_AUTH_NOT_SAFE User must bind phone or Google authenticator.
|
|
'-1056': PermissionDenied, # ILLEGAL_IP Invalid IP address.
|
|
'-1057': PermissionDenied, # USER_LOCKED User account is locked.
|
|
'-1058': PermissionDenied, # NO_PERMISSION_TRADE_PAIR No permission for self trading pair.
|
|
'-1115': InvalidOrder, # INVALID_TIME_IN_FORCE Invalid timeInForce.
|
|
'-1116': InvalidOrder, # INVALID_ORDER_TYPE Invalid order type.
|
|
'-1117': InvalidOrder, # INVALID_SIDE Invalid side.
|
|
'-1121': BadSymbol, # INVALID_SYMBOL Invalid symbol.
|
|
'-1128': BadRequest, # INVALID_PARAM_COMBINATION Combination of optional parameters invalid.
|
|
'-1135': BadRequest, # INVALID_JSON Invalid JSON request.
|
|
'-1140': BadRequest, # PARAM_VALIDATE_ERROR Parameter validation failed. limit must be between and .
|
|
'-1141': ArgumentsRequired, # PARAM_EMPTY Parameter cannot be empty.
|
|
'-1142': BadRequest, # PARAM_ERROR Parameter is invalid.
|
|
'-1150': BadRequest, # REQUEST_METHOD_NOT_SUPPORTED Request method not supported.
|
|
'-1160': BadRequest, # DECIMAL_PRECISION_ERROR Decimal precision error.
|
|
'-1170': BadRequest, # QUERY_TIME_OUT_OF_RANGE startTime must be within the last days. Time range cannot exceed days.
|
|
'-1171': BadRequest, # START_TIME_AFTER_END_TIME startTime cannot be greater than endTime.
|
|
'-1180': InvalidOrder, # CLIENT_OID_LENGTH_ERROR client_oid length must not exceed 40 and must not contain special characters.
|
|
'-1190': PermissionDenied, # FORBIDDEN_ACCESS Access forbidden. Please contact support.
|
|
'-2007': BadSymbol, # SPOT_SYMBOL_NOT_EXIST Symbol does not exist.
|
|
'-2200': OrderNotFound, # SPOT_ORDER_NOT_EXIST Order does not exist.
|
|
'-3006': InvalidOrder, # CONTRACT_DOES_NOT_SUPPORT_CONTRACT_UNITS Contract does not support ordering by contract units.
|
|
'-3007': InvalidOrder, # CONTRACT_MAX_ORDER_QUANTITY_EXCEEDED Maximum contract order quantity exceeded.
|
|
'-3200': InvalidOrder, # CONTRACT_ORDER_NOT_EXIST Order does not exist.
|
|
'-3235': PermissionDenied, # CONTRACT_NO_PERMISSION_TRADE_PAIR No permission for self trading pair.
|
|
'-3236': PermissionDenied, # CONTRACT_NO_PERMISSION_API No permission to access self API.
|
|
'-3313': InvalidOrder, # CONTRACT_LEVERAGE_ERROR Leverage exceeds maximum limit.
|
|
'-3613': ExchangeError, # CONTRACT_FATAL_TOKEN_NOT_SUPPORT Fatal: token ID not supported for symbol.
|
|
'FAILED_ORDER_NOT_FOUND': OrderNotFound, # {"orderId":121231,"status":"FAILED","errorMsg":"FAILED_ORDER_NOT_FOUND"}
|
|
},
|
|
'broad': {
|
|
'amount not enough': InsufficientFunds, # {"code":-1054,"msg":"FAILED_PRECONDITION: Move margin available amount not enough. Move out available amount is 6.98296375, move out amount is 200.00000000"}
|
|
'INVALID_ARGUMENT': BadRequest, # {"result":false,"id":1,"msg":"INVALID_ARGUMENT: invalid symbol : ASDFS_SPBL"}
|
|
},
|
|
},
|
|
'fees': {
|
|
'trading': {
|
|
'feeSide': 'get',
|
|
'tierBased': True,
|
|
'percentage': True,
|
|
'taker': self.parse_number('0.1'),
|
|
'maker': self.parse_number('0.1'),
|
|
'tiers': {
|
|
'taker': [
|
|
[self.parse_number('0'), self.parse_number('0.1')],
|
|
[self.parse_number('500000'), self.parse_number('0.09')],
|
|
[self.parse_number('1000000'), self.parse_number('0.08')],
|
|
[self.parse_number('2000000'), self.parse_number('0.06')],
|
|
[self.parse_number('5000000'), self.parse_number('0.05')],
|
|
[self.parse_number('10000000'), self.parse_number('0.04')],
|
|
[self.parse_number('25000000'), self.parse_number('0.03')],
|
|
[self.parse_number('50000000'), self.parse_number('0.02')],
|
|
[self.parse_number('100000000'), self.parse_number('0')],
|
|
],
|
|
'maker': [
|
|
[self.parse_number('0'), self.parse_number('0.1')],
|
|
[self.parse_number('500000'), self.parse_number('0.08')],
|
|
[self.parse_number('1000000'), self.parse_number('0.07')],
|
|
[self.parse_number('2000000'), self.parse_number('0.05')],
|
|
[self.parse_number('5000000'), self.parse_number('0.04')],
|
|
[self.parse_number('10000000'), self.parse_number('0.03')],
|
|
[self.parse_number('25000000'), self.parse_number('0.02')],
|
|
[self.parse_number('50000000'), self.parse_number('0.01')],
|
|
[self.parse_number('100000000'), self.parse_number('0')],
|
|
],
|
|
},
|
|
},
|
|
'spot': {
|
|
'feeSide': 'get',
|
|
'tierBased': True,
|
|
'percentage': True,
|
|
'taker': self.parse_number('0.1'),
|
|
'maker': self.parse_number('0.1'),
|
|
'tiers': {
|
|
'taker': [
|
|
[self.parse_number('0'), self.parse_number('0.1')],
|
|
[self.parse_number('500000'), self.parse_number('0.09')],
|
|
[self.parse_number('1000000'), self.parse_number('0.08')],
|
|
[self.parse_number('2000000'), self.parse_number('0.06')],
|
|
[self.parse_number('5000000'), self.parse_number('0.05')],
|
|
[self.parse_number('10000000'), self.parse_number('0.04')],
|
|
[self.parse_number('25000000'), self.parse_number('0.03')],
|
|
[self.parse_number('50000000'), self.parse_number('0.02')],
|
|
[self.parse_number('100000000'), self.parse_number('0')],
|
|
],
|
|
'maker': [
|
|
[self.parse_number('0'), self.parse_number('0.1')],
|
|
[self.parse_number('500000'), self.parse_number('0.08')],
|
|
[self.parse_number('1000000'), self.parse_number('0.07')],
|
|
[self.parse_number('2000000'), self.parse_number('0.05')],
|
|
[self.parse_number('5000000'), self.parse_number('0.04')],
|
|
[self.parse_number('10000000'), self.parse_number('0.03')],
|
|
[self.parse_number('25000000'), self.parse_number('0.02')],
|
|
[self.parse_number('50000000'), self.parse_number('0.01')],
|
|
[self.parse_number('100000000'), self.parse_number('0')],
|
|
],
|
|
},
|
|
},
|
|
'contract': {
|
|
'feeSide': 'quote',
|
|
'tierBased': True,
|
|
'percentage': True,
|
|
'taker': self.parse_number('0.08'),
|
|
'maker': self.parse_number('0.02'),
|
|
'tiers': {
|
|
'taker': [
|
|
[self.parse_number('0'), self.parse_number('0.08')],
|
|
[self.parse_number('1000000'), self.parse_number('0.075')],
|
|
[self.parse_number('5000000'), self.parse_number('0.06')],
|
|
[self.parse_number('10000000'), self.parse_number('0.055')],
|
|
[self.parse_number('30000000'), self.parse_number('0.05')],
|
|
[self.parse_number('50000000'), self.parse_number('0.048')],
|
|
[self.parse_number('100000000'), self.parse_number('0.045')],
|
|
[self.parse_number('300000000'), self.parse_number('0.042')],
|
|
[self.parse_number('500000000'), self.parse_number('0.04')],
|
|
],
|
|
'maker': [
|
|
[self.parse_number('0'), self.parse_number('0.02')],
|
|
[self.parse_number('1000000'), self.parse_number('0.02')],
|
|
[self.parse_number('5000000'), self.parse_number('0.018')],
|
|
[self.parse_number('10000000'), self.parse_number('0.018')],
|
|
[self.parse_number('30000000'), self.parse_number('0.016')],
|
|
[self.parse_number('50000000'), self.parse_number('0.016')],
|
|
[self.parse_number('100000000'), self.parse_number('0.014')],
|
|
[self.parse_number('300000000'), self.parse_number('0.012')],
|
|
[self.parse_number('500000000'), self.parse_number('0.01')],
|
|
],
|
|
},
|
|
},
|
|
},
|
|
'commonCurrencies': {
|
|
'XBT': 'XBT',
|
|
},
|
|
'options': {
|
|
'partner': 'b-WEEX111125',
|
|
'timeDifference': 0, # the difference between system clock and Binance clock
|
|
'adjustForTimeDifference': False, # controls the adjustment logic upon instantiation
|
|
'accountsByType': {
|
|
'spot': 'spot',
|
|
'trading': 'spot',
|
|
'fund': 'funding',
|
|
'funding': 'funding',
|
|
'swap': 'contract',
|
|
'contract': 'contract',
|
|
'futures': 'contract',
|
|
},
|
|
'networks': {
|
|
'BEP20': 'BEP20(BSC)',
|
|
'BSC': 'BEP20(BSC)',
|
|
'ERC20': 'ERC20',
|
|
'ETH': 'ERC20',
|
|
'POLYGON': 'POLYGON(MATIC)',
|
|
'MATIC': 'POLYGON(MATIC)',
|
|
'ARBITRUM': 'ARBITRUM(ARB)',
|
|
'ARB': 'ARBITRUM(ARB)',
|
|
'SOLANA': 'SOLANA(SOL)',
|
|
'SOL': 'SOLANA(SOL)',
|
|
'OP': 'OPTIMISM(OP)',
|
|
'OPTIMISM': 'OPTIMISM(OP)',
|
|
'AVALANCHEC': 'AVALANCHE_C(AVAX_C)',
|
|
'AVAXC': 'AVALANCHE_C(AVAX_C)',
|
|
},
|
|
'networksById': {
|
|
'BEP20(BSC)': 'BEP20',
|
|
'ERC20': 'ERC20',
|
|
'POLYGON(MATIC)': 'MATIC',
|
|
'ARBITRUM(ARB)': 'ARB',
|
|
'SOLANA(SOL)': 'SOL',
|
|
'OPTIMISM(OP)': 'OP',
|
|
'AVALANCHE_C(AVAX_C)': 'AVAXC',
|
|
},
|
|
'timeframes': {
|
|
'spot': {
|
|
'1m': '1m',
|
|
'5m': '5m',
|
|
'15m': '15m',
|
|
'30m': '30m',
|
|
'1h': '1h',
|
|
'2h': '2h',
|
|
'4h': '4h',
|
|
'6h': '6h',
|
|
'8h': '8h',
|
|
'12h': '12h',
|
|
'1d': '1d',
|
|
'1w': '1w',
|
|
'1M': '1M',
|
|
},
|
|
'contract': {
|
|
'1m': '1m',
|
|
'5m': '5m',
|
|
'15m': '15m',
|
|
'30m': '30m',
|
|
'1h': '1h',
|
|
'4h': '4h',
|
|
'12h': '12h',
|
|
'1d': '1d',
|
|
'1w': '1w',
|
|
},
|
|
},
|
|
},
|
|
'features': {
|
|
'spot': {
|
|
'sandbox': False,
|
|
'createOrder': {
|
|
'test': False,
|
|
'marginMode': False,
|
|
'triggerPrice': False,
|
|
'triggerPriceType': None,
|
|
'triggerDirection': None,
|
|
'stopLossPrice': False,
|
|
'takeProfitPrice': False,
|
|
'attachedStopLossTakeProfit': None,
|
|
'timeInForce': {
|
|
'IOC': True,
|
|
'FOK': True,
|
|
'PO': False,
|
|
'GTD': False,
|
|
},
|
|
'hedged': False,
|
|
'trailing': False,
|
|
'leverage': False,
|
|
'marketBuyByCost': False,
|
|
'marketBuyRequiresPrice': False,
|
|
'selfTradePrevention': False,
|
|
'iceberg': False,
|
|
},
|
|
'createOrders': {
|
|
'max': 10,
|
|
},
|
|
'fetchMyTrades': {
|
|
'marginMode': False,
|
|
'limit': 100,
|
|
'daysBack': None,
|
|
'untilDays': None,
|
|
'symbolRequired': True,
|
|
},
|
|
'fetchOrder': {
|
|
'marginMode': False,
|
|
'trigger': False,
|
|
'trailing': False,
|
|
'symbolRequired': False,
|
|
},
|
|
'fetchOpenOrders': {
|
|
'marginMode': False,
|
|
'limit': 100,
|
|
'trigger': False,
|
|
'trailing': False,
|
|
'symbolRequired': False,
|
|
},
|
|
'fetchOrders': {
|
|
'marginMode': False,
|
|
'limit': 1000,
|
|
'daysBack': 100000,
|
|
'untilDays': 100000,
|
|
'trigger': False,
|
|
'trailing': False,
|
|
'symbolRequired': True,
|
|
},
|
|
'fetchClosedOrders': None,
|
|
'fetchOHLCV': {
|
|
'limit': 300,
|
|
},
|
|
},
|
|
'forDerivs': {
|
|
'sandbox': False,
|
|
'createOrder': {
|
|
'marginMode': True,
|
|
'triggerPrice': False,
|
|
'triggerPriceType': None,
|
|
'triggerDirection': False,
|
|
'stopLossPrice': True,
|
|
'takeProfitPrice': True,
|
|
'attachedStopLossTakeProfit': {
|
|
'triggerPriceType': {
|
|
'last': True,
|
|
'mark': True,
|
|
'index': False,
|
|
},
|
|
'price': False,
|
|
},
|
|
'timeInForce': {
|
|
'IOC': True,
|
|
'FOK': True,
|
|
'PO': False,
|
|
'GTD': False,
|
|
},
|
|
'hedged': False,
|
|
'trailing': False,
|
|
'leverage': False,
|
|
'marketBuyByCost': False,
|
|
'marketBuyRequiresPrice': False,
|
|
'selfTradePrevention': False,
|
|
'iceberg': False,
|
|
},
|
|
'createOrders': {
|
|
'max': 10,
|
|
},
|
|
'fetchMyTrades': {
|
|
'marginMode': False,
|
|
'limit': 100,
|
|
'daysBack': None,
|
|
'untilDays': None,
|
|
'symbolRequired': False,
|
|
},
|
|
'fetchOrder': {
|
|
'marginMode': False,
|
|
'trigger': False,
|
|
'trailing': False,
|
|
'symbolRequired': False,
|
|
},
|
|
'fetchOpenOrders': {
|
|
'marginMode': False,
|
|
'limit': 100,
|
|
'trigger': True,
|
|
'trailing': False,
|
|
'symbolRequired': False,
|
|
},
|
|
'fetchOrders': None,
|
|
'fetchCanceledAndClosedOrders': {
|
|
'marginMode': False,
|
|
'limit': 1000,
|
|
'daysBack': 100000,
|
|
'untilDays': 100000,
|
|
'trigger': False,
|
|
'trailing': False,
|
|
'symbolRequired': False,
|
|
},
|
|
'fetchClosedOrders': None,
|
|
'fetchOHLCV': {
|
|
'limit': 1000, # 100 for historical
|
|
},
|
|
},
|
|
'swap': {
|
|
'linear': {
|
|
'extends': 'forDerivs',
|
|
},
|
|
'inverse': None,
|
|
},
|
|
},
|
|
})
|
|
|
|
def nonce(self):
|
|
return self.milliseconds() - self.options['timeDifference']
|
|
|
|
def fetch_status(self, params={}):
|
|
"""
|
|
the latest known information on the availability of the exchange API
|
|
|
|
https://www.weex.com/api-doc/spot/ConfigAPI/Ping
|
|
|
|
: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 = self.publicGetApiV3Ping(params)
|
|
# reutns an empty response if the exchange is alive, otherwise will trigger an error
|
|
return {
|
|
'status': 'ok',
|
|
'updated': None,
|
|
'eta': None,
|
|
'url': None,
|
|
'info': response,
|
|
}
|
|
|
|
def fetch_time(self, params={}) -> Int:
|
|
"""
|
|
fetches the current integer timestamp in milliseconds from the exchange server
|
|
|
|
https://www.weex.com/api-doc/spot/ConfigAPI/GetServerTime
|
|
https://www.weex.com/api-doc/contract/Market_API/GetServerTime
|
|
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.type]: 'spot' or 'swap', default is 'spot'
|
|
:returns int: the current integer timestamp in milliseconds from the exchange server
|
|
"""
|
|
type = None
|
|
type, params = self.handle_market_type_and_params('fetchTime', None, params)
|
|
response = None
|
|
if type != 'spot':
|
|
response = self.contractGetCapiV3MarketTime(params)
|
|
else:
|
|
response = self.publicGetApiV3Time(params)
|
|
#
|
|
# {
|
|
# "serverTime": 1764505776347
|
|
# }
|
|
#
|
|
return self.safe_integer(response, 'serverTime')
|
|
|
|
def fetch_currencies(self, params={}) -> Currencies:
|
|
"""
|
|
fetches all available currencies on an exchange
|
|
|
|
https://www.weex.com/api-doc/spot/ConfigAPI/CurrencyInfo
|
|
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: an associative dictionary of currencies
|
|
"""
|
|
response = self.publicGetApiV3Coins(params)
|
|
#
|
|
# [
|
|
# {
|
|
# "coin": "BTC",
|
|
# "depositAllEnable": True,
|
|
# "withdrawAllEnable": True,
|
|
# "name": "BTC",
|
|
# "networkList": [
|
|
# {
|
|
# "network": "BTC",
|
|
# "coin": "BTC",
|
|
# "withdrawIntegerMultiple": 1E-8,
|
|
# "isDefault": True,
|
|
# "depositEnable": True,
|
|
# "withdrawEnable": True,
|
|
# "depositDesc": null,
|
|
# "withdrawDesc": null,
|
|
# "name": "BTC",
|
|
# "withdrawFee": "0.00016",
|
|
# "withdrawMin": "0.002",
|
|
# "depositDust": "0.00001",
|
|
# "minConfirm": 3,
|
|
# "withdrawTag": False,
|
|
# "contractAddressUrl": "https://www.blockchain.com/explorer/mempool/",
|
|
# "contractAddress": "btc"
|
|
# },
|
|
# {
|
|
# "network": "BEP20(BSC)",
|
|
# "coin": "BTC",
|
|
# "withdrawIntegerMultiple": 1E-8,
|
|
# "isDefault": False,
|
|
# "depositEnable": True,
|
|
# "withdrawEnable": False,
|
|
# "depositDesc": null,
|
|
# "withdrawDesc": null,
|
|
# "name": "BEP20(BSC)",
|
|
# "withdrawFee": "0.00001",
|
|
# "withdrawMin": "0.00006",
|
|
# "depositDust": "0.00003",
|
|
# "minConfirm": 61,
|
|
# "withdrawTag": False,
|
|
# "contractAddressUrl": "",
|
|
# "contractAddress": ""
|
|
# }
|
|
# ]
|
|
# },
|
|
# {
|
|
# "coin": "USDT",
|
|
# "depositAllEnable": True,
|
|
# "withdrawAllEnable": True,
|
|
# "name": "USDT",
|
|
# "networkList": [
|
|
# {
|
|
# "network": "TRC20",
|
|
# "coin": "USDT",
|
|
# "withdrawIntegerMultiple": 1E-8,
|
|
# "isDefault": True,
|
|
# "depositEnable": True,
|
|
# "withdrawEnable": True,
|
|
# "depositDesc": null,
|
|
# "withdrawDesc": null,
|
|
# "name": "TRC20",
|
|
# "withdrawFee": "1.5",
|
|
# "withdrawMin": "10",
|
|
# "depositDust": "0.1",
|
|
# "minConfirm": 20,
|
|
# "withdrawTag": False,
|
|
# "contractAddressUrl": "https://tronscan.org/#/token20/",
|
|
# "contractAddress": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"
|
|
# },
|
|
# {
|
|
# "network": "ERC20",
|
|
# "coin": "USDT",
|
|
# "withdrawIntegerMultiple": 1E-8,
|
|
# "isDefault": False,
|
|
# "depositEnable": True,
|
|
# "withdrawEnable": True,
|
|
# "depositDesc": null,
|
|
# "withdrawDesc": null,
|
|
# "name": "ERC20",
|
|
# "withdrawFee": "1",
|
|
# "withdrawMin": "20",
|
|
# "depositDust": "0.1",
|
|
# "minConfirm": 12,
|
|
# "withdrawTag": False,
|
|
# "contractAddressUrl": "https://etherscan.io/token/",
|
|
# "contractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7"
|
|
# },
|
|
# {
|
|
# "network": "AVALANCHE_C(AVAX_C)",
|
|
# "coin": "USDT",
|
|
# "withdrawIntegerMultiple": 1E-8,
|
|
# "isDefault": False,
|
|
# "depositEnable": True,
|
|
# "withdrawEnable": True,
|
|
# "depositDesc": null,
|
|
# "withdrawDesc": null,
|
|
# "name": "AVALANCHE_C(AVAX_C)",
|
|
# "withdrawFee": "0.5",
|
|
# "withdrawMin": "10",
|
|
# "depositDust": "0.1",
|
|
# "minConfirm": 35,
|
|
# "withdrawTag": False,
|
|
# "contractAddressUrl": "https://avascan.info/blockchain/c/token/",
|
|
# "contractAddress": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7"
|
|
# }
|
|
# ]
|
|
# }
|
|
# ]
|
|
#
|
|
result: dict = {}
|
|
for i in range(0, len(response)):
|
|
currency = self.safe_dict(response, i)
|
|
currencyId = self.safe_string(currency, 'coin')
|
|
code = self.safe_currency_code(currencyId)
|
|
name = self.safe_string(currency, 'name')
|
|
networks: dict = {}
|
|
chains = self.safe_list(currency, 'networkList', [])
|
|
for j in range(0, len(chains)):
|
|
chain = self.safe_dict(chains, j)
|
|
networkId = self.safe_string(chain, 'network')
|
|
networkCode = self.network_id_to_code(networkId)
|
|
networks[networkCode] = {
|
|
'info': chain,
|
|
'id': networkId,
|
|
'network': networkCode,
|
|
'active': None,
|
|
'deposit': self.safe_bool(chain, 'depositEnable'),
|
|
'withdraw': self.safe_bool(chain, 'withdrawEnable'),
|
|
'fee': self.safe_number(chain, 'withdrawFee'),
|
|
'precision': self.safe_number(chain, 'withdrawIntegerMultiple'),
|
|
'isDefault': self.safe_bool(chain, 'isDefault', False),
|
|
'limits': {
|
|
'withdraw': {
|
|
'min': self.safe_number(chain, 'withdrawMin'),
|
|
'max': None,
|
|
},
|
|
'deposit': {
|
|
'min': self.safe_number(chain, 'depositDust'),
|
|
'max': None,
|
|
},
|
|
},
|
|
}
|
|
networkKeys = list(networks.keys())
|
|
networksLength = len(networkKeys)
|
|
emptyChains = networksLength == 0 # non-functional coins
|
|
valueForEmpty = False if emptyChains else None
|
|
result[code] = self.safe_currency_structure({
|
|
'info': currency,
|
|
'code': code,
|
|
'id': currencyId,
|
|
'type': 'crypto',
|
|
'name': name,
|
|
'active': None,
|
|
'deposit': valueForEmpty,
|
|
'withdraw': valueForEmpty,
|
|
'fee': None,
|
|
'precision': None,
|
|
'limits': {
|
|
'amount': {
|
|
'min': None,
|
|
'max': None,
|
|
},
|
|
'withdraw': {
|
|
'min': None,
|
|
'max': None,
|
|
},
|
|
'deposit': {
|
|
'min': None,
|
|
'max': None,
|
|
},
|
|
},
|
|
'networks': networks,
|
|
})
|
|
return result
|
|
|
|
def fetch_markets(self, params={}) -> List[Market]:
|
|
"""
|
|
retrieves data on all markets for exchagne
|
|
|
|
https://www.weex.com/api-doc/spot/ConfigAPI/GetProductInfo # spot
|
|
https://www.weex.com/api-doc/contract/Market_API/GetContractInfo # contract
|
|
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict[]: an array of objects representing market data
|
|
"""
|
|
if self.options['adjustForTimeDifference']:
|
|
self.load_time_difference()
|
|
promises = [
|
|
self.publicGetApiV3ExchangeInfo(params),
|
|
self.contractGetCapiV3MarketExchangeInfo(params),
|
|
]
|
|
spotResponse, contractResponse = promises
|
|
spotArray = self.safe_list(spotResponse, 'symbols', [])
|
|
contractArray = self.safe_list(contractResponse, 'symbols', [])
|
|
result = self.array_concat(spotArray, contractArray)
|
|
return self.parse_markets(result)
|
|
|
|
def parse_market(self, market: dict) -> Market:
|
|
#
|
|
# spot
|
|
# {
|
|
# "symbol": "ETHUSDT",
|
|
# "status": "TRADING",
|
|
# "baseAsset": "ETH",
|
|
# "baseAssetPrecision": "8",
|
|
# "quoteAsset": "USDT",
|
|
# "quoteAssetPrecision": "8",
|
|
# "tickSize": "0.01",
|
|
# "stepSize": "0.00001",
|
|
# "minTradeAmount": "0.0001",
|
|
# "maxTradeAmount": "99999",
|
|
# "takerFeeRate": "0.001",
|
|
# "makerFeeRate": "0.001",
|
|
# "buyLimitPriceRatio": "0.1",
|
|
# "sellLimitPriceRatio": "0.1",
|
|
# "marketBuyLimitSize": "99999",
|
|
# "marketSellLimitSize": "99999",
|
|
# "marketFallbackPriceRatio": "0",
|
|
# "enableTrade": True,
|
|
# "enableDisplay": True,
|
|
# "displayDigitMerge": "0.01,0.1,0.5,1,5",
|
|
# "displayNew": False,
|
|
# "displayHot": False
|
|
# }
|
|
#
|
|
# contract
|
|
# {
|
|
# "symbol": "ETHUSDT",
|
|
# "baseAsset": "ETH",
|
|
# "quoteAsset": "USDT",
|
|
# "marginAsset": "USDT",
|
|
# "pricePrecision": "2",
|
|
# "quantityPrecision": "3",
|
|
# "baseAssetPrecision": "2",
|
|
# "quotePrecision": "8",
|
|
# "contractVal": "0.001",
|
|
# "delivery": [
|
|
# "00:00:00",
|
|
# "08:00:00",
|
|
# "16:00:00"
|
|
# ],
|
|
# "forwardContractFlag": True,
|
|
# "minLeverage": "1",
|
|
# "maxLeverage": "400",
|
|
# "buyLimitPriceRatio": "0.01",
|
|
# "sellLimitPriceRatio": "0.01",
|
|
# "makerFeeRate": "0.0002",
|
|
# "takerFeeRate": "0.0008",
|
|
# "minOrderSize": "0.001",
|
|
# "maxOrderSize": "1000000",
|
|
# "maxPositionSize": "5000000",
|
|
# "marketOpenLimitSize": "2300"
|
|
# }
|
|
#
|
|
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)
|
|
active = True
|
|
symbol = base + '/' + quote
|
|
isSpot = True
|
|
isLinear = None
|
|
isInverse = None
|
|
if settle is not None:
|
|
symbol += ':' + settle
|
|
isSpot = False
|
|
if settle == quote:
|
|
isLinear = True
|
|
isInverse = False
|
|
elif settle == base:
|
|
isLinear = False
|
|
isInverse = True
|
|
else:
|
|
active = self.safe_bool(market, 'enableTrade')
|
|
amountPrecision = self.safe_number(market, 'stepSize')
|
|
pricePrecision = self.safe_number(market, 'tickSize')
|
|
if amountPrecision is None:
|
|
amountPrecisionString = self.parse_precision(self.safe_string(market, 'quantityPrecision'))
|
|
pricePrecisionString = self.parse_precision(self.safe_string(market, 'pricePrecision'))
|
|
amountPrecision = self.parse_number(amountPrecisionString)
|
|
pricePrecision = self.parse_number(pricePrecisionString)
|
|
fees = self.safe_dict(self.fees, 'spot' if isSpot else 'contract', {})
|
|
return self.safe_market_structure({
|
|
'id': id,
|
|
'lowercaseId': id.lower(),
|
|
'numericId': self.safe_integer(market, 'contractId'),
|
|
'symbol': symbol,
|
|
'base': base,
|
|
'quote': quote,
|
|
'settle': settle,
|
|
'baseId': baseId,
|
|
'quoteId': quoteId,
|
|
'settleId': settleId,
|
|
'type': 'spot' if isSpot else 'swap',
|
|
'spot': isSpot,
|
|
'margin': False,
|
|
'swap': not isSpot,
|
|
'future': False,
|
|
'option': False,
|
|
'active': active,
|
|
'contract': not isSpot,
|
|
'linear': isLinear,
|
|
'inverse': isInverse,
|
|
'taker': self.safe_number(market, 'takerFeeRate'),
|
|
'maker': self.safe_number(market, 'makerFeeRate'),
|
|
'feeSide': fees['feeSide'],
|
|
'contractSize': self.safe_number(market, 'contractVal'),
|
|
'expiry': None,
|
|
'expiryDatetime': None,
|
|
'strike': None,
|
|
'optionType': None,
|
|
'precision': {
|
|
'amount': amountPrecision,
|
|
'price': pricePrecision,
|
|
},
|
|
'limits': {
|
|
'leverage': {
|
|
'min': self.safe_number(market, 'minLeverage'),
|
|
'max': self.safe_number(market, 'maxLeverage'),
|
|
},
|
|
'amount': {
|
|
'min': self.safe_number_2(market, 'minTradeAmount', 'minOrderSize'),
|
|
'max': self.safe_number_2(market, 'maxTradeAmount', 'maxOrderSize'),
|
|
},
|
|
'price': {
|
|
'min': None,
|
|
'max': None,
|
|
},
|
|
'cost': {
|
|
'min': None,
|
|
'max': None,
|
|
},
|
|
},
|
|
'created': None,
|
|
'percentage': fees['percentage'],
|
|
'tierBased': fees['tierBased'],
|
|
'tiers': fees['tiers'],
|
|
'info': market,
|
|
})
|
|
|
|
def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
|
|
"""
|
|
fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
|
|
|
|
https://www.weex.com/api-doc/spot/MarketDataAPI/GetAllTickerInfo # spot
|
|
https://www.weex.com/api-doc/contract/Market_API/GetTicker24h # contract
|
|
|
|
:param str symbols: unified symbol of the market to fetch the ticker for
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.type]: 'spot' or 'swap', default is 'spot'(used if symbols are not provided)
|
|
:returns dict: a `ticker structure <https://docs.ccxt.com/?id=ticker-structure>`
|
|
"""
|
|
self.load_markets()
|
|
symbols = self.market_symbols(symbols, None, True, True)
|
|
market = self.get_market_from_symbols(symbols)
|
|
marketType = None
|
|
marketType, params = self.handle_market_type_and_params('fetchTickers', market, params)
|
|
symbolsLength = 0
|
|
if symbols is not None:
|
|
symbolsLength = len(symbols)
|
|
request: dict = {}
|
|
if symbolsLength == 1:
|
|
request['symbol'] = market['id']
|
|
response = None
|
|
if marketType == 'spot':
|
|
#
|
|
# [
|
|
# {
|
|
# "symbol": "ETHUSDT",
|
|
# "priceChange": "-72.98",
|
|
# "priceChangePercent": "-0.033811",
|
|
# "lastPrice": "2085.46",
|
|
# "bidPrice": "2085.44",
|
|
# "bidQty": "1.53848",
|
|
# "askPrice": "2085.47",
|
|
# "askQty": "1.87504",
|
|
# "openPrice": "2158.44",
|
|
# "highPrice": "2168.40",
|
|
# "lowPrice": "2061.12",
|
|
# "volume": "157359.56105",
|
|
# "quoteVolume": "331284305.7193626",
|
|
# "openTime": 1775493000000,
|
|
# "closeTime": 1775579400000,
|
|
# "count": 59727
|
|
# }
|
|
# ]
|
|
#
|
|
response = self.publicGetApiV3MarketTicker24hr(self.extend(request, params))
|
|
else:
|
|
#
|
|
# [
|
|
# {
|
|
# "symbol": "ETHUSDT",
|
|
# "priceChange": "-75.49",
|
|
# "priceChangePercent": "-0.034992",
|
|
# "lastPrice": "2081.80",
|
|
# "openPrice": "2157.29",
|
|
# "highPrice": "2167.51",
|
|
# "lowPrice": "2059.17",
|
|
# "volume": "623160.426",
|
|
# "quoteVolume": "1310647345.19346",
|
|
# "openTime": 1775493000000,
|
|
# "closeTime": 1775579400000,
|
|
# "markPrice": "2081.8",
|
|
# "indexPrice": "2082.75"
|
|
# }
|
|
# ]
|
|
#
|
|
response = self.contractGetCapiV3MarketTicker24hr(self.extend(request, params))
|
|
if not isinstance(response, list):
|
|
response = [response]
|
|
return self.parse_tickers(response, symbols)
|
|
|
|
def fetch_bids_asks(self, symbols: Strings = None, params={}):
|
|
"""
|
|
fetches the bid and ask price and volume for multiple markets
|
|
|
|
https://www.weex.com/api-doc/spot/MarketDataAPI/GetBookTicker # spot
|
|
https://www.weex.com/api-doc/contract/Market_API/GetBookTicker # contract
|
|
|
|
:param str[]|None symbols: unified symbols of the markets to fetch the bids and asks for, all markets are returned if not assigned
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.type]: 'spot' or 'swap', default is 'spot'(used if symbols are not provided)
|
|
:returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/?id=ticker-structure>`
|
|
"""
|
|
symbols = self.market_symbols(symbols, None, True, True)
|
|
market = self.get_market_from_symbols(symbols)
|
|
marketType = None
|
|
marketType, params = self.handle_market_type_and_params('fetchTickers', market, params)
|
|
response = None
|
|
if marketType == 'spot':
|
|
response = self.publicGetApiV3MarketTickerBookTicker(params)
|
|
else:
|
|
response = self.contractGetCapiV3MarketTickerBookTicker(params)
|
|
if not isinstance(response, list):
|
|
response = [response]
|
|
return self.parse_tickers(response, symbols)
|
|
|
|
def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
|
|
#
|
|
# spot
|
|
# {
|
|
# "symbol": "ETHUSDT",
|
|
# "priceChange": "-72.98",
|
|
# "priceChangePercent": "-0.033811",
|
|
# "lastPrice": "2085.46",
|
|
# "bidPrice": "2085.44",
|
|
# "bidQty": "1.53848",
|
|
# "askPrice": "2085.47",
|
|
# "askQty": "1.87504",
|
|
# "openPrice": "2158.44",
|
|
# "highPrice": "2168.40",
|
|
# "lowPrice": "2061.12",
|
|
# "volume": "157359.56105",
|
|
# "quoteVolume": "331284305.7193626",
|
|
# "openTime": 1775493000000,
|
|
# "closeTime": 1775579400000,
|
|
# "count": 59727
|
|
# }
|
|
#
|
|
# swap
|
|
# {
|
|
# "symbol": "ETHUSDT",
|
|
# "priceChange": "-75.49",
|
|
# "priceChangePercent": "-0.034992",
|
|
# "lastPrice": "2081.80",
|
|
# "openPrice": "2157.29",
|
|
# "highPrice": "2167.51",
|
|
# "lowPrice": "2059.17",
|
|
# "volume": "623160.426",
|
|
# "quoteVolume": "1310647345.19346",
|
|
# "openTime": 1775493000000,
|
|
# "closeTime": 1775579400000,
|
|
# "markPrice": "2081.8",
|
|
# "indexPrice": "2082.75"
|
|
# }
|
|
#
|
|
marketId = self.safe_string(ticker, 'symbol')
|
|
markPrice = self.safe_string(ticker, 'markPrice')
|
|
marketType = 'spot'
|
|
if markPrice is not None:
|
|
marketType = 'swap'
|
|
market = self.safe_market(marketId, market, None, marketType)
|
|
timestamp = self.safe_integer_2(ticker, 'closeTime', 'time')
|
|
return self.safe_ticker({
|
|
'symbol': market['symbol'],
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'high': self.safe_string(ticker, 'highPrice'),
|
|
'low': self.safe_string(ticker, 'lowPrice'),
|
|
'bid': self.safe_string(ticker, 'bidPrice'),
|
|
'bidVolume': self.safe_string(ticker, 'bidQty'),
|
|
'ask': self.safe_string(ticker, 'askPrice'),
|
|
'askVolume': self.safe_string(ticker, 'askQty'),
|
|
'vwap': None,
|
|
'open': self.safe_string(ticker, 'openPrice'),
|
|
'close': self.safe_string(ticker, 'lastPrice'),
|
|
'last': self.safe_string(ticker, 'lastPrice'),
|
|
'previousClose': None,
|
|
'change': self.safe_string(ticker, 'priceChange'),
|
|
'percentage': self.safe_string(ticker, 'priceChangePercent'),
|
|
'average': None,
|
|
'baseVolume': self.safe_string(ticker, 'volume'),
|
|
'quoteVolume': self.safe_string(ticker, 'quoteVolume'),
|
|
'markPrice': markPrice,
|
|
'indexPrice': self.safe_string(ticker, 'indexPrice'),
|
|
'info': ticker,
|
|
}, 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://www.weex.com/api-doc/spot/MarketDataAPI/GetDepthData # spot
|
|
https://www.weex.com/api-doc/contract/Market_API/GetDepthData # contract
|
|
|
|
:param str symbol: unified symbol of the market to fetch the order book for
|
|
:param int [limit]: the maximum amount of order book entries to return(default 15, max 200)
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/?id=order-book-structure>` indexed by market symbols
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'symbol': market['id'],
|
|
}
|
|
if (limit is not None) and (limit > 15):
|
|
request['limit'] = 200 # default is 15, max is 200
|
|
response = None
|
|
if market['spot']:
|
|
response = self.publicGetApiV3MarketDepth(self.extend(request, params))
|
|
else:
|
|
response = self.contractGetCapiV3MarketDepth(self.extend(request, params))
|
|
#
|
|
# {
|
|
# "asks": [
|
|
# [
|
|
# "2096.77",
|
|
# "45.592"
|
|
# ]
|
|
# ],
|
|
# "bids": [
|
|
# [
|
|
# "2096.76",
|
|
# "49.162"
|
|
# ]
|
|
# ],
|
|
# "lastUpdateId": 14138610208
|
|
# }
|
|
#
|
|
orderbook = self.parse_order_book(response, symbol)
|
|
orderbook['nonce'] = self.safe_integer(response, 'lastUpdateId')
|
|
return orderbook
|
|
|
|
def fetch_ohlcv(self, symbol: str, timeframe: str = '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://www.weex.com/api-doc/spot/MarketDataAPI/GetKLineData # spot
|
|
https://www.weex.com/api-doc/contract/Market_API/GetKlines # contract last price
|
|
https://www.weex.com/api-doc/contract/Market_API/GetIndexPriceKlines # contract index price
|
|
https://www.weex.com/api-doc/contract/Market_API/GetMarkPriceKlines # contract mark price
|
|
https://www.weex.com/api-doc/contract/Market_API/GetHistoryKlines # contract historical klines
|
|
|
|
: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(default 100, max 300)
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
Check fetchSpotOHLCV() and fetchContractOHLCV() for more details on the extra parameters that can be used in params
|
|
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
if market['spot']:
|
|
return self.fetch_spot_ohlcv(symbol, timeframe, since, limit, params)
|
|
else:
|
|
return self.fetch_contract_ohlcv(symbol, timeframe, since, limit, params)
|
|
|
|
def fetch_spot_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
|
"""
|
|
@ignore
|
|
helper method for fetchOHLCV
|
|
|
|
https://www.weex.com/api-doc/spot/MarketDataAPI/GetKLineData
|
|
|
|
: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
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'symbol': market['id'],
|
|
'interval': self.safe_string(self.timeframes, timeframe, timeframe),
|
|
}
|
|
response = self.publicGetApiV3MarketKlines(self.extend(request, params))
|
|
return self.parse_ohlcvs(response, market, timeframe, since, limit)
|
|
|
|
def fetch_contract_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
|
"""
|
|
@ignore
|
|
helper method for fetchOHLCV
|
|
|
|
https://www.weex.com/api-doc/contract/Market_API/GetKlines # contract last price
|
|
https://www.weex.com/api-doc/contract/Market_API/GetIndexPriceKlines # contract index price
|
|
https://www.weex.com/api-doc/contract/Market_API/GetMarkPriceKlines # contract mark price
|
|
https://www.weex.com/api-doc/contract/Market_API/GetHistoryKlines # contract historical klines
|
|
|
|
: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(default 100, max 100 for historical klines, max 1000 for other contract klines)
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param int [params.until]: timestamp in ms of the latest candle to fetch
|
|
:param boolean [params.paginate]: whether to automatically paginate requests until the required number of candles is returned
|
|
:param boolean [params.historical]: whether to fetch historical klines(default is False). If False, will fetch last price klines
|
|
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
|
"""
|
|
self.load_markets()
|
|
maxHistoricalLimit = 100
|
|
paginate = False
|
|
paginate, params = self.handle_option_and_params(params, 'fetchOHLCV', 'paginate')
|
|
if paginate:
|
|
params = self.extend(params, {'historical': True})
|
|
return self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, maxHistoricalLimit)
|
|
until = self.safe_integer(params, 'until')
|
|
historical = False
|
|
historical, params = self.handle_option_and_params(params, 'fetchOHLCV', 'historical')
|
|
timeframeOption = self.safe_dict(self.options, 'timeframes', {})
|
|
contractTimeframes = self.safe_dict(timeframeOption, 'contract', {})
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'symbol': market['id'],
|
|
'interval': self.safe_string(contractTimeframes, timeframe, timeframe),
|
|
}
|
|
priceType = self.safe_string_upper(params, 'price')
|
|
params = self.omit(params, ['historical', 'until', 'price'])
|
|
response = None
|
|
if limit is not None:
|
|
limit = min(limit, 1000) # hardcap threshold
|
|
if historical:
|
|
if priceType is not None:
|
|
request['priceType'] = priceType
|
|
startTime = since
|
|
endTime = until
|
|
if (since is None) or (until is None):
|
|
now = self.milliseconds()
|
|
duration = self.parse_timeframe(timeframe) * 1000
|
|
numberOfCandles = limit if limit else maxHistoricalLimit
|
|
timeDelta = numberOfCandles * duration
|
|
if (since is None) and (until is None):
|
|
endTime = now
|
|
startTime = now - timeDelta
|
|
elif since is None:
|
|
startTime = until - timeDelta
|
|
else:
|
|
endTime = since + timeDelta
|
|
request['startTime'] = startTime
|
|
request['endTime'] = endTime
|
|
response = self.contractGetCapiV3MarketHistoryKlines(self.extend(request, params))
|
|
else:
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
if priceType == 'MARK':
|
|
response = self.contractGetCapiV3MarketMarkPriceKlines(self.extend(request, params))
|
|
elif priceType == 'INDEX':
|
|
response = self.contractGetCapiV3MarketIndexPriceKlines(self.extend(request, params))
|
|
else:
|
|
response = self.contractGetCapiV3MarketKlines(self.extend(request, params))
|
|
return self.parse_ohlcvs(response, market, timeframe, since, limit)
|
|
|
|
def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
|
|
return [
|
|
self.safe_integer(ohlcv, 0),
|
|
self.safe_number(ohlcv, 1),
|
|
self.safe_number(ohlcv, 2),
|
|
self.safe_number(ohlcv, 3),
|
|
self.safe_number(ohlcv, 4),
|
|
self.safe_number(ohlcv, 5),
|
|
]
|
|
|
|
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://www.weex.com/api-doc/spot/MarketDataAPI/GetTradeData # spot
|
|
https://www.weex.com/api-doc/contract/Market_API/GetRecentTrades # contract
|
|
|
|
: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 100, max 1000)
|
|
: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>`
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'symbol': market['id'],
|
|
}
|
|
if limit is not None:
|
|
request['limit'] = min(limit, 1000)
|
|
response = None
|
|
if market['spot']:
|
|
response = self.publicGetApiV3MarketTrades(self.extend(request, params))
|
|
else:
|
|
response = self.contractGetCapiV3MarketTrades(self.extend(request, params))
|
|
#
|
|
# [
|
|
# {
|
|
# "id": "875fba11-f8a1-42ad-915d-012ccb375e8a",
|
|
# "price": "2114.77",
|
|
# "qty": "0.01000",
|
|
# "quoteQty": "21.1477000",
|
|
# "time": 1775594995485,
|
|
# "isBuyerMaker": False,
|
|
# "isBestMatch": True
|
|
# }
|
|
# ]
|
|
#
|
|
return self.parse_trades(response, market, since, limit)
|
|
|
|
def parse_trade(self, trade: dict, market: Market = None) -> Trade:
|
|
#
|
|
# fetchTrades
|
|
# {
|
|
# "id": "875fba11-f8a1-42ad-915d-012ccb375e8a",
|
|
# "price": "2114.77",
|
|
# "qty": "0.01000",
|
|
# "quoteQty": "21.1477000",
|
|
# "time": 1775594995485,
|
|
# "isBuyerMaker": False,
|
|
# "isBestMatch": True
|
|
# }
|
|
#
|
|
# fetchMyTrades(spot)
|
|
# {
|
|
# "symbol": "DOGEUSDT",
|
|
# "id": 736825748291060702,
|
|
# "orderId": 736825748215563230,
|
|
# "price": "0.09349",
|
|
# "qty": "250.0",
|
|
# "quoteQty": "23.3725",
|
|
# "commission": "0.0233725",
|
|
# "time": 1775672947953,
|
|
# "isBuyer": False
|
|
# }
|
|
#
|
|
# fetchMyTrades(contract)
|
|
# {
|
|
# "id": 737074389731770728,
|
|
# "orderId": 737074043320009064,
|
|
# "symbol": "DOGEUSDT",
|
|
# "buyer": True,
|
|
# "commission": "0.00183500",
|
|
# "commissionAsset": "USDT",
|
|
# "maker": True,
|
|
# "price": "0.09175",
|
|
# "qty": "100",
|
|
# "quoteQty": "9.17500",
|
|
# "realizedPnl": "0",
|
|
# "side": "BUY",
|
|
# "positionSide": "LONG",
|
|
# "time": 1775732228692
|
|
# }
|
|
#
|
|
timestamp = self.safe_integer(trade, 'time')
|
|
isBuyer = self.safe_bool(trade, 'isBuyer')
|
|
side = self.safe_string_lower(trade, 'side')
|
|
if isBuyer is not None:
|
|
side = 'buy' if isBuyer else 'sell'
|
|
isSpot = True
|
|
if market is None:
|
|
marketId = self.safe_string(trade, 'symbol')
|
|
realizedPnl = self.safe_string(trade, 'realizedPnl')
|
|
marketType = 'swap' if (realizedPnl is not None) else 'spot'
|
|
market = self.safe_market(marketId, None, None, marketType)
|
|
isSpot = marketType == 'spot'
|
|
else:
|
|
isSpot = market['spot']
|
|
fee = None
|
|
commission = self.safe_string(trade, 'commission')
|
|
if commission is not None:
|
|
commissionAsset = self.safe_string(trade, 'commissionAsset')
|
|
feeCurrency = self.safe_currency_code(commissionAsset)
|
|
if isSpot:
|
|
if side == 'buy':
|
|
feeCurrency = market['base']
|
|
else:
|
|
feeCurrency = market['quote']
|
|
fee = {
|
|
'cost': commission,
|
|
'currency': feeCurrency,
|
|
}
|
|
isMaker = self.safe_bool(trade, 'maker')
|
|
takerOrMaker = None
|
|
if isMaker is not None:
|
|
takerOrMaker = 'maker' if isMaker else 'taker'
|
|
return self.safe_trade({
|
|
'info': trade,
|
|
'id': self.safe_string(trade, 'id'),
|
|
'order': self.safe_string(trade, 'orderId'),
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'symbol': market['symbol'],
|
|
'type': None,
|
|
'takerOrMaker': takerOrMaker,
|
|
'side': side,
|
|
'price': self.safe_string(trade, 'price'),
|
|
'amount': self.safe_string(trade, 'qty'),
|
|
'cost': self.safe_string(trade, 'quoteQty'),
|
|
'fee': fee,
|
|
}, market)
|
|
|
|
def fetch_open_interest(self, symbol: str, params={}):
|
|
"""
|
|
retrieves the open interest of a contract trading pair
|
|
|
|
https://www.weex.com/api-doc/contract/Market_API/GetOpenInterest
|
|
|
|
:param str symbol: unified CCXT market symbol
|
|
:param dict [params]: exchange specific parameters
|
|
:returns dict} an open interest structure{@link https://docs.ccxt.com/?id=open-interest-structure:
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'symbol': market['id'],
|
|
}
|
|
response = self.contractGetCapiV3MarketOpenInterest(self.extend(request, params))
|
|
return self.parse_open_interest(response, market)
|
|
|
|
def parse_open_interest(self, interest, market: Market = None):
|
|
#
|
|
# {
|
|
# "symbol": "ETHUSDT",
|
|
# "openInterest": "1772356.352",
|
|
# "time": 1775595582598
|
|
# }
|
|
#
|
|
marketId = self.safe_string(interest, 'symbol')
|
|
symbol = self.safe_symbol(marketId, market, None, 'swap')
|
|
timestamp = self.safe_integer(interest, 'time')
|
|
return self.safe_open_interest({
|
|
'symbol': symbol,
|
|
'openInterestAmount': self.safe_string(interest, 'openInterest'),
|
|
'openInterestValue': None,
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'info': interest,
|
|
}, market)
|
|
|
|
def fetch_funding_rates(self, symbols: Strings = None, params={}) -> FundingRates:
|
|
"""
|
|
fetch the funding rate for multiple markets
|
|
|
|
https://www.weex.com/api-doc/contract/Market_API/GetCurrentFundingRate
|
|
|
|
:param str[]|None symbols: list of unified market symbols
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.subType]: "linear" or "inverse"
|
|
:returns dict[]: a list of `funding rate structures <https://docs.ccxt.com/?id=funding-rates-structure>`, indexed by market symbols
|
|
"""
|
|
self.load_markets()
|
|
symbols = self.market_symbols(symbols)
|
|
symbolsLength = 0
|
|
if symbols is not None:
|
|
symbolsLength = len(symbols)
|
|
request: dict = {}
|
|
if symbolsLength == 1:
|
|
market = self.get_market_from_symbols(symbols)
|
|
request['symbol'] = market['id']
|
|
response = self.contractGetCapiV3MarketPremiumIndex(self.extend(request, params))
|
|
#
|
|
# [
|
|
# {
|
|
# "symbol": "ETHUSDT",
|
|
# "markPrice": "2133.71",
|
|
# "indexPrice": "2134.44",
|
|
# "forecastFundingRate": "0.00005618",
|
|
# "lastFundingRate": "0.00001031",
|
|
# "interestRate": "0.001",
|
|
# "nextFundingTime": 1775606400000,
|
|
# "time": 1775597594265,
|
|
# "collectCycle": 480
|
|
# }
|
|
# ]
|
|
#
|
|
return self.parse_funding_rates(response, symbols)
|
|
|
|
def parse_funding_rate(self, contract, market: Market = None) -> FundingRate:
|
|
marketId = self.safe_string(contract, 'symbol')
|
|
symbol = self.safe_symbol(marketId, market, None, 'swap')
|
|
timestamp = self.safe_integer(contract, 'time')
|
|
nextFundingTimestamp = self.safe_integer(contract, 'nextFundingTime')
|
|
interval = None
|
|
collectCycle = self.safe_string(contract, 'collectCycle')
|
|
if collectCycle is not None:
|
|
interval = Precise.string_div(collectCycle, '60')
|
|
interval = interval + 'h'
|
|
return {
|
|
'info': contract,
|
|
'symbol': symbol,
|
|
'markPrice': self.safe_number(contract, 'markPrice'),
|
|
'indexPrice': self.safe_number(contract, 'indexPrice'),
|
|
'interestRate': self.safe_number(contract, 'interestRate'),
|
|
'estimatedSettlePrice': None,
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'fundingRate': self.safe_number(contract, 'lastFundingRate'),
|
|
'fundingTimestamp': timestamp,
|
|
'fundingDatetime': self.iso8601(timestamp),
|
|
'nextFundingRate': self.safe_number(contract, 'forecastFundingRate'),
|
|
'nextFundingTimestamp': nextFundingTimestamp,
|
|
'nextFundingDatetime': self.iso8601(nextFundingTimestamp),
|
|
'previousFundingRate': None,
|
|
'previousFundingTimestamp': None,
|
|
'previousFundingDatetime': None,
|
|
'interval': interval,
|
|
}
|
|
|
|
def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
"""
|
|
fetches historical funding rate prices
|
|
|
|
https://www.weex.com/api-doc/contract/Market_API/GetFundingRateHistory
|
|
|
|
: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 records to fetch(default 100, max 1000)
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param int [params.until]: timestamp in ms of the latest funding rate
|
|
: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
|
|
request, params = self.handle_until_option('endTime', request, params)
|
|
response = self.contractGetCapiV3MarketFundingRate(self.extend(request, params))
|
|
return self.parse_funding_rate_histories(response, market, since, limit)
|
|
|
|
def parse_funding_rate_history(self, contract, market: Market = None):
|
|
#
|
|
# {
|
|
# "symbol": "ETHUSDT",
|
|
# "fundingRate": "0.00001031",
|
|
# "fundingTime": 1775577600000,
|
|
# "markPrice": "2079.26"
|
|
# }
|
|
#
|
|
marketId = self.safe_string(contract, 'symbol')
|
|
symbol = self.safe_symbol(marketId, market, None, 'swap')
|
|
timestamp = self.safe_integer(contract, 'fundingTime')
|
|
return {
|
|
'info': contract,
|
|
'symbol': symbol,
|
|
'fundingRate': self.safe_number(contract, 'fundingRate'),
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
}
|
|
|
|
def fetch_balance(self, params={}) -> Balances:
|
|
"""
|
|
|
|
https://www.weex.com/api-doc/spot/AccountAPI/GetAccountBalance # spot
|
|
https://www.weex.com/api-doc/contract/Account_API/GetAccountBalance # contract
|
|
|
|
query for balance and get the amount of funds available for trading or funds locked in positions
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.type]: 'spot' or 'swap'(default is 'spot')
|
|
:returns dict: a `balance structure <https://docs.ccxt.com/?id=balance-structure>`
|
|
"""
|
|
type = None
|
|
type, params = self.handle_market_type_and_params('fetchBalance', None, params)
|
|
response = None
|
|
if type == 'spot':
|
|
#
|
|
# {
|
|
# "makerCommission": 0,
|
|
# "takerCommission": 0,
|
|
# "commissionRates": {
|
|
# "maker": "0.00000000",
|
|
# "taker": "0.00000000"
|
|
# },
|
|
# "canTrade": True,
|
|
# "canWithdraw": True,
|
|
# "canDeposit": True,
|
|
# "updateTime": 1775601317093,
|
|
# "accountType": "SPOT",
|
|
# "balances": [
|
|
# {
|
|
# "asset": "USDT",
|
|
# "free": "20.00000000",
|
|
# "locked": "0"
|
|
# }
|
|
# ],
|
|
# "permissions": [
|
|
# "SPOT"
|
|
# ],
|
|
# "uid": 8886281669
|
|
# }
|
|
#
|
|
response = self.privateGetApiV3Account(params)
|
|
else:
|
|
#
|
|
# [
|
|
# {
|
|
# "asset": "USDT",
|
|
# "balance": "20.00000000",
|
|
# "availableBalance": "20.00000000",
|
|
# "frozen": "0",
|
|
# "unrealizePnl": "0"
|
|
# }
|
|
# ]
|
|
#
|
|
response = self.contractPrivateGetCapiV3AccountBalance(params)
|
|
return self.parse_balance(response)
|
|
|
|
def parse_balance(self, response) -> Balances:
|
|
result: dict = {
|
|
'info': response,
|
|
}
|
|
balances = self.safe_list(response, 'balances', response)
|
|
for i in range(0, len(balances)):
|
|
entry = self.safe_dict(balances, i)
|
|
id = self.safe_string(entry, 'asset')
|
|
code = self.safe_currency_code(id)
|
|
account = self.account()
|
|
account['free'] = self.safe_string_2(entry, 'availableBalance', 'free')
|
|
account['used'] = self.safe_string_2(entry, 'frozen', 'locked')
|
|
account['total'] = self.safe_string(entry, 'balance')
|
|
result[code] = account
|
|
return self.safe_balance(result)
|
|
|
|
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://www.weex.com/api-doc/spot/AccountAPI/TransferRecords
|
|
|
|
: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, max 100)
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
: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 list of `transfer structures <https://docs.ccxt.com/?id=transfer-structure>`
|
|
"""
|
|
self.load_markets()
|
|
request: dict = {}
|
|
currency = None
|
|
if code is not None:
|
|
currency = self.currency(code)
|
|
maxLimit = 100
|
|
paginate = False
|
|
paginate, params = self.handle_option_and_params(params, 'fetchTransfers', 'paginate', False)
|
|
if paginate:
|
|
return self.fetch_paginated_call_dynamic('fetchTransfers', code, since, limit, params, maxLimit)
|
|
if since is not None:
|
|
request['after'] = since
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
request, params = self.handle_until_option('before', request, params)
|
|
response = self.privateGetApiV3AccountTransferRecords(self.extend(request, params))
|
|
#
|
|
# [
|
|
# {
|
|
# "coinName": "USDT",
|
|
# "status": "Successful",
|
|
# "toType": "",
|
|
# "toSymbol": "",
|
|
# "fromType": "",
|
|
# "fromSymbol": "",
|
|
# "amount": "20.00000000",
|
|
# "tradeTime": "1775605824252"
|
|
# }
|
|
# ]
|
|
#
|
|
return self.parse_transfers(response, currency, since, limit)
|
|
|
|
def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
|
|
timestamp = self.safe_integer(transfer, 'tradeTime')
|
|
currencyId = self.safe_string(transfer, 'coinName')
|
|
currencyCode = self.safe_currency_code(currencyId, currency)
|
|
status = self.safe_string(transfer, 'status')
|
|
return {
|
|
'info': transfer,
|
|
'id': None,
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'currency': currencyCode,
|
|
'amount': self.safe_number(transfer, 'amount'),
|
|
'fromAccount': self.safe_string_lower(transfer, 'fromType'),
|
|
'toAccount': self.safe_string_lower(transfer, 'toType'),
|
|
'status': self.parse_transfer_status(status),
|
|
}
|
|
|
|
def parse_transfer_status(self, status: Str) -> str:
|
|
statuses: dict = {
|
|
'Successful': 'ok',
|
|
}
|
|
return self.safe_string(statuses, status, status)
|
|
|
|
def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
|
"""
|
|
Create an order on the exchange
|
|
|
|
https://www.weex.com/api-doc/spot/orderApi/PlaceOrder # spot
|
|
https://www.weex.com/api-doc/contract/Transaction_API/PlaceOrder # contract
|
|
https://www.weex.com/api-doc/contract/Transaction_API/PlacePendingOrder # contract trigger
|
|
https://www.weex.com/api-doc/contract/Transaction_API/PlaceTpSlOrder # contract take profit / stop loss
|
|
|
|
:param str symbol: Unified CCXT market symbol
|
|
:param str type: 'limit' or 'market'
|
|
:param str side: 'buy' or 'sell'
|
|
:param float amount: the amount of currency to trade
|
|
: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
|
|
Check createSpotOrder() and createContractOrder() for more details on the extra parameters that can be used in params
|
|
:returns dict: an `order structure <https://docs.ccxt.com/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
if market['contract']:
|
|
return self.create_contract_order(symbol, type, side, amount, price, params)
|
|
else:
|
|
return self.create_spot_order(symbol, type, side, amount, price, params)
|
|
|
|
def create_spot_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
|
"""
|
|
helper method for creating spot orders
|
|
|
|
https://www.weex.com/api-doc/spot/orderApi/PlaceOrder
|
|
|
|
:param str symbol: Unified CCXT market symbol
|
|
:param str type: 'limit' or 'market'
|
|
:param str side: 'buy' or 'sell'
|
|
:param float amount: the amount of currency to trade
|
|
: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 str [params.clientOrderId]: client order id
|
|
:param str [params.timeInForce]: 'GTC', 'IOC', or 'FOK'
|
|
:returns dict: an `order structure <https://docs.ccxt.com/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request = self.create_spot_order_request(symbol, type, side, amount, price, params)
|
|
response = self.privatePostApiV3Order(request)
|
|
#
|
|
# {
|
|
# "symbol": "DOGEUSDT",
|
|
# "orderId": 736557215397183592,
|
|
# "clientOrderId": "c4551206d34641efbeb64abaa066946d",
|
|
# "transactTime": 1775608924724
|
|
# }
|
|
#
|
|
return self.parse_order(response, market)
|
|
|
|
def create_spot_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}) -> dict:
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'symbol': market['id'],
|
|
'side': side.upper(),
|
|
'type': type.upper(),
|
|
'quantity': self.amount_to_precision(symbol, amount),
|
|
}
|
|
if type == 'limit':
|
|
request['price'] = self.price_to_precision(symbol, price)
|
|
clientOrderId = self.safe_string(params, 'clientOrderId')
|
|
params = self.omit(params, 'clientOrderId')
|
|
if clientOrderId is None:
|
|
partner = self.safe_string(params, 'partner', 'b-WEEX111125')
|
|
clientOrderId = partner + '-' + self.uuid22()
|
|
request['newClientOrderId'] = clientOrderId
|
|
# timeInForce is passed directly from params
|
|
return self.extend(request, params)
|
|
|
|
def create_contract_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
|
"""
|
|
helper method for creating contract orders
|
|
|
|
https://www.weex.com/api-doc/contract/Transaction_API/PlaceOrder
|
|
https://www.weex.com/api-doc/contract/Transaction_API/PlacePendingOrder
|
|
|
|
:param str symbol: Unified CCXT market symbol
|
|
:param str type: 'limit' or 'market'
|
|
:param str side: 'buy' or 'sell'
|
|
:param float amount: the amount of currency to trade
|
|
: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 str [params.clientOrderId]: client order id
|
|
:param dict [params.takeProfit]: *takeProfit object in params* containing the triggerPrice at which the attached take profit order will be triggered and the triggerPriceType
|
|
:param float [params.takeProfit.triggerPrice]: The price at which the take profit order will be triggered
|
|
:param str [params.takeProfit.triggerPriceType]: The type of the trigger price for the take profit order, either 'last' or 'mark'(default is 'last')
|
|
:param dict [params.stopLoss]: *stopLoss object in params* containing the triggerPrice at which the attached stop loss order will be triggered and the triggerPriceType
|
|
:param float [params.stopLoss.triggerPrice]: The price at which the stop loss order will be triggered
|
|
:param str [params.stopLoss.triggerPriceType]: The type of the trigger price for the stop loss order, either 'last' or 'mark'(default is 'last')
|
|
:param float [params.stopLossPrice]: price to trigger stop-loss orders
|
|
:param str [params.stopLossPriceType]: The type of the trigger price for the stop loss order, either 'last' or 'mark'(default is 'last')
|
|
:param float [params.takeProfitPrice]: price to trigger take-profit orders
|
|
:param str [params.takeProfitPriceType]: The type of the trigger price for the take profit order, either 'last' or 'mark'(default is 'last')
|
|
:param bool [params.reduceOnly]: A mark to reduce the position size only. Set to False by default. Need to set the position size when reduceOnly is True.
|
|
:param str [params.timeInForce]: GTC, IOC, or FOK(default is GTC for limit orders)
|
|
:returns dict: an `order structure <https://docs.ccxt.com/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request = self.create_contract_order_request(symbol, type, side, amount, price, params)
|
|
triggerPrice = self.safe_string(request, 'triggerPrice')
|
|
response = None
|
|
if triggerPrice is not None:
|
|
response = self.contractPrivatePostCapiV3AlgoOrder(request)
|
|
else:
|
|
response = self.contractPrivatePostCapiV3Order(request)
|
|
return self.parse_order(response, market)
|
|
|
|
def create_contract_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(),
|
|
'quantity': self.amount_to_precision(symbol, amount),
|
|
'type': type.upper(),
|
|
}
|
|
isMarketOrder = (type == 'market')
|
|
if not isMarketOrder:
|
|
request['price'] = self.price_to_precision(symbol, price)
|
|
triggerPrice, stopLossPrice, takeProfitPrice, query = self.handle_trigger_prices_and_params(symbol, params)
|
|
if triggerPrice is not None:
|
|
raise NotSupported(self.id + ' createOrder() does not support the triggerPrice parameter')
|
|
isStopLoss = (stopLossPrice is not None)
|
|
isTakeProfit = (takeProfitPrice is not None)
|
|
reduceOnly = self.safe_bool(query, 'reduceOnly')
|
|
if isStopLoss or isTakeProfit:
|
|
reduceOnly = True
|
|
isReduceOnly = (reduceOnly is True)
|
|
positionSide = 'LONG'
|
|
if isReduceOnly:
|
|
if side == 'buy':
|
|
positionSide = 'SHORT'
|
|
elif side == 'sell':
|
|
positionSide = 'SHORT'
|
|
request['positionSide'] = positionSide
|
|
takeProfit = self.safe_dict(params, 'takeProfit')
|
|
hasTakeProfit = (takeProfit is not None)
|
|
stopLoss = self.safe_dict(params, 'stopLoss')
|
|
hasStopLoss = (stopLoss is not None)
|
|
timeInForce = self.safe_string(params, 'timeInForce')
|
|
clientOrderId = self.safe_string(params, 'clientOrderId')
|
|
if clientOrderId is None:
|
|
partner = self.safe_string(params, 'partner', 'b-WEEX111125')
|
|
clientOrderId = partner + '-' + self.uuid22()
|
|
callerMethodName = self.safe_string(params, 'callerMethodName')
|
|
if isStopLoss or isTakeProfit:
|
|
if callerMethodName == 'createOrders':
|
|
raise NotSupported(self.id + ' createOrders() does not support stop loss and take profit orders')
|
|
if timeInForce is not None:
|
|
raise BadRequest(self.id + ' createOrder() cannot use timeInForce parameter with stopLoss and takeProfit orders')
|
|
if hasStopLoss or hasTakeProfit:
|
|
raise BadRequest(self.id + ' createOrder() cannot use both stopLossPrice/takeProfitPrice parameters and stopLoss/takeProfit objects in params at the same time')
|
|
if isStopLoss and isTakeProfit:
|
|
raise BadRequest(self.id + ' createOrder() cannot use both stopLossPrice and takeProfitPrice parameters at the same time')
|
|
request['clientAlgoId'] = clientOrderId
|
|
orderType = None
|
|
if isStopLoss:
|
|
stopLossPriceType = self.safe_string_2(params, 'stopLossPriceType', 'triggerPriceType')
|
|
if stopLossPriceType is not None:
|
|
params['SlWorkingType'] = self.encode_trigger_price_type(stopLossPriceType)
|
|
params['triggerPrice'] = self.price_to_precision(symbol, stopLossPrice)
|
|
if isMarketOrder:
|
|
orderType = 'STOP_MARKET'
|
|
else:
|
|
orderType = 'STOP'
|
|
elif isTakeProfit:
|
|
takeProfitPriceType = self.safe_string_2(params, 'takeProfitPriceType', 'triggerPriceType')
|
|
if takeProfitPriceType is not None:
|
|
params['TpWorkingType'] = self.encode_trigger_price_type(takeProfitPriceType)
|
|
params['triggerPrice'] = self.price_to_precision(symbol, takeProfitPrice)
|
|
if isMarketOrder:
|
|
orderType = 'TAKE_PROFIT_MARKET'
|
|
else:
|
|
orderType = 'TAKE_PROFIT'
|
|
params['type'] = orderType
|
|
else:
|
|
if not isMarketOrder and timeInForce is None:
|
|
request['timeInForce'] = 'GTC'
|
|
request['newClientOrderId'] = clientOrderId
|
|
if hasStopLoss:
|
|
stopLossTriggerPrice = self.safe_number(stopLoss, 'triggerPrice')
|
|
request['slTriggerPrice'] = self.price_to_precision(symbol, stopLossTriggerPrice)
|
|
stopLossPriceType = self.safe_string(stopLoss, 'triggerPriceType')
|
|
if stopLossPriceType is not None:
|
|
params['SlWorkingType'] = self.encode_trigger_price_type(stopLossPriceType)
|
|
if hasTakeProfit:
|
|
takeProfitTriggerPrice = self.safe_number(takeProfit, 'triggerPrice')
|
|
request['tpTriggerPrice'] = self.price_to_precision(symbol, takeProfitTriggerPrice)
|
|
takeProfitPriceType = self.safe_string(takeProfit, 'triggerPriceType')
|
|
if takeProfitPriceType is not None:
|
|
params['TpWorkingType'] = self.encode_trigger_price_type(takeProfitPriceType)
|
|
params = self.omit(params, ['takeProfit', 'stopLoss', 'stopLossPrice', 'takeProfitPrice', 'triggerPriceType', 'stopLossPriceType', 'takeProfitPriceType', 'clientOrderId', 'callerMethodName'])
|
|
return self.extend(request, params)
|
|
|
|
def encode_trigger_price_type(self, triggerPriceType: Str):
|
|
types: dict = {
|
|
'mark': 'MARK_PRICE',
|
|
'last': 'CONTRACT_PRICE',
|
|
}
|
|
return self.safe_string(types, triggerPriceType, triggerPriceType)
|
|
|
|
def cancel_order(self, id: str, symbol: Str = None, params={}):
|
|
"""
|
|
cancels an open order
|
|
|
|
https://www.weex.com/api-doc/spot/orderApi/CancelOrder # spot
|
|
https://www.weex.com/api-doc/contract/Transaction_API/CancelOrder # contract
|
|
|
|
:param str id: order id
|
|
:param str [symbol]: unified symbol of the market the order was made in
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.type]: 'spot' or 'swap'(default is 'spot')
|
|
:param boolean [params.trigger]: *contract orders only* whether the order to cancel is a trigger order
|
|
:param str [params.clientOrderId]: *non-trigger orders only* a unique id for the order
|
|
:returns dict: an `order structure <https://docs.ccxt.com/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
type = None
|
|
type, params = self.handle_market_type_and_params('cancelOrder', market, params)
|
|
trigger = self.safe_bool(params, 'trigger', False)
|
|
if trigger and id is None:
|
|
raise ArgumentsRequired(self.id + ' cancelOrder() requires an id argument for trigger orders')
|
|
request: dict = {}
|
|
clientOrderId = self.safe_string(params, 'clientOrderId')
|
|
params = self.omit(params, ['clientOrderId', 'trigger'])
|
|
if clientOrderId is not None:
|
|
request['origClientOrderId'] = clientOrderId
|
|
elif id is None:
|
|
raise ArgumentsRequired(self.id + ' cancelOrder() requires an id argument or clientOrderId parameter')
|
|
else:
|
|
request['orderId'] = id
|
|
response = None
|
|
if type == 'spot':
|
|
# by orderId
|
|
# {
|
|
# "orderId": 736775987680772200,
|
|
# "status": "CANCELED"
|
|
# }
|
|
#
|
|
# by clientOrderId
|
|
# {
|
|
# "origClientOrderId": "test_cancel_order",
|
|
# "status": "CANCELED"
|
|
# }
|
|
#
|
|
response = self.privateDeleteApiV3Order(self.extend(request, params))
|
|
elif trigger:
|
|
response = self.contractPrivateDeleteCapiV3AlgoOrder(self.extend(request, params))
|
|
else:
|
|
response = self.contractPrivateDeleteCapiV3Order(self.extend(request, params))
|
|
order = self.parse_order(response, market)
|
|
order['status'] = 'canceled'
|
|
return order
|
|
|
|
def cancel_all_orders(self, symbol: Str = None, params={}):
|
|
"""
|
|
cancel all open orders
|
|
|
|
https://www.weex.com/api-doc/spot/orderApi/Cancel-Symbol-Orders # spot
|
|
https://www.weex.com/api-doc/contract/Transaction_API/CancelAllOrders # contract
|
|
https://www.weex.com/api-doc/contract/Transaction_API/CancelAllPendingOrders # contract trigger
|
|
|
|
: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
|
|
:param str [params.type]: 'spot' or 'swap', used if symbol is not provided(default is 'spot')
|
|
:param boolean [params.trigger]: *swap only* True for cancelling trigger orders(default is False)
|
|
:returns: Response from the exchange
|
|
"""
|
|
self.load_markets()
|
|
request: dict = {}
|
|
market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
request['symbol'] = market['id']
|
|
marketType = None
|
|
marketType, params = self.handle_market_type_and_params('cancelAllOrders', market, params)
|
|
trigger = self.safe_bool(params, 'trigger', False)
|
|
params = self.omit(params, 'trigger')
|
|
response = None
|
|
if marketType == 'spot':
|
|
if symbol is None:
|
|
raise ArgumentsRequired(self.id + ' cancelAllOrders() requires a symbol argument for spot markets')
|
|
response = self.privateDeleteApiV3OpenOrders(self.extend(request, params))
|
|
elif trigger:
|
|
response = self.contractPrivateDeleteCapiV3AlgoOpenOrders(self.extend(request, params))
|
|
else:
|
|
response = self.contractPrivateDeleteCapiV3AllOpenOrders(self.extend(request, params))
|
|
extendedParams: dict = {
|
|
'status': 'canceled',
|
|
}
|
|
return self.parse_orders(response, market, None, None, extendedParams)
|
|
|
|
def cancel_orders(self, ids: List[str], symbol: Str = None, params={}):
|
|
"""
|
|
cancel multiple orders
|
|
|
|
https://www.weex.com/api-doc/spot/orderApi/BulkCancel # spot
|
|
https://www.weex.com/api-doc/contract/Transaction_API/CancelOrdersBatch # contract
|
|
|
|
:param str[] ids: order ids
|
|
:param str [symbol]: unified market symbol, default is None
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str[] [params.clientOrderIds]: client order ids(could be an alternative to ids)
|
|
:param str [params.type]: 'spot' or 'swap', used if symbol is not provided(default is 'spot')
|
|
:returns dict: an list of `order structures <https://docs.ccxt.com/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
request: dict = {}
|
|
market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
marketType = None
|
|
marketType, params = self.handle_market_type_and_params('cancelOrders', market, params)
|
|
isSpot = (marketType == 'spot')
|
|
clientOrderIds = self.safe_list(params, 'clientOrderIds')
|
|
params = self.omit(params, 'clientOrderIds')
|
|
if clientOrderIds is not None:
|
|
if isSpot:
|
|
request['origClientOrderIds'] = clientOrderIds
|
|
else:
|
|
request['origClientOrderIdList'] = clientOrderIds
|
|
elif ids is not None:
|
|
if isSpot:
|
|
request['orderIds'] = ids
|
|
else:
|
|
request['orderIdList'] = ids
|
|
else:
|
|
raise ArgumentsRequired(self.id + ' cancelOrders() requires an ids argument or clientOrderIds parameter')
|
|
response = None
|
|
if isSpot:
|
|
response = self.privateDeleteApiV3OrderBatch(self.extend(request, params))
|
|
else:
|
|
response = self.contractPrivateDeleteCapiV3BatchOrders(self.extend(request, params))
|
|
ordersResponse = self.safe_list(response, 'orderList', [])
|
|
extendedParams: dict = {
|
|
'status': 'canceled',
|
|
}
|
|
return self.parse_orders(ordersResponse, market, None, None, extendedParams)
|
|
|
|
def fetch_order(self, id: Str, symbol: Str = None, params={}):
|
|
"""
|
|
fetches information on an order made by the user
|
|
|
|
https://www.weex.com/api-doc/spot/orderApi/OrderDetails # spot
|
|
https://www.weex.com/api-doc/contract/Transaction_API/GetSingleOrderInfo # contract
|
|
|
|
:param str id: order id
|
|
:param str symbol: unified symbol of the market the order was made in
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.type]: 'spot' or 'swap', used if symbol is not provided(default is 'spot')
|
|
:param str [params.clientOrderId]: *spot only* a unique id for the order, used if id is not provided
|
|
:returns dict: An `order structure <https://docs.ccxt.com/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
marketType = None
|
|
marketType, params = self.handle_market_type_and_params('fetchOrder', market, params)
|
|
isSpot = (marketType == 'spot')
|
|
request: dict = {}
|
|
if (id is None) and not isSpot:
|
|
raise ArgumentsRequired(self.id + ' fetchOrder() requires an id argument for non-spot markets')
|
|
clientOrderId = self.safe_string(params, 'clientOrderId')
|
|
params = self.omit(params, 'clientOrderId')
|
|
if clientOrderId is not None:
|
|
request['origClientOrderId'] = clientOrderId
|
|
elif id is None:
|
|
raise ArgumentsRequired(self.id + ' fetchOrder() requires an id argument or clientOrderId parameter for spot markets')
|
|
else:
|
|
request['orderId'] = id
|
|
response = None
|
|
if isSpot:
|
|
#
|
|
# {
|
|
# "symbol": "DOGEUSDT",
|
|
# "orderId": 736800333186991070,
|
|
# "clientOrderId": "082007092f624a18bb7af2ab42e7c8e8",
|
|
# "price": "0.08500",
|
|
# "origQty": "300.0",
|
|
# "executedQty": "0",
|
|
# "cummulativeQuoteQty": "0",
|
|
# "status": "NEW",
|
|
# "timeInForce": "GTC",
|
|
# "type": "LIMIT",
|
|
# "side": "BUY",
|
|
# "time": 1775666888520,
|
|
# "updateTime": 1775666888536,
|
|
# "isWorking": True
|
|
# }
|
|
#
|
|
response = self.privateGetApiV3Order(self.extend(request, params))
|
|
else:
|
|
response = self.contractPrivateGetCapiV3Order(self.extend(request, params))
|
|
return self.parse_order(response, market)
|
|
|
|
def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
|
"""
|
|
|
|
https://www.weex.com/api-doc/spot/orderApi/UnfinishedOrders # spot
|
|
https://www.weex.com/api-doc/contract/Transaction_API/GetCurrentOrderStatus # contract
|
|
https://www.weex.com/api-doc/contract/Transaction_API/GetCurrentPendingOrders # contract trigger
|
|
|
|
fetch all unfilled currently open orders
|
|
:param str symbol: unified market symbol
|
|
:param int [since]: the earliest time in ms to fetch open orders for
|
|
:param int [limit]: the maximum number of open orders structures to retrieve
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.type]: 'spot' or 'swap', used if symbol is not provided(default is 'spot')
|
|
:param boolean [params.trigger]: *swap only* whether to fetch trigger orders(default is False)
|
|
:returns Order[]: a list of `order structures <https://docs.ccxt.com/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
marketType = None
|
|
marketType, params = self.handle_market_type_and_params('fetchOpenOrders', market, params)
|
|
isSpot = (marketType == 'spot')
|
|
paginate = False
|
|
paginate, params = self.handle_option_and_params(params, 'fetchOpenOrders', 'paginate', False)
|
|
maxLimit = 100
|
|
if paginate:
|
|
if isSpot:
|
|
raise NotSupported(self.id + ' fetchOpenOrders() pagination is not supported for spot markets')
|
|
return self.fetch_paginated_call_dynamic('fetchOpenOrders', symbol, since, limit, params, maxLimit)
|
|
request: dict = {}
|
|
if symbol is not None:
|
|
request['symbol'] = market['id']
|
|
response = None
|
|
if isSpot:
|
|
#
|
|
# [
|
|
# {
|
|
# "symbol": "DOGEUSDT",
|
|
# "orderId": 736807745679786974,
|
|
# "clientOrderId": "e6dc41082bf342f580a19264d82dab31",
|
|
# "price": "0.12000",
|
|
# "origQty": "299.0",
|
|
# "executedQty": "0",
|
|
# "cummulativeQuoteQty": "0",
|
|
# "status": "NEW",
|
|
# "timeInForce": "GTC",
|
|
# "type": "LIMIT",
|
|
# "side": "SELL",
|
|
# "time": 1775668655796,
|
|
# "updateTime": 1775668655810,
|
|
# "isWorking": True
|
|
# }
|
|
# ]
|
|
#
|
|
response = self.privateGetApiV3OpenOrders(self.extend(request, params))
|
|
else:
|
|
if since is not None:
|
|
request['startTime'] = since
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
request, params = self.handle_until_option('endTime', request, params)
|
|
trigger = self.safe_bool(params, 'trigger', False)
|
|
if trigger:
|
|
params = self.omit(params, 'trigger')
|
|
#
|
|
# [
|
|
# {
|
|
# "algoId": 737074389748547944,
|
|
# "clientAlgoId": "d574f517-cea5-433e-b029-415590d3bb80",
|
|
# "algoType": "CONDITIONAL",
|
|
# "orderType": "STOP_MARKET",
|
|
# "symbol": "DOGEUSDT",
|
|
# "side": "SELL",
|
|
# "positionSide": "LONG",
|
|
# "timeInForce": "IOC",
|
|
# "quantity": "100",
|
|
# "algoStatus": "UNTRIGGERED",
|
|
# "actualOrderId": 737074043320009064,
|
|
# "actualPrice": "0.00000",
|
|
# "triggerPrice": "0.02000",
|
|
# "price": "0.00000",
|
|
# "tpTriggerPrice": null,
|
|
# "tpPrice": null,
|
|
# "slTriggerPrice": null,
|
|
# "slPrice": null,
|
|
# "tpOrderType": null,
|
|
# "workingType": "CONTRACT_PRICE",
|
|
# "closePosition": False,
|
|
# "reduceOnly": True,
|
|
# "createTime": 1775732228695,
|
|
# "updateTime": 1775732228695,
|
|
# "triggerTime": 0
|
|
# }
|
|
# ]
|
|
#
|
|
response = self.contractPrivateGetCapiV3OpenAlgoOrders(self.extend(request, params))
|
|
else:
|
|
#
|
|
# [
|
|
# {
|
|
# "avgPrice": "0.00000",
|
|
# "clientOrderId": "857e1482-3225-44ce-bc0a-947714c5cabc",
|
|
# "cumQuote": "0",
|
|
# "executedQty": "0",
|
|
# "orderId": 737185556881998184,
|
|
# "origQty": "1400",
|
|
# "price": "0.05000",
|
|
# "reduceOnly": False,
|
|
# "side": "BUY",
|
|
# "positionSide": "LONG",
|
|
# "status": "NEW",
|
|
# "stopPrice": "0",
|
|
# "symbol": "DOGEUSDT",
|
|
# "time": 1775758733006,
|
|
# "timeInForce": "GTC",
|
|
# "type": "LIMIT",
|
|
# "updateTime": 1775758733006,
|
|
# "workingType": "UNKNOWN_PRICE_TYPE"
|
|
# }
|
|
# ]
|
|
#
|
|
response = self.contractPrivateGetCapiV3OpenOrders(self.extend(request, params))
|
|
extendedParams: dict = {
|
|
'status': 'open',
|
|
}
|
|
return self.parse_orders(response, market, since, limit, extendedParams)
|
|
|
|
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://www.weex.com/api-doc/spot/orderApi/HistoryOrders # spot
|
|
https://www.weex.com/api-doc/contract/Transaction_API/GetOrderHistory # contract
|
|
|
|
: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 int [params.until]: the latest time in ms to fetch orders for
|
|
:param str [params.type]: 'spot' or 'swap', used if symbol is not provided(default is 'spot')
|
|
:returns Order[]: a list of `order structures <https://docs.ccxt.com/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
marketType = None
|
|
marketType, params = self.handle_market_type_and_params('fetchClosedOrders', market, params)
|
|
orders = None
|
|
if marketType == 'spot':
|
|
if symbol is None:
|
|
raise ArgumentsRequired(self.id + ' fetchClosedOrders() requires a symbol argument for spot markets')
|
|
orders = self.fetch_orders(symbol, since, None, params)
|
|
else:
|
|
orders = self.fetch_canceled_and_closed_orders(symbol, since, limit, params)
|
|
return self.filter_by(orders, 'status', 'closed')
|
|
|
|
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://www.weex.com/api-doc/spot/orderApi/HistoryOrders # spot
|
|
https://www.weex.com/api-doc/contract/Transaction_API/GetOrderHistory # contract
|
|
|
|
: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 int [params.until]: the latest time in ms to fetch orders for
|
|
:param str [params.type]: 'spot' or 'swap', used if symbol is not provided(default is 'spot')
|
|
:returns Order[]: a list of `order structures <https://docs.ccxt.com/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
marketType = None
|
|
marketType, params = self.handle_market_type_and_params('fetchCanceledOrders', market, params)
|
|
orders = None
|
|
if marketType == 'spot':
|
|
if symbol is None:
|
|
raise ArgumentsRequired(self.id + ' fetchCanceledOrders() requires a symbol argument for spot markets')
|
|
orders = self.fetch_orders(symbol, since, None, params)
|
|
else:
|
|
orders = self.fetch_canceled_and_closed_orders(symbol, since, limit, params)
|
|
return self.filter_by(orders, 'status', 'canceled')
|
|
|
|
def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
|
"""
|
|
fetches information on multiple spot orders made by the user
|
|
|
|
https://www.weex.com/api-doc/spot/orderApi/HistoryOrders # spot
|
|
|
|
:param str symbol: unified market symbol of the market orders were made in(required for spot orders)
|
|
: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 dict [params.until]: end time, ms
|
|
: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 Order[]: a list of `order structures <https://docs.ccxt.com/?id=order-structure>`
|
|
"""
|
|
if symbol is None:
|
|
raise ArgumentsRequired(self.id + ' fetchOrders() requires a symbol argument')
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
if not market['spot']:
|
|
raise NotSupported(self.id + ' fetchOrders() supports spot markets only')
|
|
maxLimit = 1000
|
|
paginate = False
|
|
paginate, params = self.handle_option_and_params(params, 'fetchOrders', 'paginate', False)
|
|
if paginate:
|
|
return self.fetch_paginated_call_dynamic('fetchOrders', symbol, since, limit, params, maxLimit)
|
|
request: dict = {
|
|
'symbol': market['id'],
|
|
}
|
|
if since is not None:
|
|
request['startTime'] = since
|
|
if limit is not None:
|
|
request['limit'] = min(limit, maxLimit)
|
|
request, params = self.handle_until_option('endTime', request, params)
|
|
response = self.privateGetApiV3AllOrders(self.extend(request, params))
|
|
#
|
|
# [
|
|
# {
|
|
# "symbol": "DOGEUSDT",
|
|
# "orderId": 736806838401500126,
|
|
# "clientOrderId": "e93fcb1423fc4b4982fd02eb3bc4955c",
|
|
# "price": "0.09365",
|
|
# "origQty": "300.0",
|
|
# "executedQty": "300.0",
|
|
# "cummulativeQuoteQty": "28.095",
|
|
# "status": "FILLED",
|
|
# "timeInForce": "IOC",
|
|
# "type": "MARKET",
|
|
# "side": "BUY",
|
|
# "time": 1775668439484,
|
|
# "updateTime": 1775668439498,
|
|
# "isWorking": False
|
|
# }
|
|
# ]
|
|
#
|
|
return self.parse_orders(response, market, since, limit)
|
|
|
|
def fetch_canceled_and_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
|
"""
|
|
fetches information on multiple closed and canceled orders made by the user
|
|
|
|
https://www.weex.com/api-doc/contract/Transaction_API/GetOrderHistory # contract
|
|
|
|
:param str [symbol]: unified market symbol of the market orders were made in(required for spot orders)
|
|
: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 dict [params.until]: end time, ms
|
|
:param str [params.type]: 'spot' or 'swap', used if symbol is not provided(default is 'spot')
|
|
: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 Order[]: a list of `order structures <https://docs.ccxt.com/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
marketType = None
|
|
marketType, params = self.handle_market_type_and_params('fetchOrders', market, params)
|
|
if marketType == 'spot':
|
|
raise NotSupported(self.id + ' fetchCanceledAndClosedOrders() does not support spot markets. Use fetchOrders() instead and filter by status "canceled" or "closed"')
|
|
paginate = False
|
|
paginate, params = self.handle_option_and_params(params, 'fetchOrders', 'paginate', False)
|
|
maxLimit = 1000
|
|
if paginate:
|
|
return self.fetch_paginated_call_dynamic('fetchOrders', symbol, since, limit, params, maxLimit)
|
|
request: dict = {}
|
|
if symbol is not None:
|
|
request['symbol'] = market['id']
|
|
if since is not None:
|
|
request['startTime'] = since
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
request, params = self.handle_until_option('endTime', request, params)
|
|
response = self.contractPrivateGetCapiV3OrderHistory(self.extend(request, params))
|
|
#
|
|
# [
|
|
# {
|
|
# "avgPrice": "0.00000",
|
|
# "clientOrderId": "7bd80776-0c3f-4ed9-ab9c-a616d66fac5e",
|
|
# "cumQuote": "0",
|
|
# "executedQty": "0",
|
|
# "orderId": 737074389744353640,
|
|
# "origQty": "100",
|
|
# "price": "0.00000",
|
|
# "reduceOnly": True,
|
|
# "side": "SELL",
|
|
# "positionSide": "LONG",
|
|
# "status": "CANCELED",
|
|
# "stopPrice": "1.00000",
|
|
# "symbol": "DOGEUSDT",
|
|
# "time": 1775732228695,
|
|
# "timeInForce": "IOC",
|
|
# "type": "TAKE_PROFIT_MARKET",
|
|
# "updateTime": 1775732228695,
|
|
# "workingType": "CONTRACT_PRICE"
|
|
# }
|
|
# ]
|
|
#
|
|
return self.parse_orders(response, market, since, limit)
|
|
|
|
def parse_order(self, order: dict, market: Market = None) -> Order:
|
|
#
|
|
# createOrder(spot)
|
|
# {
|
|
# "symbol": "DOGEUSDT",
|
|
# "orderId": 736557215397183592,
|
|
# "clientOrderId": "c4551206d34641efbeb64abaa066946d",
|
|
# "transactTime": 1775608924724
|
|
# }
|
|
#
|
|
# fetchOpenOrders / fetchOrders / fetchOrder(spot)
|
|
# {
|
|
# "symbol": "DOGEUSDT",
|
|
# "orderId": 736800333186991070,
|
|
# "clientOrderId": "082007092f624a18bb7af2ab42e7c8e8",
|
|
# "price": "0.08500",
|
|
# "origQty": "300.0",
|
|
# "executedQty": "0",
|
|
# "cummulativeQuoteQty": "0",
|
|
# "status": "NEW",
|
|
# "timeInForce": "GTC",
|
|
# "type": "LIMIT",
|
|
# "side": "BUY",
|
|
# "time": 1775666888520,
|
|
# "updateTime": 1775666888536,
|
|
# "isWorking": True
|
|
# }
|
|
#
|
|
# fetchOpenOrders(contract)
|
|
# {
|
|
# "avgPrice": "0.00000",
|
|
# "clientOrderId": "857e1482-3225-44ce-bc0a-947714c5cabc",
|
|
# "cumQuote": "0",
|
|
# "executedQty": "0",
|
|
# "orderId": 737185556881998184,
|
|
# "origQty": "1400",
|
|
# "price": "0.05000",
|
|
# "reduceOnly": False,
|
|
# "side": "BUY",
|
|
# "positionSide": "LONG",
|
|
# "status": "NEW",
|
|
# "stopPrice": "0",
|
|
# "symbol": "DOGEUSDT",
|
|
# "time": 1775758733006,
|
|
# "timeInForce": "GTC",
|
|
# "type": "LIMIT",
|
|
# "updateTime": 1775758733006,
|
|
# "workingType": "UNKNOWN_PRICE_TYPE"
|
|
# }
|
|
#
|
|
# fetchOpenOrders(contract-trigger)
|
|
# {
|
|
# "algoId": 737074389748547944,
|
|
# "clientAlgoId": "d574f517-cea5-433e-b029-415590d3bb80",
|
|
# "algoType": "CONDITIONAL",
|
|
# "orderType": "STOP_MARKET",
|
|
# "symbol": "DOGEUSDT",
|
|
# "side": "SELL",
|
|
# "positionSide": "LONG",
|
|
# "timeInForce": "IOC",
|
|
# "quantity": "100",
|
|
# "algoStatus": "UNTRIGGERED",
|
|
# "actualOrderId": 737074043320009064,
|
|
# "actualPrice": "0.00000",
|
|
# "triggerPrice": "0.02000",
|
|
# "price": "0.00000",
|
|
# "tpTriggerPrice": null,
|
|
# "tpPrice": null,
|
|
# "slTriggerPrice": null,
|
|
# "slPrice": null,
|
|
# "tpOrderType": null,
|
|
# "workingType": "CONTRACT_PRICE",
|
|
# "closePosition": False,
|
|
# "reduceOnly": True,
|
|
# "createTime": 1775732228695,
|
|
# "updateTime": 1775732228695,
|
|
# "triggerTime": 0
|
|
# }
|
|
#
|
|
# fetchCanceledAndClosedOrders(swap only)
|
|
# {
|
|
# "avgPrice": "0.00000",
|
|
# "clientOrderId": "7bd80776-0c3f-4ed9-ab9c-a616d66fac5e",
|
|
# "cumQuote": "0",
|
|
# "executedQty": "0",
|
|
# "orderId": 737074389744353640,
|
|
# "origQty": "100",
|
|
# "price": "0.00000",
|
|
# "reduceOnly": True,
|
|
# "side": "SELL",
|
|
# "positionSide": "LONG",
|
|
# "status": "CANCELED",
|
|
# "stopPrice": "1.00000",
|
|
# "symbol": "DOGEUSDT",
|
|
# "time": 1775732228695,
|
|
# "timeInForce": "IOC",
|
|
# "type": "TAKE_PROFIT_MARKET",
|
|
# "updateTime": 1775732228695,
|
|
# "workingType": "CONTRACT_PRICE"
|
|
# }
|
|
#
|
|
errorCode = self.safe_string(order, 'errorCode')
|
|
errorMessage = self.safe_string(order, 'errorMsg')
|
|
if (errorCode is not None) or (errorMessage is not None):
|
|
self.handle_order_or_position_error(errorCode, errorMessage, order)
|
|
if market is None:
|
|
marketId = self.safe_string(order, 'symbol')
|
|
positionSide = self.safe_string(order, 'positionSide')
|
|
marketType = 'spot' if (positionSide is None) else 'swap'
|
|
market = self.safe_market(marketId, None, None, marketType)
|
|
timestamp = self.safe_integer_n(order, ['transactTime', 'time', 'createTime'])
|
|
rawStatus = self.safe_string_lower(order, 'status')
|
|
triggerPrice = self.omit_zero(self.safe_string_2(order, 'triggerPrice', 'stopPrice'))
|
|
rawType = self.safe_string_upper_2(order, 'type', 'orderType')
|
|
takeProfitPrice = None
|
|
stopLossPrice = None
|
|
if rawType == 'TAKE_PROFIT_MARKET' or rawType == 'TAKE_PROFIT':
|
|
takeProfitPrice = triggerPrice
|
|
elif rawType == 'STOP_LOSS' or rawType == 'STOP' or rawType == 'STOP_MARKET':
|
|
stopLossPrice = triggerPrice
|
|
return self.safe_order({
|
|
'id': self.safe_string_n(order, ['orderId', 'algoId', 'successOrderId']),
|
|
'clientOrderId': self.safe_string_n(order, ['clientOrderId', 'origClientOrderId', 'clientAlgoId']),
|
|
'symbol': self.safe_string(market, 'symbol'),
|
|
'type': self.parse_order_type(rawType),
|
|
'timeInForce': self.safe_string(order, 'timeInForce'),
|
|
'postOnly': None,
|
|
'reduceOnly': self.safe_bool(order, 'reduceOnly'),
|
|
'side': self.safe_string_lower(order, 'side'),
|
|
'amount': self.safe_string_2(order, 'origQty', 'quantity'),
|
|
'price': self.safe_string(order, 'price'),
|
|
'triggerPrice': triggerPrice,
|
|
'cost': self.safe_string_2(order, 'cummulativeQuoteQty', 'cumQuote'),
|
|
'filled': self.safe_string(order, 'executedQty'),
|
|
'remaining': None,
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'fee': None,
|
|
'status': self.parse_order_status(rawStatus),
|
|
'lastTradeTimestamp': None,
|
|
'lastUpdateTimestamp': self.safe_integer(order, 'updateTime'),
|
|
'average': self.safe_string(order, 'avgPrice'),
|
|
'trades': None,
|
|
'stopLossPrice': stopLossPrice,
|
|
'takeProfitPrice': takeProfitPrice,
|
|
'info': order,
|
|
}, market)
|
|
|
|
def parse_order_status(self, status: Str):
|
|
statuses: dict = {
|
|
'new': 'open',
|
|
'partial_fill': 'closed',
|
|
'full_fill': 'closed',
|
|
'filled': 'closed',
|
|
'cancelled': 'canceled',
|
|
'canceled': 'canceled',
|
|
'rejected': 'rejected',
|
|
'untriggered': 'open',
|
|
}
|
|
return self.safe_string(statuses, status, status)
|
|
|
|
def parse_order_type(self, type: Str):
|
|
types: dict = {
|
|
'LIMIT': 'limit',
|
|
'MARKET': 'market',
|
|
'STOP_LOSS': 'limit',
|
|
'STOP': 'limit',
|
|
'TAKE_PROFIT': 'limit',
|
|
'TAKE_PROFIT_MARKET': 'market',
|
|
'STOP_MARKET': 'market',
|
|
}
|
|
return self.safe_string(types, type, type)
|
|
|
|
def handle_order_or_position_error(self, errorCode: Str, errorMessage: Str, order: dict):
|
|
if errorCode is None:
|
|
errorCode = ''
|
|
if errorMessage is None:
|
|
errorMessage = ''
|
|
if (errorCode == '') and (errorMessage == ''):
|
|
# some endpoints could return an empty string if there is no error
|
|
return
|
|
feedback = self.id + ' ' + self.json(order)
|
|
self.throw_exactly_matched_exception(self.exceptions['exact'], errorMessage, feedback)
|
|
self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
|
|
self.throw_broadly_matched_exception(self.exceptions['broad'], errorMessage, feedback)
|
|
self.throw_broadly_matched_exception(self.exceptions['broad'], errorCode, feedback)
|
|
raise InvalidOrder(feedback)
|
|
|
|
def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
"""
|
|
fetch all the trades made from a single order
|
|
|
|
https://www.weex.com/api-doc/spot/orderApi/TransactionDetails # spot
|
|
https://www.weex.com/api-doc/contract/Transaction_API/GetTradeDetails # contract
|
|
|
|
:param str id: order id
|
|
: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 to retrieve
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/?id=trade-structure>`
|
|
"""
|
|
self.load_markets()
|
|
request: dict = {
|
|
'orderId': id,
|
|
}
|
|
return self.fetch_my_trades(symbol, since, limit, self.extend(request, params))
|
|
|
|
def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
|
"""
|
|
|
|
https://www.weex.com/api-doc/spot/orderApi/TransactionDetails # spot
|
|
https://www.weex.com/api-doc/contract/Transaction_API/GetTradeDetails # contract
|
|
|
|
fetch all trades made by the user
|
|
: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 boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
|
|
:param str [params.type]: 'spot' or 'swap', used if symbol is not provided(default is 'spot')
|
|
:returns Trade[]: a list of `trade structures <https://docs.ccxt.com/?id=trade-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
marketType = None
|
|
marketType, params = self.handle_market_type_and_params('fetchMyTrades', market, params)
|
|
isSpot = (marketType == 'spot')
|
|
if isSpot and (symbol is None):
|
|
raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol argument for spot markets')
|
|
paginate = False
|
|
paginate, params = self.handle_option_and_params(params, 'fetchMyTrades', 'paginate', False)
|
|
maxLimit = 100
|
|
if paginate:
|
|
return self.fetch_paginated_call_dynamic('fetchMyTrades', symbol, since, limit, params, maxLimit)
|
|
request: dict = {}
|
|
if symbol is not None:
|
|
request['symbol'] = market['id']
|
|
if since is not None:
|
|
request['startTime'] = since
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
request, params = self.handle_until_option('endTime', request, params)
|
|
response = None
|
|
if isSpot:
|
|
#
|
|
# [
|
|
# {
|
|
# "symbol": "DOGEUSDT",
|
|
# "id": 736825748291060702,
|
|
# "orderId": 736825748215563230,
|
|
# "price": "0.09349",
|
|
# "qty": "250.0",
|
|
# "quoteQty": "23.3725",
|
|
# "commission": "0.0233725",
|
|
# "time": 1775672947953,
|
|
# "isBuyer": False
|
|
# }
|
|
# ]
|
|
#
|
|
response = self.privateGetApiV3MyTrades(self.extend(request, params))
|
|
else:
|
|
#
|
|
# [
|
|
# {
|
|
# "id": 737074389731770728,
|
|
# "orderId": 737074043320009064,
|
|
# "symbol": "DOGEUSDT",
|
|
# "buyer": True,
|
|
# "commission": "0.00183500",
|
|
# "commissionAsset": "USDT",
|
|
# "maker": True,
|
|
# "price": "0.09175",
|
|
# "qty": "100",
|
|
# "quoteQty": "9.17500",
|
|
# "realizedPnl": "0",
|
|
# "side": "BUY",
|
|
# "positionSide": "LONG",
|
|
# "time": 1775732228692
|
|
# }
|
|
# ]
|
|
#
|
|
response = self.contractPrivateGetCapiV3UserTrades(self.extend(request, params))
|
|
return self.parse_trades(response, market, since, limit)
|
|
|
|
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://www.weex.com/api-doc/spot/AccountAPI/GetBillRecords # spot
|
|
https://www.weex.com/api-doc/spot/AccountAPI/GetFundBillRecords # funding
|
|
https://www.weex.com/api-doc/contract/Account_API/GetContractBills # contract
|
|
|
|
:param str [code]: unified currency code, default is None
|
|
:param int [since]: timestamp in ms of the earliest ledger entry, default is None
|
|
:param int [limit]: max number of ledger entries to return, default is None, max is 100
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param int [params.until]: timestamp in ms of the latest ledger entry
|
|
:param str [params.type]: 'spot', 'funding' or 'swap'(default is 'spot')
|
|
: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-entry-structure>`
|
|
"""
|
|
self.load_markets()
|
|
paginate = False
|
|
paginate, params = self.handle_option_and_params(params, 'fetchLedger', 'paginate', False)
|
|
maxLimit = 100
|
|
if paginate:
|
|
return self.fetch_paginated_call_dynamic('fetchLedger', code, since, limit, params, maxLimit)
|
|
accountType = None
|
|
accountType, params = self.handle_market_type_and_params('fetchLedger', None, params)
|
|
accountsByType = self.safe_dict(self.options, 'accountsByType', {})
|
|
accountType = self.safe_string(accountsByType, accountType, accountType)
|
|
request: dict = {}
|
|
items = None
|
|
currency = None
|
|
if code is not None:
|
|
currency = self.currency(code)
|
|
if accountType == 'contract':
|
|
if code is not None:
|
|
request['currency'] = currency['id']
|
|
if since is not None:
|
|
request['startTime'] = since
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
request, params = self.handle_until_option('endTime', request, params)
|
|
contractResponse = self.contractPrivatePostCapiV3AccountIncome(self.extend(request, params))
|
|
items = self.safe_list(contractResponse, 'items', [])
|
|
elif accountType == 'funding':
|
|
if since is not None:
|
|
request['startTime'] = since
|
|
if limit is not None:
|
|
request['pageSize'] = limit
|
|
request, params = self.handle_until_option('endTime', request, params)
|
|
fundingResponse = self.privatePostApiV3AccountFundingBills(self.extend(request, params))
|
|
items = self.safe_list(fundingResponse, 'items', [])
|
|
else:
|
|
if since is not None:
|
|
request['after'] = since
|
|
if limit is not None:
|
|
request['limit'] = limit
|
|
request, params = self.handle_until_option('before', request, params)
|
|
items = self.privatePostApiV3AccountBills(self.extend(request, params))
|
|
return self.parse_ledger(items, currency, since, limit)
|
|
|
|
def parse_ledger_entry(self, item: dict, currency: Currency = None) -> LedgerEntry:
|
|
#
|
|
# spot
|
|
# {
|
|
# "billId": "736825748291061726",
|
|
# "coinId": 82,
|
|
# "coinName": "DOGE",
|
|
# "bizType": "trade_out",
|
|
# "fillSize": "250.0",
|
|
# "fillValue": "23.372500",
|
|
# "deltaAmount": "-250.0",
|
|
# "afterAmount": "49.70000000",
|
|
# "fees": "0",
|
|
# "cTime": "1775672947953"
|
|
# }
|
|
#
|
|
# contract
|
|
# {
|
|
# "billId": 736791763716407518,
|
|
# "asset": "USDT",
|
|
# "symbol": null,
|
|
# "income": "-90.00000000",
|
|
# "incomeType": "withdraw",
|
|
# "balance": "106.00000000",
|
|
# "fillFee": "0",
|
|
# "time": 1775664845399,
|
|
# "transferReason": "UNKNOWN_TRANSFER_REASON"
|
|
# }
|
|
#
|
|
# funding
|
|
# {
|
|
# "billId": "16502414",
|
|
# "coinId": 2,
|
|
# "coinName": "USDT",
|
|
# "bizType": "transfer_out",
|
|
# "fillSize": null,
|
|
# "fillValue": null,
|
|
# "deltaAmount": "-100.00000000",
|
|
# "afterAmount": "0.00000000",
|
|
# "fees": "0.00000000",
|
|
# "cTime": "1775664588931"
|
|
# }
|
|
#
|
|
currencyId = self.safe_string_2(item, 'coinName', 'asset')
|
|
code = self.safe_currency_code(currencyId, currency)
|
|
currency = self.safe_currency(currencyId, currency)
|
|
timestamp = self.safe_integer_2(item, 'cTime', 'time')
|
|
amountRaw = self.safe_string_2(item, 'deltaAmount', 'income')
|
|
after = self.safe_string_2(item, 'afterAmount', 'balance')
|
|
before = Precise.string_sub(after, amountRaw)
|
|
amount = self.parse_number(Precise.string_abs(amountRaw))
|
|
direction = 'in'
|
|
if amountRaw.find('-') >= 0:
|
|
direction = 'out'
|
|
rawType = self.safe_string_2(item, 'bizType', 'incomeType')
|
|
transferReason = self.safe_string(item, 'transferReason')
|
|
isContractEntry = (transferReason is not None)
|
|
if isContractEntry:
|
|
if (rawType == 'withdraw') or (rawType == 'deposit'):
|
|
rawType = 'transfer'
|
|
return self.safe_ledger_entry({
|
|
'info': item,
|
|
'id': self.safe_string(item, 'billId'),
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'direction': direction,
|
|
'account': None,
|
|
'referenceId': None,
|
|
'referenceAccount': None,
|
|
'type': self.parse_ledger_type(rawType),
|
|
'currency': code,
|
|
'amount': amount,
|
|
'before': self.parse_number(before),
|
|
'after': self.parse_number(after),
|
|
'status': None,
|
|
'fee': {
|
|
'currency': code,
|
|
'cost': self.safe_number_2(item, 'fees', 'fillFee'),
|
|
},
|
|
}, currency)
|
|
|
|
def parse_ledger_type(self, type: Str):
|
|
types: dict = {
|
|
'transfer_in': 'transfer',
|
|
'transfer_out': 'transfer',
|
|
'deposit': 'deposit',
|
|
'withdraw': 'withdrawal',
|
|
'trade_in': 'trade',
|
|
'trade_out': 'trade',
|
|
'position_open_long': 'trade',
|
|
'position_open_short': 'trade',
|
|
'position_close_long': 'trade',
|
|
'position_close_short': 'trade',
|
|
}
|
|
return self.safe_string(types, type, type)
|
|
|
|
def fetch_positions(self, symbols: Strings = None, params={}) -> List[Position]:
|
|
"""
|
|
fetch all open positions
|
|
|
|
https://www.weex.com/api-doc/contract/Account_API/GetAllPositions
|
|
|
|
: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>`
|
|
"""
|
|
self.load_markets()
|
|
symbols = self.market_symbols(symbols)
|
|
response = self.contractPrivateGetCapiV3AccountPositionAllPosition(params)
|
|
return self.parse_positions(response, symbols)
|
|
|
|
def fetch_position(self, symbol: str, params={}):
|
|
"""
|
|
fetch data on an open position
|
|
|
|
https://www.weex.com/api-doc/contract/Account_API/GetSinglePosition
|
|
|
|
:param str symbol: unified market symbol of the market the position is held in
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `position structure <https://docs.ccxt.com/?id=position-structure>`
|
|
"""
|
|
positions = self.fetch_positions_for_symbol(symbol, params)
|
|
return self.safe_dict(positions, 0)
|
|
|
|
def fetch_positions_for_symbol(self, symbol: str, params={}) -> List[Position]:
|
|
"""
|
|
fetch open positions for a single market
|
|
|
|
https://www.weex.com/api-doc/contract/Account_API/GetSinglePosition
|
|
|
|
fetch all open positions for specific symbol
|
|
:param str symbol: unified market symbol
|
|
: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>`
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'symbol': market['id'],
|
|
}
|
|
response = self.contractPrivateGetCapiV3AccountPositionSinglePosition(self.extend(request, params))
|
|
return self.parse_positions(response, [market['symbol']])
|
|
|
|
def parse_position(self, position: dict, market: Market = None):
|
|
#
|
|
# {
|
|
# "id": 737191855967437160,
|
|
# "asset": "USDT",
|
|
# "symbol": "DOGEUSDT",
|
|
# "side": "LONG",
|
|
# "marginType": "CROSSED",
|
|
# "separatedMode": "COMBINED",
|
|
# "separatedOpenOrderId": 0,
|
|
# "leverage": "20.00",
|
|
# "size": "300",
|
|
# "openValue": "27.96900",
|
|
# "openFee": "0.02237520",
|
|
# "fundingFee": "0",
|
|
# "marginSize": "100",
|
|
# "isolatedMargin": "0",
|
|
# "isAutoAppendIsolatedMargin": False,
|
|
# "cumOpenSize": "300",
|
|
# "cumOpenValue": "27.96900",
|
|
# "cumOpenFee": "0.02237520",
|
|
# "cumCloseSize": "0",
|
|
# "cumCloseValue": "0",
|
|
# "cumCloseFee": "0",
|
|
# "cumFundingFee": "0",
|
|
# "cumLiquidateFee": "0",
|
|
# "createdMatchSequenceId": 5762536243,
|
|
# "updatedMatchSequenceId": 5762741613,
|
|
# "createdTime": 1775760234825,
|
|
# "updatedTime": 1775763170789,
|
|
# "unrealizePnl": "0.00600",
|
|
# "liquidatePrice": "0"
|
|
# }
|
|
#
|
|
# watchPoisions
|
|
# {
|
|
# "id": "739004481374519656",
|
|
# "coin": "USDT",
|
|
# "symbol": "DOGEUSDT",
|
|
# "side": "LONG",
|
|
# "marginMode": "CROSSED",
|
|
# "separatedMode": "COMBINED",
|
|
# "separatedOpenOrderId": "0",
|
|
# "leverage": "11",
|
|
# "size": "100",
|
|
# "openValue": "9.31100",
|
|
# "openFee": "0.00744880",
|
|
# "fundingFee": "0",
|
|
# "isolatedMargin": "0",
|
|
# "autoAppendIsolatedMargin": False,
|
|
# "cumOpenSize": "100",
|
|
# "cumOpenValue": "9.31100",
|
|
# "cumOpenFee": "0.00744880",
|
|
# "cumCloseSize": "0",
|
|
# "cumCloseValue": "0",
|
|
# "cumCloseFee": "0",
|
|
# "cumFundingFee": "0",
|
|
# "cumLiquidateFee": "0",
|
|
# "createdMatchSequenceId": "5792711540",
|
|
# "updatedMatchSequenceId": "5792711540",
|
|
# "createdTime": "1776192398399",
|
|
# "updatedTime": "1776192398399"
|
|
# }
|
|
#
|
|
errorMessage = self.safe_string(position, 'errorMsg')
|
|
errorCode = self.safe_string(position, 'errorCode')
|
|
if errorMessage is not None:
|
|
self.handle_order_or_position_error(errorCode, errorMessage, position)
|
|
marketId = self.safe_string_2(position, 'symbol', 'coinId') # coinId might be used in testnet: https://github.com/ccxt/ccxt/issues/28576#issuecomment-4439400273
|
|
market = self.safe_market(marketId, market, None, 'contract')
|
|
timestamp = self.safe_integer(position, 'createdTime')
|
|
marginType = self.safe_string_2(position, 'marginType', 'marginMode')
|
|
marginMode = 'cross'
|
|
if marginType == 'ISOLATED':
|
|
marginMode = 'isolated'
|
|
separatedMode = self.safe_string(position, 'separatedMode')
|
|
hedged = None
|
|
if separatedMode == 'COMBINED':
|
|
hedged = False
|
|
elif separatedMode == 'SEPARATED':
|
|
hedged = True
|
|
notional = self.safe_string(position, 'openValue')
|
|
size = self.safe_string(position, 'size')
|
|
entryPrice = Precise.string_div(notional, size)
|
|
return self.safe_position({
|
|
'symbol': market['symbol'],
|
|
'id': self.safe_string_2(position, 'id', 'positionId'),
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'contracts': self.parse_number(size),
|
|
'contractSize': None,
|
|
'side': self.safe_string_lower(position, 'side'),
|
|
'notional': self.parse_number(notional),
|
|
'leverage': self.safe_number(position, 'leverage'),
|
|
'unrealizedPnl': self.safe_number(position, 'unrealizePnl'),
|
|
'realizedPnl': None,
|
|
'collateral': None,
|
|
'entryPrice': self.parse_number(entryPrice),
|
|
'markPrice': None,
|
|
'liquidationPrice': self.safe_number(position, 'liquidatePrice'),
|
|
'marginMode': marginMode,
|
|
'hedged': hedged,
|
|
'maintenanceMargin': None,
|
|
'maintenanceMarginPercentage': None,
|
|
'initialMargin': self.safe_number(position, 'marginSize'),
|
|
'initialMarginPercentage': None,
|
|
'marginRatio': None,
|
|
'lastUpdateTimestamp': self.safe_integer(position, 'updatedTime'),
|
|
'lastPrice': None,
|
|
'stopLossPrice': None,
|
|
'takeProfitPrice': None,
|
|
'percentage': None,
|
|
'info': position,
|
|
})
|
|
|
|
def close_all_positions(self, params={}) -> List[Position]:
|
|
"""
|
|
closes all open positions for a market type
|
|
|
|
https://www.weex.com/api-doc/contract/Transaction_API/ClosePositions
|
|
|
|
: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>`
|
|
"""
|
|
self.load_markets()
|
|
response = self.contractPrivatePostCapiV3ClosePositions(params)
|
|
#
|
|
# [
|
|
# {
|
|
# "positionId": 737191855967437160,
|
|
# "successOrderId": 737215340433375592,
|
|
# "errorMessage": "",
|
|
# "success": True
|
|
# }
|
|
# ]
|
|
#
|
|
return self.parse_positions(response)
|
|
|
|
def close_position(self, symbol: str, side: OrderSide = None, params={}) -> Order:
|
|
"""
|
|
closes open positions for a market
|
|
|
|
https://www.weex.com/api-doc/contract/Transaction_API/ClosePositions
|
|
|
|
:param str symbol: Unified CCXT market symbol
|
|
:param str [side]: not used by current exchange
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: an `order structure <https://docs.ccxt.com/?id=order-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'symbol': market['id'],
|
|
}
|
|
response = self.contractPrivatePostCapiV3ClosePositions(self.extend(request, params))
|
|
orders = self.parse_orders(response, market)
|
|
return self.safe_dict(orders, 0)
|
|
|
|
def fetch_trading_fee(self, symbol: str, params={}) -> TradingFeeInterface:
|
|
"""
|
|
|
|
https://www.weex.com/api-doc/contract/Account_API/GetCommissionRate # contract
|
|
|
|
fetch the trading fees for a contract market
|
|
:param str symbol: unified market symbol
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `fee structure <https://docs.ccxt.com/?id=fee-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
if market['spot']:
|
|
# spot markets return 0 for fees
|
|
raise NotSupported(self.id + ' fetchTradingFee() is not supported for spot markets')
|
|
request: dict = {
|
|
'symbol': market['id'],
|
|
}
|
|
response = self.contractPrivateGetCapiV3AccountCommissionRate(self.extend(request, params))
|
|
#
|
|
# {
|
|
# "symbol": "DOGEUSDT",
|
|
# "makerCommissionRate": "0.0002",
|
|
# "takerCommissionRate": "0.0008"
|
|
# }
|
|
#
|
|
return self.parse_trading_fee(response, market)
|
|
|
|
def parse_trading_fee(self, fee: dict, market: Market = None) -> TradingFeeInterface:
|
|
#
|
|
# contract
|
|
# {
|
|
# "symbol": "DOGEUSDT",
|
|
# "makerCommissionRate": "0.0002",
|
|
# "takerCommissionRate": "0.0008"
|
|
# }
|
|
#
|
|
marketId = self.safe_string(fee, 'symbol')
|
|
return {
|
|
'info': fee,
|
|
'symbol': self.safe_symbol(marketId, market, None, 'contract'),
|
|
'maker': self.safe_number(fee, 'makerCommissionRate'),
|
|
'taker': self.safe_number(fee, 'takerCommissionRate'),
|
|
'percentage': True,
|
|
'tierBased': True,
|
|
}
|
|
|
|
def fetch_margin_mode(self, symbol: str, params={}) -> MarginMode:
|
|
"""
|
|
fetches the margin mode of a specific symbol
|
|
|
|
https://www.weex.com/api-doc/contract/Account_API/GetSymbolConfig
|
|
|
|
: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: a `margin mode structure <https://docs.ccxt.com/?id=margin-mode-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'symbol': market['id'],
|
|
}
|
|
response = self.contractPrivateGetCapiV3AccountSymbolConfig(self.extend(request, params))
|
|
#
|
|
# [
|
|
# {
|
|
# "symbol": "DOGEUSDT",
|
|
# "marginType": "CROSSED",
|
|
# "separatedType": "COMBINED",
|
|
# "crossLeverage": "20.00",
|
|
# "isolatedLongLeverage": "20.00",
|
|
# "isolatedShortLeverage": "20.00"
|
|
# }
|
|
# ]
|
|
#
|
|
marginMode = self.safe_dict(response, 0, {})
|
|
return self.parse_margin_mode(marginMode, market)
|
|
|
|
def fetch_margin_modes(self, symbols: Strings = None, params={}) -> MarginModes:
|
|
"""
|
|
fetches margin modes the symbols, with symbols=None all markets are returned
|
|
|
|
https://www.weex.com/api-doc/contract/Account_API/GetSymbolConfig
|
|
|
|
:param str[] symbols: unified market symbols
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a list of `margin mode structures <https://docs.ccxt.com/?id=margin-mode-structure>`
|
|
"""
|
|
self.load_markets()
|
|
symbols = self.market_symbols(symbols)
|
|
response = self.contractPrivateGetCapiV3AccountSymbolConfig(params)
|
|
return self.parse_margin_modes(response, symbols, 'symbol', 'swap')
|
|
|
|
def parse_margin_mode(self, marginMode: dict, market=None) -> MarginMode:
|
|
marketId = self.safe_string(marginMode, 'symbol')
|
|
marginType = self.safe_string(marginMode, 'marginType')
|
|
return {
|
|
'info': marginMode,
|
|
'symbol': self.safe_symbol(marketId, market, None, 'swap'),
|
|
'marginMode': self.parse_margin_type(marginType),
|
|
}
|
|
|
|
def parse_margin_type(self, marginType: Str):
|
|
marginTypes: dict = {
|
|
'CROSSED': 'cross',
|
|
'ISOLATED': 'isolated',
|
|
}
|
|
return self.safe_string(marginTypes, marginType, marginType)
|
|
|
|
def set_margin_mode(self, marginMode: str, symbol: Str = None, params={}):
|
|
"""
|
|
set margin mode to 'cross' or 'isolated'
|
|
|
|
https://www.weex.com/api-doc/contract/Account_API/ChangeMarginModeTRADE
|
|
|
|
:param str marginMode: 'cross' or 'isolated'
|
|
: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 + ' setMarginMode() requires a symbol argument')
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'symbol': market['id'],
|
|
'marginType': self.encode_margin_mode(marginMode),
|
|
}
|
|
return self.contractPrivatePostCapiV3AccountMarginType(self.extend(request, params))
|
|
|
|
def encode_margin_mode(self, marginMode: Str):
|
|
marginTypes: dict = {
|
|
'cross': 'CROSSED',
|
|
'isolated': 'ISOLATED',
|
|
}
|
|
result = self.safe_string(marginTypes, marginMode)
|
|
if result is None:
|
|
raise ArgumentsRequired(self.id + ' marginMode must be either cross or isolated')
|
|
return result
|
|
|
|
def fetch_leverage(self, symbol: str, params={}) -> Leverage:
|
|
"""
|
|
fetch the set leverage for a market
|
|
|
|
https://www.weex.com/api-doc/contract/Account_API/GetSymbolConfig
|
|
|
|
:param str symbol: unified market symbol
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `leverage structure <https://docs.ccxt.com/?id=leverage-structure>`
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'symbol': market['id'],
|
|
}
|
|
response = self.contractPrivateGetCapiV3AccountSymbolConfig(self.extend(request, params))
|
|
marginMode = self.safe_dict(response, 0, {})
|
|
return self.parse_leverage(marginMode, market)
|
|
|
|
def fetch_leverages(self, symbols: Strings = None, params={}) -> Leverages:
|
|
"""
|
|
fetch the set leverage for all markets
|
|
|
|
https://www.weex.com/api-doc/contract/Account_API/GetSymbolConfig
|
|
|
|
:param str[] [symbols]: a list of unified market symbols
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a list of `leverage structures <https://docs.ccxt.com/?id=leverage-structure>`
|
|
"""
|
|
self.load_markets()
|
|
symbols = self.market_symbols(symbols)
|
|
response = self.contractPrivateGetCapiV3AccountSymbolConfig(params)
|
|
return self.parse_leverages(response, symbols, 'symbol', 'swap')
|
|
|
|
def parse_leverage(self, leverage: dict, market: Market = None) -> Leverage:
|
|
marketId = self.safe_string(leverage, 'symbol')
|
|
marginType = self.safe_string(leverage, 'marginType')
|
|
marginMode = self.parse_margin_type(marginType)
|
|
crossLeverage = self.safe_number(leverage, 'crossLeverage')
|
|
longLeverage = self.safe_number(leverage, 'isolatedLongLeverage')
|
|
shortLeverage = self.safe_number(leverage, 'isolatedShortLeverage')
|
|
if marginMode == 'cross':
|
|
longLeverage = crossLeverage
|
|
shortLeverage = crossLeverage
|
|
return {
|
|
'info': leverage,
|
|
'symbol': self.safe_symbol(marketId, market, None, 'swap'),
|
|
'marginMode': marginMode,
|
|
'longLeverage': longLeverage,
|
|
'shortLeverage': shortLeverage,
|
|
}
|
|
|
|
def set_leverage(self, leverage: int, symbol: Str = None, params={}):
|
|
"""
|
|
set the level of leverage for a market
|
|
|
|
https://www.weex.com/api-doc/contract/Account_API/UpdateLeverageTRADE
|
|
|
|
: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.marginMode]: 'cross' or 'isolated'(default is 'cross' if specific leverage parameters are not provided)
|
|
:param number [params.crossLeverage]: *cross margin mode only* leverage for cross margin mode when marginMode is 'cross'
|
|
:param number [params.isolatedLongLeverage]: *isolated margin mode only* leverage for long positions when marginMode is 'isolated'
|
|
:param number [params.isolatedShortLeverage]: *isolated margin mode only* leverage for short positions when marginMode is 'isolated'
|
|
If specific leverage parameters are not provided
|
|
the leverage value will be applied to both long and short positions if marginMode is 'isolated'
|
|
or to cross margin mode if marginMode is 'cross'
|
|
If marginMode is not provided and specific leverage parameters are not provided too
|
|
the leverage value will be applied to cross leverage
|
|
: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)
|
|
request: dict = {
|
|
'symbol': market['id'],
|
|
}
|
|
marginMode = None
|
|
marginMode, params = self.handle_margin_mode_and_params('setLeverage', params)
|
|
if marginMode is not None:
|
|
request['marginType'] = self.encode_margin_mode(marginMode)
|
|
isolatedLongLeverage = self.safe_number(params, 'isolatedLongLeverage')
|
|
isolatedShortLeverage = self.safe_number(params, 'isolatedShortLeverage')
|
|
crossLeverage = self.safe_number(params, 'crossLeverage')
|
|
if (isolatedLongLeverage is None) and (isolatedShortLeverage is None) and (crossLeverage is None):
|
|
if marginMode == 'isolated':
|
|
request['isolatedLongLeverage'] = leverage
|
|
request['isolatedShortLeverage'] = leverage
|
|
else:
|
|
request['crossLeverage'] = leverage
|
|
return self.contractPrivatePostCapiV3AccountLeverage(self.extend(request, params))
|
|
|
|
def fetch_position_mode(self, symbol: Str = None, params={}):
|
|
"""
|
|
fetchs the position mode, hedged or one way
|
|
|
|
https://www.weex.com/api-doc/contract/Account_API/GetSymbolConfig
|
|
|
|
: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
|
|
:returns dict: an object detailing whether the market is in hedged or one-way mode
|
|
"""
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'symbol': market['id'],
|
|
}
|
|
response = self.contractPrivateGetCapiV3AccountSymbolConfig(self.extend(request, params))
|
|
entry = self.safe_dict(response, 0, {})
|
|
separatedType = self.safe_string(entry, 'separatedType')
|
|
return {
|
|
'info': response,
|
|
'hedged': (separatedType == 'SEPARATED'),
|
|
}
|
|
|
|
def set_position_mode(self, hedged: bool, symbol: Str = None, params={}):
|
|
"""
|
|
set hedged to True or False for a market
|
|
|
|
https://www.weex.com/api-doc/contract/Account_API/ChangeMarginModeTRADE
|
|
|
|
:param bool hedged: set to True to use dualSidePosition
|
|
:param str symbol: unified market symbol
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str params['marginMode']: 'cross' or 'isolated'(default is 'cross')
|
|
:returns dict: response from the exchange
|
|
"""
|
|
if symbol is None:
|
|
raise ArgumentsRequired(self.id + ' setPositionMode() requires a symbol argument')
|
|
self.load_markets()
|
|
market = self.market(symbol)
|
|
marginMode = None
|
|
marginMode, params = self.handle_margin_mode_and_params('setPositionMode', params)
|
|
if marginMode is None:
|
|
raise ArgumentsRequired(self.id + ' setPositionMode() also sets marginMode, so a marginMode parameter is required')
|
|
separatedType = 'SEPARATED' if hedged else 'COMBINED'
|
|
request: dict = {
|
|
'symbol': market['id'],
|
|
'marginType': self.encode_margin_mode(marginMode),
|
|
'separatedType': separatedType,
|
|
}
|
|
return self.contractPrivatePostCapiV3AccountMarginType(self.extend(request, params))
|
|
|
|
def modify_margin_helper(self, symbol: str, amount, type, params={}) -> MarginModification:
|
|
self.load_markets()
|
|
isolatedPositionId = self.safe_string_n(params, ['positionId', 'id', 'isolatedPositionId'])
|
|
if isolatedPositionId is None:
|
|
raise ArgumentsRequired(self.id + ' modifyMarginHelper() requires a positionId parameter')
|
|
params = self.omit(params, ['positionId', 'id'])
|
|
market = self.market(symbol)
|
|
request: dict = {
|
|
'isolatedPositionId': isolatedPositionId,
|
|
'amount': self.cost_to_precision(symbol, amount),
|
|
'type': type,
|
|
}
|
|
parsedType = 'add' if (type == 1) else 'reduce'
|
|
response = self.contractPrivatePostCapiV3AccountPositionMargin(self.extend(request, params))
|
|
return self.extend(self.parse_margin_modification(response, market), {
|
|
'amount': self.parse_number(amount),
|
|
'type': parsedType,
|
|
})
|
|
|
|
def parse_margin_modification(self, data: dict, market: Market = None) -> MarginModification:
|
|
#
|
|
# {
|
|
# "code": "200",
|
|
# "msg": "success",
|
|
# "requestTime": 1764505776347
|
|
# }
|
|
#
|
|
msg = self.safe_string(data, 'msg')
|
|
status = 'ok' if (msg == 'success') else 'failed'
|
|
timestamp = self.safe_integer(data, 'requestTime')
|
|
return {
|
|
'info': data,
|
|
'symbol': market['symbol'],
|
|
'type': None,
|
|
'marginMode': 'isolated',
|
|
'amount': None,
|
|
'total': None,
|
|
'code': market['settle'],
|
|
'status': status,
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
}
|
|
|
|
def reduce_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
|
|
"""
|
|
remove margin from a position
|
|
|
|
https://www.weex.com/api-doc/contract/Account_API/AdjustPositionMarginTRADE
|
|
|
|
:param str symbol: unified market symbol
|
|
:param float amount: the amount of margin to remove
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str params['positionId']: the id of the position to reduce margin from, required
|
|
:returns dict: a `margin structure <https://docs.ccxt.com/?id=margin-structure>`
|
|
"""
|
|
return self.modify_margin_helper(symbol, amount, 2, params)
|
|
|
|
def add_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
|
|
"""
|
|
add margin
|
|
|
|
https://www.weex.com/api-doc/contract/Account_API/AdjustPositionMarginTRADE
|
|
|
|
:param str symbol: unified market symbol
|
|
:param float amount: amount of margin to add
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str params['positionId']: the id of the position to add margin to, required
|
|
:returns dict: a `margin structure <https://docs.ccxt.com/?id=margin-structure>`
|
|
"""
|
|
return self.modify_margin_helper(symbol, amount, 1, params)
|
|
|
|
def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
|
|
endpoint = self.implode_params(path, params)
|
|
query = self.omit(params, self.extract_params(path))
|
|
isBatch = (path.find('batch') >= 0)
|
|
if not isBatch and ((method == 'GET') or (method == 'DELETE')):
|
|
if query:
|
|
endpoint += '?' + self.urlencode(query)
|
|
if (api == 'private') or (api == 'contractPrivate'):
|
|
self.check_required_credentials()
|
|
timestamp = self.number_to_string(self.nonce())
|
|
payload = timestamp + method + '/' + endpoint
|
|
if (method == 'POST') or isBatch:
|
|
body = self.json(query)
|
|
payload += body
|
|
signature = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha256, 'base64')
|
|
headers = {
|
|
'ACCESS-KEY': self.apiKey,
|
|
'ACCESS-SIGN': signature,
|
|
'ACCESS-PASSPHRASE': self.password,
|
|
'ACCESS-TIMESTAMP': timestamp,
|
|
}
|
|
if (method == 'POST') or (method == 'DELETE'):
|
|
headers['Content-Type'] = 'application/json'
|
|
else:
|
|
headers = {
|
|
'User-Agent': 'ccxt',
|
|
}
|
|
url = self.urls['api'][api] + '/' + endpoint
|
|
return {'url': url, 'method': method, 'body': body, 'headers': headers}
|
|
|
|
def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
|
|
#
|
|
# {
|
|
# "code": -1140,
|
|
# "msg": "Either orderId or origClientOrderId must be sent."
|
|
# }
|
|
#
|
|
message = self.safe_string(response, 'msg')
|
|
if message is not None:
|
|
errorCode = self.safe_string(response, 'code')
|
|
feedback = self.id + ' ' + body
|
|
self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
|
|
self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
|
|
self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
|
|
raise ExchangeError(self.id + ' ' + body)
|
|
return None
|