/python/ccxt/binance.py
https://github.com/kroitor/ccxt · Python · 2396 lines · 1563 code · 47 blank · 786 comment · 245 complexity · abde173efb0eefcaa0c3a83cd9cbb0c8 MD5 · raw file
Large files are truncated click here to view the full file
- # -*- 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
- import math
- import json
- from ccxt.base.errors import ExchangeError
- from ccxt.base.errors import AuthenticationError
- from ccxt.base.errors import PermissionDenied
- from ccxt.base.errors import AccountSuspended
- 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 InvalidAddress
- from ccxt.base.errors import InvalidOrder
- from ccxt.base.errors import OrderNotFound
- from ccxt.base.errors import OrderImmediatelyFillable
- from ccxt.base.errors import NotSupported
- from ccxt.base.errors import DDoSProtection
- from ccxt.base.errors import RateLimitExceeded
- from ccxt.base.errors import ExchangeNotAvailable
- from ccxt.base.errors import InvalidNonce
- from ccxt.base.decimal_to_precision import ROUND
- from ccxt.base.decimal_to_precision import TRUNCATE
- class binance(Exchange):
- def describe(self):
- return self.deep_extend(super(binance, self).describe(), {
- 'id': 'binance',
- 'name': 'Binance',
- 'countries': ['JP', 'MT'], # Japan, Malta
- 'rateLimit': 500,
- 'certified': True,
- 'pro': True,
- # new metainfo interface
- 'has': {
- 'cancelAllOrders': True,
- 'cancelOrder': True,
- 'CORS': False,
- 'createOrder': True,
- 'fetchBalance': True,
- 'fetchBidsAsks': True,
- 'fetchClosedOrders': 'emulated',
- 'fetchDepositAddress': True,
- 'fetchDeposits': True,
- 'fetchFundingFees': True,
- 'fetchMarkets': True,
- 'fetchMyTrades': True,
- 'fetchOHLCV': True,
- 'fetchOpenOrders': True,
- 'fetchOrder': True,
- 'fetchOrders': True,
- 'fetchOrderBook': True,
- 'fetchStatus': True,
- 'fetchTicker': True,
- 'fetchTickers': True,
- 'fetchTime': True,
- 'fetchTrades': True,
- 'fetchTradingFee': True,
- 'fetchTradingFees': True,
- 'fetchTransactions': False,
- 'fetchWithdrawals': True,
- 'withdraw': True,
- },
- 'timeframes': {
- '1m': '1m',
- '3m': '3m',
- '5m': '5m',
- '15m': '15m',
- '30m': '30m',
- '1h': '1h',
- '2h': '2h',
- '4h': '4h',
- '6h': '6h',
- '8h': '8h',
- '12h': '12h',
- '1d': '1d',
- '3d': '3d',
- '1w': '1w',
- '1M': '1M',
- },
- 'urls': {
- 'logo': 'https://user-images.githubusercontent.com/1294454/29604020-d5483cdc-87ee-11e7-94c7-d1a8d9169293.jpg',
- 'test': {
- 'dapiPublic': 'https://testnet.binancefuture.com/dapi/v1',
- 'dapiPrivate': 'https://testnet.binancefuture.com/dapi/v1',
- 'fapiPublic': 'https://testnet.binancefuture.com/fapi/v1',
- 'fapiPrivate': 'https://testnet.binancefuture.com/fapi/v1',
- 'fapiPrivateV2': 'https://testnet.binancefuture.com/fapi/v2',
- 'public': 'https://testnet.binance.vision/api/v3',
- 'private': 'https://testnet.binance.vision/api/v3',
- 'v3': 'https://testnet.binance.vision/api/v3',
- 'v1': 'https://testnet.binance.vision/api/v1',
- },
- 'api': {
- 'wapi': 'https://api.binance.com/wapi/v3',
- 'sapi': 'https://api.binance.com/sapi/v1',
- 'dapiPublic': 'https://dapi.binance.com/dapi/v1',
- 'dapiPrivate': 'https://dapi.binance.com/dapi/v1',
- 'dapiData': 'https://dapi.binance.com/futures/data',
- 'fapiPublic': 'https://fapi.binance.com/fapi/v1',
- 'fapiPrivate': 'https://fapi.binance.com/fapi/v1',
- 'fapiData': 'https://fapi.binance.com/futures/data',
- 'fapiPrivateV2': 'https://fapi.binance.com/fapi/v2',
- 'public': 'https://api.binance.com/api/v3',
- 'private': 'https://api.binance.com/api/v3',
- 'v3': 'https://api.binance.com/api/v3',
- 'v1': 'https://api.binance.com/api/v1',
- },
- 'www': 'https://www.binance.com',
- 'referral': 'https://www.binance.com/?ref=10205187',
- 'doc': [
- 'https://binance-docs.github.io/apidocs/spot/en',
- ],
- 'api_management': 'https://www.binance.com/en/usercenter/settings/api-management',
- 'fees': 'https://www.binance.com/en/fee/schedule',
- },
- 'api': {
- # the API structure below will need 3-layer apidefs
- 'sapi': {
- 'get': [
- 'accountSnapshot',
- # these endpoints require self.apiKey
- 'margin/asset',
- 'margin/pair',
- 'margin/allAssets',
- 'margin/allPairs',
- 'margin/priceIndex',
- # these endpoints require self.apiKey + self.secret
- 'asset/assetDividend',
- 'margin/loan',
- 'margin/repay',
- 'margin/account',
- 'margin/transfer',
- 'margin/interestHistory',
- 'margin/forceLiquidationRec',
- 'margin/order',
- 'margin/openOrders',
- 'margin/allOrders',
- 'margin/myTrades',
- 'margin/maxBorrowable',
- 'margin/maxTransferable',
- 'margin/isolated/transfer',
- 'margin/isolated/account',
- 'margin/isolated/pair',
- 'margin/isolated/allPairs',
- 'futures/transfer',
- 'futures/loan/borrow/history',
- 'futures/loan/repay/history',
- 'futures/loan/wallet',
- 'futures/loan/configs',
- 'futures/loan/calcAdjustLevel',
- 'futures/loan/calcMaxAdjustAmount',
- 'futures/loan/adjustCollateral/history',
- 'futures/loan/liquidationHistory',
- # https://binance-docs.github.io/apidocs/spot/en/#withdraw-sapi
- 'capital/config/getall', # get networks for withdrawing USDT ERC20 vs USDT Omni
- 'capital/deposit/address',
- 'capital/deposit/hisrec',
- 'capital/deposit/subAddress',
- 'capital/deposit/subHisrec',
- 'capital/withdraw/history',
- 'sub-account/futures/account',
- 'sub-account/futures/accountSummary',
- 'sub-account/futures/positionRisk',
- 'sub-account/futures/internalTransfer',
- 'sub-account/margin/account',
- 'sub-account/margin/accountSummary',
- 'sub-account/spotSummary',
- 'sub-account/status',
- 'sub-account/transfer/subUserHistory',
- # lending endpoints
- 'lending/daily/product/list',
- 'lending/daily/userLeftQuota',
- 'lending/daily/userRedemptionQuota',
- 'lending/daily/token/position',
- 'lending/union/account',
- 'lending/union/purchaseRecord',
- 'lending/union/redemptionRecord',
- 'lending/union/interestHistory',
- 'lending/project/list',
- 'lending/project/position/list',
- # mining endpoints
- 'mining/pub/algoList',
- 'mining/pub/coinList',
- 'mining/worker/detail',
- 'mining/worker/list',
- 'mining/payment/list',
- 'mining/statistics/user/status',
- 'mining/statistics/user/list',
- ],
- 'post': [
- 'asset/dust',
- 'account/disableFastWithdrawSwitch',
- 'account/enableFastWithdrawSwitch',
- 'capital/withdraw/apply',
- 'margin/transfer',
- 'margin/loan',
- 'margin/repay',
- 'margin/order',
- 'margin/isolated/create',
- 'margin/isolated/transfer',
- 'sub-account/margin/transfer',
- 'sub-account/margin/enable',
- 'sub-account/margin/enable',
- 'sub-account/futures/enable',
- 'sub-account/futures/transfer',
- 'sub-account/futures/internalTransfer',
- 'sub-account/transfer/subToSub',
- 'sub-account/transfer/subToMaster',
- 'userDataStream',
- 'userDataStream/isolated',
- 'futures/transfer',
- 'futures/loan/borrow',
- 'futures/loan/repay',
- 'futures/loan/adjustCollateral',
- # lending
- 'lending/customizedFixed/purchase',
- 'lending/daily/purchase',
- 'lending/daily/redeem',
- ],
- 'put': [
- 'userDataStream',
- 'userDataStream/isolated',
- ],
- 'delete': [
- 'margin/order',
- 'userDataStream',
- 'userDataStream/isolated',
- ],
- },
- 'wapi': {
- 'post': [
- 'withdraw',
- 'sub-account/transfer',
- ],
- 'get': [
- 'depositHistory',
- 'withdrawHistory',
- 'depositAddress',
- 'accountStatus',
- 'systemStatus',
- 'apiTradingStatus',
- 'userAssetDribbletLog',
- 'tradeFee',
- 'assetDetail',
- 'sub-account/list',
- 'sub-account/transfer/history',
- 'sub-account/assets',
- ],
- },
- 'dapiPublic': {
- 'get': [
- 'ping',
- 'time',
- 'exchangeInfo',
- 'depth',
- 'trades',
- 'historicalTrades',
- 'aggTrades',
- 'premiumIndex',
- 'fundingRate',
- 'klines',
- 'continuousKlines',
- 'indexPriceKlines',
- 'markPriceKlines',
- 'ticker/24hr',
- 'ticker/price',
- 'ticker/bookTicker',
- 'allForceOrders',
- 'openInterest',
- ],
- },
- 'dapiData': {
- 'get': [
- 'openInterestHist',
- 'topLongShortAccountRatio',
- 'topLongShortPositionRatio',
- 'globalLongShortAccountRatio',
- 'takerBuySellVol',
- 'basis',
- ],
- },
- 'dapiPrivate': {
- 'get': [
- 'positionSide/dual',
- 'order',
- 'openOrder',
- 'openOrders',
- 'allOrders',
- 'balance',
- 'account',
- 'positionMargin/history',
- 'positionRisk',
- 'userTrades',
- 'income',
- 'leverageBracket',
- 'forceOrders',
- 'adlQuantile',
- ],
- 'post': [
- 'positionSide/dual',
- 'order',
- 'batchOrders',
- 'countdownCancelAll',
- 'leverage',
- 'marginType',
- 'positionMargin',
- 'listenKey',
- ],
- 'put': [
- 'listenKey',
- ],
- 'delete': [
- 'order',
- 'allOpenOrders',
- 'batchOrders',
- 'listenKey',
- ],
- },
- 'fapiPublic': {
- 'get': [
- 'ping',
- 'time',
- 'exchangeInfo',
- 'depth',
- 'trades',
- 'historicalTrades',
- 'aggTrades',
- 'klines',
- 'fundingRate',
- 'premiumIndex',
- 'ticker/24hr',
- 'ticker/price',
- 'ticker/bookTicker',
- 'allForceOrders',
- 'openInterest',
- ],
- },
- 'fapiData': {
- 'get': [
- 'openInterestHist',
- 'topLongShortAccountRatio',
- 'topLongShortPositionRatio',
- 'globalLongShortAccountRatio',
- 'takerlongshortRatio',
- ],
- },
- 'fapiPrivate': {
- 'get': [
- 'allForceOrders',
- 'allOrders',
- 'openOrder',
- 'openOrders',
- 'order',
- 'account',
- 'balance',
- 'leverageBracket',
- 'positionMargin/history',
- 'positionRisk',
- 'positionSide/dual',
- 'userTrades',
- 'income',
- ],
- 'post': [
- 'batchOrders',
- 'positionSide/dual',
- 'positionMargin',
- 'marginType',
- 'order',
- 'leverage',
- 'listenKey',
- 'countdownCancelAll',
- ],
- 'put': [
- 'listenKey',
- ],
- 'delete': [
- 'batchOrders',
- 'order',
- 'allOpenOrders',
- 'listenKey',
- ],
- },
- 'fapiPrivateV2': {
- 'get': [
- 'account',
- 'balance',
- 'positionRisk',
- ],
- },
- 'v3': {
- 'get': [
- 'ticker/price',
- 'ticker/bookTicker',
- ],
- },
- 'public': {
- 'get': [
- 'ping',
- 'time',
- 'depth',
- 'trades',
- 'aggTrades',
- 'historicalTrades',
- 'klines',
- 'ticker/24hr',
- 'ticker/price',
- 'ticker/bookTicker',
- 'exchangeInfo',
- ],
- 'put': ['userDataStream'],
- 'post': ['userDataStream'],
- 'delete': ['userDataStream'],
- },
- 'private': {
- 'get': [
- 'allOrderList', # oco
- 'openOrderList', # oco
- 'orderList', # oco
- 'order',
- 'openOrders',
- 'allOrders',
- 'account',
- 'myTrades',
- ],
- 'post': [
- 'order/oco',
- 'order',
- 'order/test',
- ],
- 'delete': [
- 'openOrders', # added on 2020-04-25 for canceling all open orders per symbol
- 'orderList', # oco
- 'order',
- ],
- },
- },
- 'fees': {
- 'trading': {
- 'tierBased': False,
- 'percentage': True,
- 'taker': 0.001,
- 'maker': 0.001,
- },
- },
- 'commonCurrencies': {
- 'BCC': 'BCC', # kept for backward-compatibility https://github.com/ccxt/ccxt/issues/4848
- 'YOYO': 'YOYOW',
- },
- # exchange-specific options
- 'options': {
- # 'fetchTradesMethod': 'publicGetAggTrades', # publicGetTrades, publicGetHistoricalTrades
- 'defaultTimeInForce': 'GTC', # 'GTC' = Good To Cancel(default), 'IOC' = Immediate Or Cancel
- 'defaultType': 'spot', # 'spot', 'future', 'margin', 'delivery'
- 'hasAlreadyAuthenticatedSuccessfully': False,
- 'warnOnFetchOpenOrdersWithoutSymbol': True,
- 'recvWindow': 5 * 1000, # 5 sec, binance default
- 'timeDifference': 0, # the difference between system clock and Binance clock
- 'adjustForTimeDifference': False, # controls the adjustment logic upon instantiation
- 'parseOrderToPrecision': False, # force amounts and costs in parseOrder to precision
- 'newOrderRespType': {
- 'market': 'FULL', # 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills
- 'limit': 'RESULT', # we change it from 'ACK' by default to 'RESULT'
- },
- 'quoteOrderQty': True, # whether market orders support amounts in quote currency
- },
- # https://binance-docs.github.io/apidocs/spot/en/#error-codes-2
- 'exceptions': {
- 'API key does not exist': AuthenticationError,
- 'Order would trigger immediately.': OrderImmediatelyFillable,
- 'Order would immediately match and take.': OrderImmediatelyFillable, # {"code":-2010,"msg":"Order would immediately match and take."}
- 'Account has insufficient balance for requested action.': InsufficientFunds,
- 'Rest API trading is not enabled.': ExchangeNotAvailable,
- "You don't have permission.": PermissionDenied, # {"msg":"You don't have permission.","success":false}
- 'Market is closed.': ExchangeNotAvailable, # {"code":-1013,"msg":"Market is closed."}
- 'Too many requests.': DDoSProtection, # {"msg":"Too many requests. Please try again later.","success":false}
- '-1000': ExchangeNotAvailable, # {"code":-1000,"msg":"An unknown error occured while processing the request."}
- '-1001': ExchangeNotAvailable, # 'Internal error; unable to process your request. Please try again.'
- '-1002': AuthenticationError, # 'You are not authorized to execute self request.'
- '-1003': RateLimitExceeded, # {"code":-1003,"msg":"Too much request weight used, current limit is 1200 request weight per 1 MINUTE. Please use the websocket for live updates to avoid polling the API."}
- '-1013': InvalidOrder, # createOrder -> 'invalid quantity'/'invalid price'/MIN_NOTIONAL
- '-1015': RateLimitExceeded, # 'Too many new orders; current limit is %s orders per %s.'
- '-1016': ExchangeNotAvailable, # 'This service is no longer available.',
- '-1020': BadRequest, # 'This operation is not supported.'
- '-1021': InvalidNonce, # 'your time is ahead of server'
- '-1022': AuthenticationError, # {"code":-1022,"msg":"Signature for self request is not valid."}
- '-1100': BadRequest, # createOrder(symbol, 1, asdf) -> 'Illegal characters found in parameter 'price'
- '-1101': BadRequest, # Too many parameters; expected %s and received %s.
- '-1102': BadRequest, # Param %s or %s must be sent, but both were empty
- '-1103': BadRequest, # An unknown parameter was sent.
- '-1104': BadRequest, # Not all sent parameters were read, read 8 parameters but was sent 9
- '-1105': BadRequest, # Parameter %s was empty.
- '-1106': BadRequest, # Parameter %s sent when not required.
- '-1111': BadRequest, # Precision is over the maximum defined for self asset.
- '-1112': InvalidOrder, # No orders on book for symbol.
- '-1114': BadRequest, # TimeInForce parameter sent when not required.
- '-1115': BadRequest, # Invalid timeInForce.
- '-1116': BadRequest, # Invalid orderType.
- '-1117': BadRequest, # Invalid side.
- '-1118': BadRequest, # New client order ID was empty.
- '-1119': BadRequest, # Original client order ID was empty.
- '-1120': BadRequest, # Invalid interval.
- '-1121': BadSymbol, # Invalid symbol.
- '-1125': AuthenticationError, # This listenKey does not exist.
- '-1127': BadRequest, # More than %s hours between startTime and endTime.
- '-1128': BadRequest, # {"code":-1128,"msg":"Combination of optional parameters invalid."}
- '-1130': BadRequest, # Data sent for paramter %s is not valid.
- '-1131': BadRequest, # recvWindow must be less than 60000
- '-2010': ExchangeError, # generic error code for createOrder -> 'Account has insufficient balance for requested action.', {"code":-2010,"msg":"Rest API trading is not enabled."}, etc...
- '-2011': OrderNotFound, # cancelOrder(1, 'BTC/USDT') -> 'UNKNOWN_ORDER'
- '-2013': OrderNotFound, # fetchOrder(1, 'BTC/USDT') -> 'Order does not exist'
- '-2014': AuthenticationError, # {"code":-2014, "msg": "API-key format invalid."}
- '-2015': AuthenticationError, # "Invalid API-key, IP, or permissions for action."
- '-3005': InsufficientFunds, # {"code":-3005,"msg":"Transferring out not allowed. Transfer out amount exceeds max amount."}
- '-3008': InsufficientFunds, # {"code":-3008,"msg":"Borrow not allowed. Your borrow amount has exceed maximum borrow amount."}
- '-3010': ExchangeError, # {"code":-3010,"msg":"Repay not allowed. Repay amount exceeds borrow amount."}
- '-3022': AccountSuspended, # You account's trading is banned.
- },
- })
- def nonce(self):
- return self.milliseconds() - self.options['timeDifference']
- def fetch_time(self, params={}):
- type = self.safe_string_2(self.options, 'fetchTime', 'defaultType', 'spot')
- method = 'publicGetTime'
- if type == 'future':
- method = 'fapiPublicGetTime'
- elif type == 'delivery':
- method = 'dapiPublicGetTime'
- response = getattr(self, method)(params)
- return self.safe_integer(response, 'serverTime')
- def load_time_difference(self, params={}):
- serverTime = self.fetch_time(params)
- after = self.milliseconds()
- self.options['timeDifference'] = after - serverTime
- return self.options['timeDifference']
- def fetch_markets(self, params={}):
- defaultType = self.safe_string_2(self.options, 'fetchMarkets', 'defaultType', 'spot')
- type = self.safe_string(params, 'type', defaultType)
- query = self.omit(params, 'type')
- if (type != 'spot') and (type != 'future') and (type != 'margin') and (type != 'delivery'):
- raise ExchangeError(self.id + " does not support '" + type + "' type, set exchange.options['defaultType'] to 'spot', 'margin', 'delivery' or 'future'") # eslint-disable-line quotes
- method = 'publicGetExchangeInfo'
- if type == 'future':
- method = 'fapiPublicGetExchangeInfo'
- elif type == 'delivery':
- method = 'dapiPublicGetExchangeInfo'
- response = getattr(self, method)(query)
- #
- # spot / margin
- #
- # {
- # "timezone":"UTC",
- # "serverTime":1575416692969,
- # "rateLimits":[
- # {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200},
- # {"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":10,"limit":100},
- # {"rateLimitType":"ORDERS","interval":"DAY","intervalNum":1,"limit":200000}
- # ],
- # "exchangeFilters":[],
- # "symbols":[
- # {
- # "symbol":"ETHBTC",
- # "status":"TRADING",
- # "baseAsset":"ETH",
- # "baseAssetPrecision":8,
- # "quoteAsset":"BTC",
- # "quotePrecision":8,
- # "baseCommissionPrecision":8,
- # "quoteCommissionPrecision":8,
- # "orderTypes":["LIMIT","LIMIT_MAKER","MARKET","STOP_LOSS_LIMIT","TAKE_PROFIT_LIMIT"],
- # "icebergAllowed":true,
- # "ocoAllowed":true,
- # "quoteOrderQtyMarketAllowed":true,
- # "isSpotTradingAllowed":true,
- # "isMarginTradingAllowed":true,
- # "filters":[
- # {"filterType":"PRICE_FILTER","minPrice":"0.00000100","maxPrice":"100000.00000000","tickSize":"0.00000100"},
- # {"filterType":"PERCENT_PRICE","multiplierUp":"5","multiplierDown":"0.2","avgPriceMins":5},
- # {"filterType":"LOT_SIZE","minQty":"0.00100000","maxQty":"100000.00000000","stepSize":"0.00100000"},
- # {"filterType":"MIN_NOTIONAL","minNotional":"0.00010000","applyToMarket":true,"avgPriceMins":5},
- # {"filterType":"ICEBERG_PARTS","limit":10},
- # {"filterType":"MARKET_LOT_SIZE","minQty":"0.00000000","maxQty":"63100.00000000","stepSize":"0.00000000"},
- # {"filterType":"MAX_NUM_ALGO_ORDERS","maxNumAlgoOrders":5}
- # ]
- # },
- # ],
- # }
- #
- # futures/usdt-margined(fapi)
- #
- # {
- # "timezone":"UTC",
- # "serverTime":1575417244353,
- # "rateLimits":[
- # {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200},
- # {"rateLimitType":"ORDERS","interval":"MINUTE","intervalNum":1,"limit":1200}
- # ],
- # "exchangeFilters":[],
- # "symbols":[
- # {
- # "symbol":"BTCUSDT",
- # "status":"TRADING",
- # "maintMarginPercent":"2.5000",
- # "requiredMarginPercent":"5.0000",
- # "baseAsset":"BTC",
- # "quoteAsset":"USDT",
- # "pricePrecision":2,
- # "quantityPrecision":3,
- # "baseAssetPrecision":8,
- # "quotePrecision":8,
- # "filters":[
- # {"minPrice":"0.01","maxPrice":"100000","filterType":"PRICE_FILTER","tickSize":"0.01"},
- # {"stepSize":"0.001","filterType":"LOT_SIZE","maxQty":"1000","minQty":"0.001"},
- # {"stepSize":"0.001","filterType":"MARKET_LOT_SIZE","maxQty":"1000","minQty":"0.001"},
- # {"limit":200,"filterType":"MAX_NUM_ORDERS"},
- # {"multiplierDown":"0.8500","multiplierUp":"1.1500","multiplierDecimal":"4","filterType":"PERCENT_PRICE"}
- # ],
- # "orderTypes":["LIMIT","MARKET","STOP"],
- # "timeInForce":["GTC","IOC","FOK","GTX"]
- # }
- # ]
- # }
- #
- # delivery/coin-margined(dapi)
- #
- # {
- # "timezone": "UTC",
- # "serverTime": 1597667052958,
- # "rateLimits": [
- # {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000},
- # {"rateLimitType":"ORDERS","interval":"MINUTE","intervalNum":1,"limit":6000}
- # ],
- # "exchangeFilters": [],
- # "symbols": [
- # {
- # "symbol": "BTCUSD_200925",
- # "pair": "BTCUSD",
- # "contractType": "CURRENT_QUARTER",
- # "deliveryDate": 1601020800000,
- # "onboardDate": 1590739200000,
- # "contractStatus": "TRADING",
- # "contractSize": 100,
- # "marginAsset": "BTC",
- # "maintMarginPercent": "2.5000",
- # "requiredMarginPercent": "5.0000",
- # "baseAsset": "BTC",
- # "quoteAsset": "USD",
- # "pricePrecision": 1,
- # "quantityPrecision": 0,
- # "baseAssetPrecision": 8,
- # "quotePrecision": 8,
- # "equalQtyPrecision": 4,
- # "filters": [
- # {"minPrice":"0.1","maxPrice":"100000","filterType":"PRICE_FILTER","tickSize":"0.1"},
- # {"stepSize":"1","filterType":"LOT_SIZE","maxQty":"100000","minQty":"1"},
- # {"stepSize":"0","filterType":"MARKET_LOT_SIZE","maxQty":"100000","minQty":"1"},
- # {"limit":200,"filterType":"MAX_NUM_ORDERS"},
- # {"multiplierDown":"0.9500","multiplierUp":"1.0500","multiplierDecimal":"4","filterType":"PERCENT_PRICE"}
- # ],
- # "orderTypes": ["LIMIT","MARKET","STOP","STOP_MARKET","TAKE_PROFIT","TAKE_PROFIT_MARKET","TRAILING_STOP_MARKET"],
- # "timeInForce": ["GTC","IOC","FOK","GTX"]
- # },
- # {
- # "symbol": "BTCUSD_PERP",
- # "pair": "BTCUSD",
- # "contractType": "PERPETUAL",
- # "deliveryDate": 4133404800000,
- # "onboardDate": 1596006000000,
- # "contractStatus": "TRADING",
- # "contractSize": 100,
- # "marginAsset": "BTC",
- # "maintMarginPercent": "2.5000",
- # "requiredMarginPercent": "5.0000",
- # "baseAsset": "BTC",
- # "quoteAsset": "USD",
- # "pricePrecision": 1,
- # "quantityPrecision": 0,
- # "baseAssetPrecision": 8,
- # "quotePrecision": 8,
- # "equalQtyPrecision": 4,
- # "filters": [
- # {"minPrice":"0.1","maxPrice":"100000","filterType":"PRICE_FILTER","tickSize":"0.1"},
- # {"stepSize":"1","filterType":"LOT_SIZE","maxQty":"100000","minQty":"1"},
- # {"stepSize":"1","filterType":"MARKET_LOT_SIZE","maxQty":"100000","minQty":"1"},
- # {"limit":200,"filterType":"MAX_NUM_ORDERS"},
- # {"multiplierDown":"0.8500","multiplierUp":"1.1500","multiplierDecimal":"4","filterType":"PERCENT_PRICE"}
- # ],
- # "orderTypes": ["LIMIT","MARKET","STOP","STOP_MARKET","TAKE_PROFIT","TAKE_PROFIT_MARKET","TRAILING_STOP_MARKET"],
- # "timeInForce": ["GTC","IOC","FOK","GTX"]
- # }
- # ]
- # }
- #
- if self.options['adjustForTimeDifference']:
- self.load_time_difference()
- markets = self.safe_value(response, 'symbols')
- result = []
- for i in range(0, len(markets)):
- market = markets[i]
- marketType = 'spot'
- future = False
- delivery = False
- if 'maintMarginPercent' in market:
- delivery = ('marginAsset' in market)
- future = not delivery
- marketType = 'delivery' if delivery else 'future'
- spot = not (future or delivery)
- id = self.safe_string(market, 'symbol')
- lowercaseId = self.safe_string_lower(market, 'symbol')
- baseId = self.safe_string(market, 'baseAsset')
- quoteId = self.safe_string(market, 'quoteAsset')
- base = self.safe_currency_code(baseId)
- quote = self.safe_currency_code(quoteId)
- symbol = id if delivery else (base + '/' + quote)
- filters = self.safe_value(market, 'filters', [])
- filtersByType = self.index_by(filters, 'filterType')
- precision = {
- 'base': self.safe_integer(market, 'baseAssetPrecision'),
- 'quote': self.safe_integer(market, 'quotePrecision'),
- 'amount': self.safe_integer(market, 'baseAssetPrecision'),
- 'price': self.safe_integer(market, 'quotePrecision'),
- }
- status = self.safe_string_2(market, 'status', 'contractStatus')
- active = (status == 'TRADING')
- margin = self.safe_value(market, 'isMarginTradingAllowed', future or delivery)
- entry = {
- 'id': id,
- 'lowercaseId': lowercaseId,
- 'symbol': symbol,
- 'base': base,
- 'quote': quote,
- 'baseId': baseId,
- 'quoteId': quoteId,
- 'info': market,
- 'type': marketType,
- 'spot': spot,
- 'margin': margin,
- 'future': future,
- 'delivery': delivery,
- 'active': active,
- 'precision': precision,
- 'limits': {
- 'amount': {
- 'min': math.pow(10, -precision['amount']),
- 'max': None,
- },
- 'price': {
- 'min': None,
- 'max': None,
- },
- 'cost': {
- 'min': None,
- 'max': None,
- },
- },
- }
- if 'PRICE_FILTER' in filtersByType:
- filter = self.safe_value(filtersByType, 'PRICE_FILTER', {})
- # PRICE_FILTER reports zero values for maxPrice
- # since they updated filter types in November 2018
- # https://github.com/ccxt/ccxt/issues/4286
- # therefore limits['price']['max'] doesn't have any meaningful value except None
- entry['limits']['price'] = {
- 'min': self.safe_float(filter, 'minPrice'),
- 'max': None,
- }
- maxPrice = self.safe_float(filter, 'maxPrice')
- if (maxPrice is not None) and (maxPrice > 0):
- entry['limits']['price']['max'] = maxPrice
- entry['precision']['price'] = self.precision_from_string(filter['tickSize'])
- if 'LOT_SIZE' in filtersByType:
- filter = self.safe_value(filtersByType, 'LOT_SIZE', {})
- stepSize = self.safe_string(filter, 'stepSize')
- entry['precision']['amount'] = self.precision_from_string(stepSize)
- entry['limits']['amount'] = {
- 'min': self.safe_float(filter, 'minQty'),
- 'max': self.safe_float(filter, 'maxQty'),
- }
- if 'MARKET_LOT_SIZE' in filtersByType:
- filter = self.safe_value(filtersByType, 'MARKET_LOT_SIZE', {})
- entry['limits']['market'] = {
- 'min': self.safe_float(filter, 'minQty'),
- 'max': self.safe_float(filter, 'maxQty'),
- }
- if 'MIN_NOTIONAL' in filtersByType:
- filter = self.safe_value(filtersByType, 'MIN_NOTIONAL', {})
- entry['limits']['cost']['min'] = self.safe_float(filter, 'minNotional')
- result.append(entry)
- return result
- def calculate_fee(self, symbol, type, side, amount, price, takerOrMaker='taker', params={}):
- market = self.markets[symbol]
- key = 'quote'
- rate = market[takerOrMaker]
- cost = amount * rate
- precision = market['precision']['price']
- if side == 'sell':
- cost *= price
- else:
- key = 'base'
- precision = market['precision']['amount']
- cost = self.decimal_to_precision(cost, ROUND, precision, self.precisionMode)
- return {
- 'type': takerOrMaker,
- 'currency': market[key],
- 'rate': rate,
- 'cost': float(cost),
- }
- def fetch_balance(self, params={}):
- self.load_markets()
- defaultType = self.safe_string_2(self.options, 'fetchBalance', 'defaultType', 'spot')
- type = self.safe_string(params, 'type', defaultType)
- method = 'privateGetAccount'
- if type == 'future':
- options = self.safe_value(self.options, 'future', {})
- fetchBalanceOptions = self.safe_value(options, 'fetchBalance', {})
- method = self.safe_string(fetchBalanceOptions, 'method', 'fapiPrivateV2GetAccount')
- elif type == 'delivery':
- options = self.safe_value(self.options, 'delivery', {})
- fetchBalanceOptions = self.safe_value(options, 'fetchBalance', {})
- method = self.safe_string(fetchBalanceOptions, 'method', 'dapiPrivateGetAccount')
- elif type == 'margin':
- method = 'sapiGetMarginAccount'
- query = self.omit(params, 'type')
- response = getattr(self, method)(query)
- #
- # spot
- #
- # {
- # makerCommission: 10,
- # takerCommission: 10,
- # buyerCommission: 0,
- # sellerCommission: 0,
- # canTrade: True,
- # canWithdraw: True,
- # canDeposit: True,
- # updateTime: 1575357359602,
- # accountType: "MARGIN",
- # balances: [
- # {asset: "BTC", free: "0.00219821", locked: "0.00000000" },
- # ]
- # }
- #
- # margin
- #
- # {
- # "borrowEnabled":true,
- # "marginLevel":"999.00000000",
- # "totalAssetOfBtc":"0.00000000",
- # "totalLiabilityOfBtc":"0.00000000",
- # "totalNetAssetOfBtc":"0.00000000",
- # "tradeEnabled":true,
- # "transferEnabled":true,
- # "userAssets":[
- # {"asset":"MATIC","borrowed":"0.00000000","free":"0.00000000","interest":"0.00000000","locked":"0.00000000","netAsset":"0.00000000"},
- # {"asset":"VET","borrowed":"0.00000000","free":"0.00000000","interest":"0.00000000","locked":"0.00000000","netAsset":"0.00000000"},
- # {"asset":"USDT","borrowed":"0.00000000","free":"0.00000000","interest":"0.00000000","locked":"0.00000000","netAsset":"0.00000000"}
- # ],
- # }
- #
- # futures(fapi)
- #
- # fapiPrivateGetAccount
- #
- # {
- # "feeTier":0,
- # "canTrade":true,
- # "canDeposit":true,
- # "canWithdraw":true,
- # "updateTime":0,
- # "totalInitialMargin":"0.00000000",
- # "totalMaintMargin":"0.00000000",
- # "totalWalletBalance":"4.54000000",
- # "totalUnrealizedProfit":"0.00000000",
- # "totalMarginBalance":"4.54000000",
- # "totalPositionInitialMargin":"0.00000000",
- # "totalOpenOrderInitialMargin":"0.00000000",
- # "maxWithdrawAmount":"4.54000000",
- # "assets":[
- # {
- # "asset":"USDT",
- # "walletBalance":"4.54000000",
- # "unrealizedProfit":"0.00000000",
- # "marginBalance":"4.54000000",
- # "maintMargin":"0.00000000",
- # "initialMargin":"0.00000000",
- # "positionInitialMargin":"0.00000000",
- # "openOrderInitialMargin":"0.00000000",
- # "maxWithdrawAmount":"4.54000000"
- # }
- # ],
- # "positions":[
- # {
- # "symbol":"BTCUSDT",
- # "initialMargin":"0.00000",
- # "maintMargin":"0.00000",
- # "unrealizedProfit":"0.00000000",
- # "positionInitialMargin":"0.00000",
- # "openOrderInitialMargin":"0.00000"
- # }
- # ]
- # }
- #
- # fapiPrivateV2GetAccount
- #
- # {
- # "feeTier":0,
- # "canTrade":true,
- # "canDeposit":true,
- # "canWithdraw":true,
- # "updateTime":0,
- # "totalInitialMargin":"0.00000000",
- # "totalMaintMargin":"0.00000000",
- # "totalWalletBalance":"0.00000000",
- # "totalUnrealizedProfit":"0.00000000",
- # "totalMarginBalance":"0.00000000",
- # "totalPositionInitialMargin":"0.00000000",
- # "totalOpenOrderInitialMargin":"0.00000000",
- # "totalCrossWalletBalance":"0.00000000",
- # "totalCrossUnPnl":"0.00000000",
- # "availableBalance":"0.00000000",
- # "maxWithdrawAmount":"0.00000000",
- # "assets":[
- # {
- # "asset":"BNB",
- # "walletBalance":"0.01000000",
- # "unrealizedProfit":"0.00000000",
- # "marginBalance":"0.01000000",
- # "maintMargin":"0.00000000",
- # "initialMargin":"0.00000000",
- # "positionInitialMargin":"0.00000000",
- # "openOrderInitialMargin":"0.00000000",
- # "maxWithdrawAmount":"0.01000000",
- # "crossWalletBalance":"0.01000000",
- # "crossUnPnl":"0.00000000",
- # "availableBalance":"0.01000000"
- # }
- # ],
- # "positions":[
- # {
- # "symbol":"BTCUSDT",
- # "initialMargin":"0",
- # "maintMargin":"0",
- # "unrealizedProfit":"0.00000000",
- # "positionInitialMargin":"0",
- # "openOrderInitialMargin":"0",
- # "leverage":"20",
- # "isolated":false,
- # "entryPrice":"0.00000",
- # "maxNotional":"5000000",
- # "positionSide":"BOTH"
- # },
- # ]
- # }
- #
- # fapiPrivateV2GetBalance
- #
- # [
- # {
- # "accountAlias":"FzFzXquXXqoC",
- # "asset":"BNB",
- # "balance":"0.01000000",
- # "crossWalletBalance":"0.01000000",
- # "crossUnPnl":"0.00000000",
- # "availableBalance":"0.01000000",
- # "maxWithdrawAmount":"0.01000000"
- # }
- # ]
- #
- result = {'info': response}
- if (type == 'spot') or (type == 'margin'):
- balances = self.safe_value_2(response, 'balances', 'userAssets', [])
- for i in range(0, len(balances)):
- balance = balances[i]
- currencyId = self.safe_string(balance, 'asset')
- code = self.safe_currency_code(currencyId)
- account = self.account()
- account['free'] = self.safe_float(balance, 'free')
- account['used'] = self.safe_float(balance, 'locked')
- result[code] = account
- else:
- balances = response
- if not isinstance(response, list):
- balances = self.safe_value(response, 'assets', [])
- for i in range(0, len(balances)):
- balance = balances[i]
- currencyId = self.safe_string(balance, 'asset')
- code = self.safe_currency_code(currencyId)
- account = self.account()
- account['free'] = self.safe_float(balance, 'availableBalance')
- account['used'] = self.safe_float(balance, 'initialMargin')
- account['total'] = self.safe_float_2(balance, 'marginBalance', 'balance')
- result[code] = account
- return self.parse_balance(result)
- def fetch_order_book(self, symbol, limit=None, params={}):
- self.load_markets()
- market = self.market(symbol)
- request = {
- 'symbol': market['id'],
- }
- if limit is not None:
- request['limit'] = limit # default 100, max 5000, see https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#order-book
- method = 'publicGetDepth'
- if market['future']:
- method = 'fapiPublicGetDepth'
- elif market['delivery']:
- method = 'dapiPublicGetDepth'
- response = getattr(self, method)(self.extend(request, params))
- orderbook = self.parse_order_book(response)
- orderbook['nonce'] = self.safe_integer(response, 'lastUpdateId')
- return orderbook
- def parse_ticker(self, ticker, market=None):
- #
- # {
- # symbol: 'ETHBTC',
- # priceChange: '0.00068700',
- # priceChangePercent: '2.075',
- # weightedAvgPrice: '0.03342681',
- # prevClosePrice: '0.03310300',
- # lastPrice: '0.03378900',
- # lastQty: '0.07700000',
- # bidPrice: '0.03378900',
- # bidQty: '7.16800000',
- # askPrice: '0.03379000',
- # askQty: '24.00000000',
- # openPrice: '0.03310200',
- # highPrice: '0.03388900',
- # lowPrice: '0.03306900',
- # volume: '205478.41000000',
- # quoteVolume: '6868.48826294',
- # openTime: 1601469986932,
- # closeTime: 1601556386932,
- # firstId: 196098772,
- # lastId: 196186315,
- # count: 87544
- # }
- #
- timestamp = self.safe_integer(ticker, 'closeTime')
- marketId = self.safe_string(ticker, 'symbol')
- symbol = self.safe_symbol(marketId, market)
- last = self.safe_float(ticker, 'lastPrice')
- return {
- 'symbol': symbol,
- 'timestamp': timestamp,…