/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

  1. # -*- coding: utf-8 -*-
  2. # PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
  3. # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
  4. from ccxt.base.exchange import Exchange
  5. import math
  6. import json
  7. from ccxt.base.errors import ExchangeError
  8. from ccxt.base.errors import AuthenticationError
  9. from ccxt.base.errors import PermissionDenied
  10. from ccxt.base.errors import AccountSuspended
  11. from ccxt.base.errors import ArgumentsRequired
  12. from ccxt.base.errors import BadRequest
  13. from ccxt.base.errors import BadSymbol
  14. from ccxt.base.errors import InsufficientFunds
  15. from ccxt.base.errors import InvalidAddress
  16. from ccxt.base.errors import InvalidOrder
  17. from ccxt.base.errors import OrderNotFound
  18. from ccxt.base.errors import OrderImmediatelyFillable
  19. from ccxt.base.errors import NotSupported
  20. from ccxt.base.errors import DDoSProtection
  21. from ccxt.base.errors import RateLimitExceeded
  22. from ccxt.base.errors import ExchangeNotAvailable
  23. from ccxt.base.errors import InvalidNonce
  24. from ccxt.base.decimal_to_precision import ROUND
  25. from ccxt.base.decimal_to_precision import TRUNCATE
  26. class binance(Exchange):
  27. def describe(self):
  28. return self.deep_extend(super(binance, self).describe(), {
  29. 'id': 'binance',
  30. 'name': 'Binance',
  31. 'countries': ['JP', 'MT'], # Japan, Malta
  32. 'rateLimit': 500,
  33. 'certified': True,
  34. 'pro': True,
  35. # new metainfo interface
  36. 'has': {
  37. 'cancelAllOrders': True,
  38. 'cancelOrder': True,
  39. 'CORS': False,
  40. 'createOrder': True,
  41. 'fetchBalance': True,
  42. 'fetchBidsAsks': True,
  43. 'fetchClosedOrders': 'emulated',
  44. 'fetchDepositAddress': True,
  45. 'fetchDeposits': True,
  46. 'fetchFundingFees': True,
  47. 'fetchMarkets': True,
  48. 'fetchMyTrades': True,
  49. 'fetchOHLCV': True,
  50. 'fetchOpenOrders': True,
  51. 'fetchOrder': True,
  52. 'fetchOrders': True,
  53. 'fetchOrderBook': True,
  54. 'fetchStatus': True,
  55. 'fetchTicker': True,
  56. 'fetchTickers': True,
  57. 'fetchTime': True,
  58. 'fetchTrades': True,
  59. 'fetchTradingFee': True,
  60. 'fetchTradingFees': True,
  61. 'fetchTransactions': False,
  62. 'fetchWithdrawals': True,
  63. 'withdraw': True,
  64. },
  65. 'timeframes': {
  66. '1m': '1m',
  67. '3m': '3m',
  68. '5m': '5m',
  69. '15m': '15m',
  70. '30m': '30m',
  71. '1h': '1h',
  72. '2h': '2h',
  73. '4h': '4h',
  74. '6h': '6h',
  75. '8h': '8h',
  76. '12h': '12h',
  77. '1d': '1d',
  78. '3d': '3d',
  79. '1w': '1w',
  80. '1M': '1M',
  81. },
  82. 'urls': {
  83. 'logo': 'https://user-images.githubusercontent.com/1294454/29604020-d5483cdc-87ee-11e7-94c7-d1a8d9169293.jpg',
  84. 'test': {
  85. 'dapiPublic': 'https://testnet.binancefuture.com/dapi/v1',
  86. 'dapiPrivate': 'https://testnet.binancefuture.com/dapi/v1',
  87. 'fapiPublic': 'https://testnet.binancefuture.com/fapi/v1',
  88. 'fapiPrivate': 'https://testnet.binancefuture.com/fapi/v1',
  89. 'fapiPrivateV2': 'https://testnet.binancefuture.com/fapi/v2',
  90. 'public': 'https://testnet.binance.vision/api/v3',
  91. 'private': 'https://testnet.binance.vision/api/v3',
  92. 'v3': 'https://testnet.binance.vision/api/v3',
  93. 'v1': 'https://testnet.binance.vision/api/v1',
  94. },
  95. 'api': {
  96. 'wapi': 'https://api.binance.com/wapi/v3',
  97. 'sapi': 'https://api.binance.com/sapi/v1',
  98. 'dapiPublic': 'https://dapi.binance.com/dapi/v1',
  99. 'dapiPrivate': 'https://dapi.binance.com/dapi/v1',
  100. 'dapiData': 'https://dapi.binance.com/futures/data',
  101. 'fapiPublic': 'https://fapi.binance.com/fapi/v1',
  102. 'fapiPrivate': 'https://fapi.binance.com/fapi/v1',
  103. 'fapiData': 'https://fapi.binance.com/futures/data',
  104. 'fapiPrivateV2': 'https://fapi.binance.com/fapi/v2',
  105. 'public': 'https://api.binance.com/api/v3',
  106. 'private': 'https://api.binance.com/api/v3',
  107. 'v3': 'https://api.binance.com/api/v3',
  108. 'v1': 'https://api.binance.com/api/v1',
  109. },
  110. 'www': 'https://www.binance.com',
  111. 'referral': 'https://www.binance.com/?ref=10205187',
  112. 'doc': [
  113. 'https://binance-docs.github.io/apidocs/spot/en',
  114. ],
  115. 'api_management': 'https://www.binance.com/en/usercenter/settings/api-management',
  116. 'fees': 'https://www.binance.com/en/fee/schedule',
  117. },
  118. 'api': {
  119. # the API structure below will need 3-layer apidefs
  120. 'sapi': {
  121. 'get': [
  122. 'accountSnapshot',
  123. # these endpoints require self.apiKey
  124. 'margin/asset',
  125. 'margin/pair',
  126. 'margin/allAssets',
  127. 'margin/allPairs',
  128. 'margin/priceIndex',
  129. # these endpoints require self.apiKey + self.secret
  130. 'asset/assetDividend',
  131. 'margin/loan',
  132. 'margin/repay',
  133. 'margin/account',
  134. 'margin/transfer',
  135. 'margin/interestHistory',
  136. 'margin/forceLiquidationRec',
  137. 'margin/order',
  138. 'margin/openOrders',
  139. 'margin/allOrders',
  140. 'margin/myTrades',
  141. 'margin/maxBorrowable',
  142. 'margin/maxTransferable',
  143. 'margin/isolated/transfer',
  144. 'margin/isolated/account',
  145. 'margin/isolated/pair',
  146. 'margin/isolated/allPairs',
  147. 'futures/transfer',
  148. 'futures/loan/borrow/history',
  149. 'futures/loan/repay/history',
  150. 'futures/loan/wallet',
  151. 'futures/loan/configs',
  152. 'futures/loan/calcAdjustLevel',
  153. 'futures/loan/calcMaxAdjustAmount',
  154. 'futures/loan/adjustCollateral/history',
  155. 'futures/loan/liquidationHistory',
  156. # https://binance-docs.github.io/apidocs/spot/en/#withdraw-sapi
  157. 'capital/config/getall', # get networks for withdrawing USDT ERC20 vs USDT Omni
  158. 'capital/deposit/address',
  159. 'capital/deposit/hisrec',
  160. 'capital/deposit/subAddress',
  161. 'capital/deposit/subHisrec',
  162. 'capital/withdraw/history',
  163. 'sub-account/futures/account',
  164. 'sub-account/futures/accountSummary',
  165. 'sub-account/futures/positionRisk',
  166. 'sub-account/futures/internalTransfer',
  167. 'sub-account/margin/account',
  168. 'sub-account/margin/accountSummary',
  169. 'sub-account/spotSummary',
  170. 'sub-account/status',
  171. 'sub-account/transfer/subUserHistory',
  172. # lending endpoints
  173. 'lending/daily/product/list',
  174. 'lending/daily/userLeftQuota',
  175. 'lending/daily/userRedemptionQuota',
  176. 'lending/daily/token/position',
  177. 'lending/union/account',
  178. 'lending/union/purchaseRecord',
  179. 'lending/union/redemptionRecord',
  180. 'lending/union/interestHistory',
  181. 'lending/project/list',
  182. 'lending/project/position/list',
  183. # mining endpoints
  184. 'mining/pub/algoList',
  185. 'mining/pub/coinList',
  186. 'mining/worker/detail',
  187. 'mining/worker/list',
  188. 'mining/payment/list',
  189. 'mining/statistics/user/status',
  190. 'mining/statistics/user/list',
  191. ],
  192. 'post': [
  193. 'asset/dust',
  194. 'account/disableFastWithdrawSwitch',
  195. 'account/enableFastWithdrawSwitch',
  196. 'capital/withdraw/apply',
  197. 'margin/transfer',
  198. 'margin/loan',
  199. 'margin/repay',
  200. 'margin/order',
  201. 'margin/isolated/create',
  202. 'margin/isolated/transfer',
  203. 'sub-account/margin/transfer',
  204. 'sub-account/margin/enable',
  205. 'sub-account/margin/enable',
  206. 'sub-account/futures/enable',
  207. 'sub-account/futures/transfer',
  208. 'sub-account/futures/internalTransfer',
  209. 'sub-account/transfer/subToSub',
  210. 'sub-account/transfer/subToMaster',
  211. 'userDataStream',
  212. 'userDataStream/isolated',
  213. 'futures/transfer',
  214. 'futures/loan/borrow',
  215. 'futures/loan/repay',
  216. 'futures/loan/adjustCollateral',
  217. # lending
  218. 'lending/customizedFixed/purchase',
  219. 'lending/daily/purchase',
  220. 'lending/daily/redeem',
  221. ],
  222. 'put': [
  223. 'userDataStream',
  224. 'userDataStream/isolated',
  225. ],
  226. 'delete': [
  227. 'margin/order',
  228. 'userDataStream',
  229. 'userDataStream/isolated',
  230. ],
  231. },
  232. 'wapi': {
  233. 'post': [
  234. 'withdraw',
  235. 'sub-account/transfer',
  236. ],
  237. 'get': [
  238. 'depositHistory',
  239. 'withdrawHistory',
  240. 'depositAddress',
  241. 'accountStatus',
  242. 'systemStatus',
  243. 'apiTradingStatus',
  244. 'userAssetDribbletLog',
  245. 'tradeFee',
  246. 'assetDetail',
  247. 'sub-account/list',
  248. 'sub-account/transfer/history',
  249. 'sub-account/assets',
  250. ],
  251. },
  252. 'dapiPublic': {
  253. 'get': [
  254. 'ping',
  255. 'time',
  256. 'exchangeInfo',
  257. 'depth',
  258. 'trades',
  259. 'historicalTrades',
  260. 'aggTrades',
  261. 'premiumIndex',
  262. 'fundingRate',
  263. 'klines',
  264. 'continuousKlines',
  265. 'indexPriceKlines',
  266. 'markPriceKlines',
  267. 'ticker/24hr',
  268. 'ticker/price',
  269. 'ticker/bookTicker',
  270. 'allForceOrders',
  271. 'openInterest',
  272. ],
  273. },
  274. 'dapiData': {
  275. 'get': [
  276. 'openInterestHist',
  277. 'topLongShortAccountRatio',
  278. 'topLongShortPositionRatio',
  279. 'globalLongShortAccountRatio',
  280. 'takerBuySellVol',
  281. 'basis',
  282. ],
  283. },
  284. 'dapiPrivate': {
  285. 'get': [
  286. 'positionSide/dual',
  287. 'order',
  288. 'openOrder',
  289. 'openOrders',
  290. 'allOrders',
  291. 'balance',
  292. 'account',
  293. 'positionMargin/history',
  294. 'positionRisk',
  295. 'userTrades',
  296. 'income',
  297. 'leverageBracket',
  298. 'forceOrders',
  299. 'adlQuantile',
  300. ],
  301. 'post': [
  302. 'positionSide/dual',
  303. 'order',
  304. 'batchOrders',
  305. 'countdownCancelAll',
  306. 'leverage',
  307. 'marginType',
  308. 'positionMargin',
  309. 'listenKey',
  310. ],
  311. 'put': [
  312. 'listenKey',
  313. ],
  314. 'delete': [
  315. 'order',
  316. 'allOpenOrders',
  317. 'batchOrders',
  318. 'listenKey',
  319. ],
  320. },
  321. 'fapiPublic': {
  322. 'get': [
  323. 'ping',
  324. 'time',
  325. 'exchangeInfo',
  326. 'depth',
  327. 'trades',
  328. 'historicalTrades',
  329. 'aggTrades',
  330. 'klines',
  331. 'fundingRate',
  332. 'premiumIndex',
  333. 'ticker/24hr',
  334. 'ticker/price',
  335. 'ticker/bookTicker',
  336. 'allForceOrders',
  337. 'openInterest',
  338. ],
  339. },
  340. 'fapiData': {
  341. 'get': [
  342. 'openInterestHist',
  343. 'topLongShortAccountRatio',
  344. 'topLongShortPositionRatio',
  345. 'globalLongShortAccountRatio',
  346. 'takerlongshortRatio',
  347. ],
  348. },
  349. 'fapiPrivate': {
  350. 'get': [
  351. 'allForceOrders',
  352. 'allOrders',
  353. 'openOrder',
  354. 'openOrders',
  355. 'order',
  356. 'account',
  357. 'balance',
  358. 'leverageBracket',
  359. 'positionMargin/history',
  360. 'positionRisk',
  361. 'positionSide/dual',
  362. 'userTrades',
  363. 'income',
  364. ],
  365. 'post': [
  366. 'batchOrders',
  367. 'positionSide/dual',
  368. 'positionMargin',
  369. 'marginType',
  370. 'order',
  371. 'leverage',
  372. 'listenKey',
  373. 'countdownCancelAll',
  374. ],
  375. 'put': [
  376. 'listenKey',
  377. ],
  378. 'delete': [
  379. 'batchOrders',
  380. 'order',
  381. 'allOpenOrders',
  382. 'listenKey',
  383. ],
  384. },
  385. 'fapiPrivateV2': {
  386. 'get': [
  387. 'account',
  388. 'balance',
  389. 'positionRisk',
  390. ],
  391. },
  392. 'v3': {
  393. 'get': [
  394. 'ticker/price',
  395. 'ticker/bookTicker',
  396. ],
  397. },
  398. 'public': {
  399. 'get': [
  400. 'ping',
  401. 'time',
  402. 'depth',
  403. 'trades',
  404. 'aggTrades',
  405. 'historicalTrades',
  406. 'klines',
  407. 'ticker/24hr',
  408. 'ticker/price',
  409. 'ticker/bookTicker',
  410. 'exchangeInfo',
  411. ],
  412. 'put': ['userDataStream'],
  413. 'post': ['userDataStream'],
  414. 'delete': ['userDataStream'],
  415. },
  416. 'private': {
  417. 'get': [
  418. 'allOrderList', # oco
  419. 'openOrderList', # oco
  420. 'orderList', # oco
  421. 'order',
  422. 'openOrders',
  423. 'allOrders',
  424. 'account',
  425. 'myTrades',
  426. ],
  427. 'post': [
  428. 'order/oco',
  429. 'order',
  430. 'order/test',
  431. ],
  432. 'delete': [
  433. 'openOrders', # added on 2020-04-25 for canceling all open orders per symbol
  434. 'orderList', # oco
  435. 'order',
  436. ],
  437. },
  438. },
  439. 'fees': {
  440. 'trading': {
  441. 'tierBased': False,
  442. 'percentage': True,
  443. 'taker': 0.001,
  444. 'maker': 0.001,
  445. },
  446. },
  447. 'commonCurrencies': {
  448. 'BCC': 'BCC', # kept for backward-compatibility https://github.com/ccxt/ccxt/issues/4848
  449. 'YOYO': 'YOYOW',
  450. },
  451. # exchange-specific options
  452. 'options': {
  453. # 'fetchTradesMethod': 'publicGetAggTrades', # publicGetTrades, publicGetHistoricalTrades
  454. 'defaultTimeInForce': 'GTC', # 'GTC' = Good To Cancel(default), 'IOC' = Immediate Or Cancel
  455. 'defaultType': 'spot', # 'spot', 'future', 'margin', 'delivery'
  456. 'hasAlreadyAuthenticatedSuccessfully': False,
  457. 'warnOnFetchOpenOrdersWithoutSymbol': True,
  458. 'recvWindow': 5 * 1000, # 5 sec, binance default
  459. 'timeDifference': 0, # the difference between system clock and Binance clock
  460. 'adjustForTimeDifference': False, # controls the adjustment logic upon instantiation
  461. 'parseOrderToPrecision': False, # force amounts and costs in parseOrder to precision
  462. 'newOrderRespType': {
  463. 'market': 'FULL', # 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills
  464. 'limit': 'RESULT', # we change it from 'ACK' by default to 'RESULT'
  465. },
  466. 'quoteOrderQty': True, # whether market orders support amounts in quote currency
  467. },
  468. # https://binance-docs.github.io/apidocs/spot/en/#error-codes-2
  469. 'exceptions': {
  470. 'API key does not exist': AuthenticationError,
  471. 'Order would trigger immediately.': OrderImmediatelyFillable,
  472. 'Order would immediately match and take.': OrderImmediatelyFillable, # {"code":-2010,"msg":"Order would immediately match and take."}
  473. 'Account has insufficient balance for requested action.': InsufficientFunds,
  474. 'Rest API trading is not enabled.': ExchangeNotAvailable,
  475. "You don't have permission.": PermissionDenied, # {"msg":"You don't have permission.","success":false}
  476. 'Market is closed.': ExchangeNotAvailable, # {"code":-1013,"msg":"Market is closed."}
  477. 'Too many requests.': DDoSProtection, # {"msg":"Too many requests. Please try again later.","success":false}
  478. '-1000': ExchangeNotAvailable, # {"code":-1000,"msg":"An unknown error occured while processing the request."}
  479. '-1001': ExchangeNotAvailable, # 'Internal error; unable to process your request. Please try again.'
  480. '-1002': AuthenticationError, # 'You are not authorized to execute self request.'
  481. '-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."}
  482. '-1013': InvalidOrder, # createOrder -> 'invalid quantity'/'invalid price'/MIN_NOTIONAL
  483. '-1015': RateLimitExceeded, # 'Too many new orders; current limit is %s orders per %s.'
  484. '-1016': ExchangeNotAvailable, # 'This service is no longer available.',
  485. '-1020': BadRequest, # 'This operation is not supported.'
  486. '-1021': InvalidNonce, # 'your time is ahead of server'
  487. '-1022': AuthenticationError, # {"code":-1022,"msg":"Signature for self request is not valid."}
  488. '-1100': BadRequest, # createOrder(symbol, 1, asdf) -> 'Illegal characters found in parameter 'price'
  489. '-1101': BadRequest, # Too many parameters; expected %s and received %s.
  490. '-1102': BadRequest, # Param %s or %s must be sent, but both were empty
  491. '-1103': BadRequest, # An unknown parameter was sent.
  492. '-1104': BadRequest, # Not all sent parameters were read, read 8 parameters but was sent 9
  493. '-1105': BadRequest, # Parameter %s was empty.
  494. '-1106': BadRequest, # Parameter %s sent when not required.
  495. '-1111': BadRequest, # Precision is over the maximum defined for self asset.
  496. '-1112': InvalidOrder, # No orders on book for symbol.
  497. '-1114': BadRequest, # TimeInForce parameter sent when not required.
  498. '-1115': BadRequest, # Invalid timeInForce.
  499. '-1116': BadRequest, # Invalid orderType.
  500. '-1117': BadRequest, # Invalid side.
  501. '-1118': BadRequest, # New client order ID was empty.
  502. '-1119': BadRequest, # Original client order ID was empty.
  503. '-1120': BadRequest, # Invalid interval.
  504. '-1121': BadSymbol, # Invalid symbol.
  505. '-1125': AuthenticationError, # This listenKey does not exist.
  506. '-1127': BadRequest, # More than %s hours between startTime and endTime.
  507. '-1128': BadRequest, # {"code":-1128,"msg":"Combination of optional parameters invalid."}
  508. '-1130': BadRequest, # Data sent for paramter %s is not valid.
  509. '-1131': BadRequest, # recvWindow must be less than 60000
  510. '-2010': ExchangeError, # generic error code for createOrder -> 'Account has insufficient balance for requested action.', {"code":-2010,"msg":"Rest API trading is not enabled."}, etc...
  511. '-2011': OrderNotFound, # cancelOrder(1, 'BTC/USDT') -> 'UNKNOWN_ORDER'
  512. '-2013': OrderNotFound, # fetchOrder(1, 'BTC/USDT') -> 'Order does not exist'
  513. '-2014': AuthenticationError, # {"code":-2014, "msg": "API-key format invalid."}
  514. '-2015': AuthenticationError, # "Invalid API-key, IP, or permissions for action."
  515. '-3005': InsufficientFunds, # {"code":-3005,"msg":"Transferring out not allowed. Transfer out amount exceeds max amount."}
  516. '-3008': InsufficientFunds, # {"code":-3008,"msg":"Borrow not allowed. Your borrow amount has exceed maximum borrow amount."}
  517. '-3010': ExchangeError, # {"code":-3010,"msg":"Repay not allowed. Repay amount exceeds borrow amount."}
  518. '-3022': AccountSuspended, # You account's trading is banned.
  519. },
  520. })
  521. def nonce(self):
  522. return self.milliseconds() - self.options['timeDifference']
  523. def fetch_time(self, params={}):
  524. type = self.safe_string_2(self.options, 'fetchTime', 'defaultType', 'spot')
  525. method = 'publicGetTime'
  526. if type == 'future':
  527. method = 'fapiPublicGetTime'
  528. elif type == 'delivery':
  529. method = 'dapiPublicGetTime'
  530. response = getattr(self, method)(params)
  531. return self.safe_integer(response, 'serverTime')
  532. def load_time_difference(self, params={}):
  533. serverTime = self.fetch_time(params)
  534. after = self.milliseconds()
  535. self.options['timeDifference'] = after - serverTime
  536. return self.options['timeDifference']
  537. def fetch_markets(self, params={}):
  538. defaultType = self.safe_string_2(self.options, 'fetchMarkets', 'defaultType', 'spot')
  539. type = self.safe_string(params, 'type', defaultType)
  540. query = self.omit(params, 'type')
  541. if (type != 'spot') and (type != 'future') and (type != 'margin') and (type != 'delivery'):
  542. raise ExchangeError(self.id + " does not support '" + type + "' type, set exchange.options['defaultType'] to 'spot', 'margin', 'delivery' or 'future'") # eslint-disable-line quotes
  543. method = 'publicGetExchangeInfo'
  544. if type == 'future':
  545. method = 'fapiPublicGetExchangeInfo'
  546. elif type == 'delivery':
  547. method = 'dapiPublicGetExchangeInfo'
  548. response = getattr(self, method)(query)
  549. #
  550. # spot / margin
  551. #
  552. # {
  553. # "timezone":"UTC",
  554. # "serverTime":1575416692969,
  555. # "rateLimits":[
  556. # {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200},
  557. # {"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":10,"limit":100},
  558. # {"rateLimitType":"ORDERS","interval":"DAY","intervalNum":1,"limit":200000}
  559. # ],
  560. # "exchangeFilters":[],
  561. # "symbols":[
  562. # {
  563. # "symbol":"ETHBTC",
  564. # "status":"TRADING",
  565. # "baseAsset":"ETH",
  566. # "baseAssetPrecision":8,
  567. # "quoteAsset":"BTC",
  568. # "quotePrecision":8,
  569. # "baseCommissionPrecision":8,
  570. # "quoteCommissionPrecision":8,
  571. # "orderTypes":["LIMIT","LIMIT_MAKER","MARKET","STOP_LOSS_LIMIT","TAKE_PROFIT_LIMIT"],
  572. # "icebergAllowed":true,
  573. # "ocoAllowed":true,
  574. # "quoteOrderQtyMarketAllowed":true,
  575. # "isSpotTradingAllowed":true,
  576. # "isMarginTradingAllowed":true,
  577. # "filters":[
  578. # {"filterType":"PRICE_FILTER","minPrice":"0.00000100","maxPrice":"100000.00000000","tickSize":"0.00000100"},
  579. # {"filterType":"PERCENT_PRICE","multiplierUp":"5","multiplierDown":"0.2","avgPriceMins":5},
  580. # {"filterType":"LOT_SIZE","minQty":"0.00100000","maxQty":"100000.00000000","stepSize":"0.00100000"},
  581. # {"filterType":"MIN_NOTIONAL","minNotional":"0.00010000","applyToMarket":true,"avgPriceMins":5},
  582. # {"filterType":"ICEBERG_PARTS","limit":10},
  583. # {"filterType":"MARKET_LOT_SIZE","minQty":"0.00000000","maxQty":"63100.00000000","stepSize":"0.00000000"},
  584. # {"filterType":"MAX_NUM_ALGO_ORDERS","maxNumAlgoOrders":5}
  585. # ]
  586. # },
  587. # ],
  588. # }
  589. #
  590. # futures/usdt-margined(fapi)
  591. #
  592. # {
  593. # "timezone":"UTC",
  594. # "serverTime":1575417244353,
  595. # "rateLimits":[
  596. # {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200},
  597. # {"rateLimitType":"ORDERS","interval":"MINUTE","intervalNum":1,"limit":1200}
  598. # ],
  599. # "exchangeFilters":[],
  600. # "symbols":[
  601. # {
  602. # "symbol":"BTCUSDT",
  603. # "status":"TRADING",
  604. # "maintMarginPercent":"2.5000",
  605. # "requiredMarginPercent":"5.0000",
  606. # "baseAsset":"BTC",
  607. # "quoteAsset":"USDT",
  608. # "pricePrecision":2,
  609. # "quantityPrecision":3,
  610. # "baseAssetPrecision":8,
  611. # "quotePrecision":8,
  612. # "filters":[
  613. # {"minPrice":"0.01","maxPrice":"100000","filterType":"PRICE_FILTER","tickSize":"0.01"},
  614. # {"stepSize":"0.001","filterType":"LOT_SIZE","maxQty":"1000","minQty":"0.001"},
  615. # {"stepSize":"0.001","filterType":"MARKET_LOT_SIZE","maxQty":"1000","minQty":"0.001"},
  616. # {"limit":200,"filterType":"MAX_NUM_ORDERS"},
  617. # {"multiplierDown":"0.8500","multiplierUp":"1.1500","multiplierDecimal":"4","filterType":"PERCENT_PRICE"}
  618. # ],
  619. # "orderTypes":["LIMIT","MARKET","STOP"],
  620. # "timeInForce":["GTC","IOC","FOK","GTX"]
  621. # }
  622. # ]
  623. # }
  624. #
  625. # delivery/coin-margined(dapi)
  626. #
  627. # {
  628. # "timezone": "UTC",
  629. # "serverTime": 1597667052958,
  630. # "rateLimits": [
  631. # {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000},
  632. # {"rateLimitType":"ORDERS","interval":"MINUTE","intervalNum":1,"limit":6000}
  633. # ],
  634. # "exchangeFilters": [],
  635. # "symbols": [
  636. # {
  637. # "symbol": "BTCUSD_200925",
  638. # "pair": "BTCUSD",
  639. # "contractType": "CURRENT_QUARTER",
  640. # "deliveryDate": 1601020800000,
  641. # "onboardDate": 1590739200000,
  642. # "contractStatus": "TRADING",
  643. # "contractSize": 100,
  644. # "marginAsset": "BTC",
  645. # "maintMarginPercent": "2.5000",
  646. # "requiredMarginPercent": "5.0000",
  647. # "baseAsset": "BTC",
  648. # "quoteAsset": "USD",
  649. # "pricePrecision": 1,
  650. # "quantityPrecision": 0,
  651. # "baseAssetPrecision": 8,
  652. # "quotePrecision": 8,
  653. # "equalQtyPrecision": 4,
  654. # "filters": [
  655. # {"minPrice":"0.1","maxPrice":"100000","filterType":"PRICE_FILTER","tickSize":"0.1"},
  656. # {"stepSize":"1","filterType":"LOT_SIZE","maxQty":"100000","minQty":"1"},
  657. # {"stepSize":"0","filterType":"MARKET_LOT_SIZE","maxQty":"100000","minQty":"1"},
  658. # {"limit":200,"filterType":"MAX_NUM_ORDERS"},
  659. # {"multiplierDown":"0.9500","multiplierUp":"1.0500","multiplierDecimal":"4","filterType":"PERCENT_PRICE"}
  660. # ],
  661. # "orderTypes": ["LIMIT","MARKET","STOP","STOP_MARKET","TAKE_PROFIT","TAKE_PROFIT_MARKET","TRAILING_STOP_MARKET"],
  662. # "timeInForce": ["GTC","IOC","FOK","GTX"]
  663. # },
  664. # {
  665. # "symbol": "BTCUSD_PERP",
  666. # "pair": "BTCUSD",
  667. # "contractType": "PERPETUAL",
  668. # "deliveryDate": 4133404800000,
  669. # "onboardDate": 1596006000000,
  670. # "contractStatus": "TRADING",
  671. # "contractSize": 100,
  672. # "marginAsset": "BTC",
  673. # "maintMarginPercent": "2.5000",
  674. # "requiredMarginPercent": "5.0000",
  675. # "baseAsset": "BTC",
  676. # "quoteAsset": "USD",
  677. # "pricePrecision": 1,
  678. # "quantityPrecision": 0,
  679. # "baseAssetPrecision": 8,
  680. # "quotePrecision": 8,
  681. # "equalQtyPrecision": 4,
  682. # "filters": [
  683. # {"minPrice":"0.1","maxPrice":"100000","filterType":"PRICE_FILTER","tickSize":"0.1"},
  684. # {"stepSize":"1","filterType":"LOT_SIZE","maxQty":"100000","minQty":"1"},
  685. # {"stepSize":"1","filterType":"MARKET_LOT_SIZE","maxQty":"100000","minQty":"1"},
  686. # {"limit":200,"filterType":"MAX_NUM_ORDERS"},
  687. # {"multiplierDown":"0.8500","multiplierUp":"1.1500","multiplierDecimal":"4","filterType":"PERCENT_PRICE"}
  688. # ],
  689. # "orderTypes": ["LIMIT","MARKET","STOP","STOP_MARKET","TAKE_PROFIT","TAKE_PROFIT_MARKET","TRAILING_STOP_MARKET"],
  690. # "timeInForce": ["GTC","IOC","FOK","GTX"]
  691. # }
  692. # ]
  693. # }
  694. #
  695. if self.options['adjustForTimeDifference']:
  696. self.load_time_difference()
  697. markets = self.safe_value(response, 'symbols')
  698. result = []
  699. for i in range(0, len(markets)):
  700. market = markets[i]
  701. marketType = 'spot'
  702. future = False
  703. delivery = False
  704. if 'maintMarginPercent' in market:
  705. delivery = ('marginAsset' in market)
  706. future = not delivery
  707. marketType = 'delivery' if delivery else 'future'
  708. spot = not (future or delivery)
  709. id = self.safe_string(market, 'symbol')
  710. lowercaseId = self.safe_string_lower(market, 'symbol')
  711. baseId = self.safe_string(market, 'baseAsset')
  712. quoteId = self.safe_string(market, 'quoteAsset')
  713. base = self.safe_currency_code(baseId)
  714. quote = self.safe_currency_code(quoteId)
  715. symbol = id if delivery else (base + '/' + quote)
  716. filters = self.safe_value(market, 'filters', [])
  717. filtersByType = self.index_by(filters, 'filterType')
  718. precision = {
  719. 'base': self.safe_integer(market, 'baseAssetPrecision'),
  720. 'quote': self.safe_integer(market, 'quotePrecision'),
  721. 'amount': self.safe_integer(market, 'baseAssetPrecision'),
  722. 'price': self.safe_integer(market, 'quotePrecision'),
  723. }
  724. status = self.safe_string_2(market, 'status', 'contractStatus')
  725. active = (status == 'TRADING')
  726. margin = self.safe_value(market, 'isMarginTradingAllowed', future or delivery)
  727. entry = {
  728. 'id': id,
  729. 'lowercaseId': lowercaseId,
  730. 'symbol': symbol,
  731. 'base': base,
  732. 'quote': quote,
  733. 'baseId': baseId,
  734. 'quoteId': quoteId,
  735. 'info': market,
  736. 'type': marketType,
  737. 'spot': spot,
  738. 'margin': margin,
  739. 'future': future,
  740. 'delivery': delivery,
  741. 'active': active,
  742. 'precision': precision,
  743. 'limits': {
  744. 'amount': {
  745. 'min': math.pow(10, -precision['amount']),
  746. 'max': None,
  747. },
  748. 'price': {
  749. 'min': None,
  750. 'max': None,
  751. },
  752. 'cost': {
  753. 'min': None,
  754. 'max': None,
  755. },
  756. },
  757. }
  758. if 'PRICE_FILTER' in filtersByType:
  759. filter = self.safe_value(filtersByType, 'PRICE_FILTER', {})
  760. # PRICE_FILTER reports zero values for maxPrice
  761. # since they updated filter types in November 2018
  762. # https://github.com/ccxt/ccxt/issues/4286
  763. # therefore limits['price']['max'] doesn't have any meaningful value except None
  764. entry['limits']['price'] = {
  765. 'min': self.safe_float(filter, 'minPrice'),
  766. 'max': None,
  767. }
  768. maxPrice = self.safe_float(filter, 'maxPrice')
  769. if (maxPrice is not None) and (maxPrice > 0):
  770. entry['limits']['price']['max'] = maxPrice
  771. entry['precision']['price'] = self.precision_from_string(filter['tickSize'])
  772. if 'LOT_SIZE' in filtersByType:
  773. filter = self.safe_value(filtersByType, 'LOT_SIZE', {})
  774. stepSize = self.safe_string(filter, 'stepSize')
  775. entry['precision']['amount'] = self.precision_from_string(stepSize)
  776. entry['limits']['amount'] = {
  777. 'min': self.safe_float(filter, 'minQty'),
  778. 'max': self.safe_float(filter, 'maxQty'),
  779. }
  780. if 'MARKET_LOT_SIZE' in filtersByType:
  781. filter = self.safe_value(filtersByType, 'MARKET_LOT_SIZE', {})
  782. entry['limits']['market'] = {
  783. 'min': self.safe_float(filter, 'minQty'),
  784. 'max': self.safe_float(filter, 'maxQty'),
  785. }
  786. if 'MIN_NOTIONAL' in filtersByType:
  787. filter = self.safe_value(filtersByType, 'MIN_NOTIONAL', {})
  788. entry['limits']['cost']['min'] = self.safe_float(filter, 'minNotional')
  789. result.append(entry)
  790. return result
  791. def calculate_fee(self, symbol, type, side, amount, price, takerOrMaker='taker', params={}):
  792. market = self.markets[symbol]
  793. key = 'quote'
  794. rate = market[takerOrMaker]
  795. cost = amount * rate
  796. precision = market['precision']['price']
  797. if side == 'sell':
  798. cost *= price
  799. else:
  800. key = 'base'
  801. precision = market['precision']['amount']
  802. cost = self.decimal_to_precision(cost, ROUND, precision, self.precisionMode)
  803. return {
  804. 'type': takerOrMaker,
  805. 'currency': market[key],
  806. 'rate': rate,
  807. 'cost': float(cost),
  808. }
  809. def fetch_balance(self, params={}):
  810. self.load_markets()
  811. defaultType = self.safe_string_2(self.options, 'fetchBalance', 'defaultType', 'spot')
  812. type = self.safe_string(params, 'type', defaultType)
  813. method = 'privateGetAccount'
  814. if type == 'future':
  815. options = self.safe_value(self.options, 'future', {})
  816. fetchBalanceOptions = self.safe_value(options, 'fetchBalance', {})
  817. method = self.safe_string(fetchBalanceOptions, 'method', 'fapiPrivateV2GetAccount')
  818. elif type == 'delivery':
  819. options = self.safe_value(self.options, 'delivery', {})
  820. fetchBalanceOptions = self.safe_value(options, 'fetchBalance', {})
  821. method = self.safe_string(fetchBalanceOptions, 'method', 'dapiPrivateGetAccount')
  822. elif type == 'margin':
  823. method = 'sapiGetMarginAccount'
  824. query = self.omit(params, 'type')
  825. response = getattr(self, method)(query)
  826. #
  827. # spot
  828. #
  829. # {
  830. # makerCommission: 10,
  831. # takerCommission: 10,
  832. # buyerCommission: 0,
  833. # sellerCommission: 0,
  834. # canTrade: True,
  835. # canWithdraw: True,
  836. # canDeposit: True,
  837. # updateTime: 1575357359602,
  838. # accountType: "MARGIN",
  839. # balances: [
  840. # {asset: "BTC", free: "0.00219821", locked: "0.00000000" },
  841. # ]
  842. # }
  843. #
  844. # margin
  845. #
  846. # {
  847. # "borrowEnabled":true,
  848. # "marginLevel":"999.00000000",
  849. # "totalAssetOfBtc":"0.00000000",
  850. # "totalLiabilityOfBtc":"0.00000000",
  851. # "totalNetAssetOfBtc":"0.00000000",
  852. # "tradeEnabled":true,
  853. # "transferEnabled":true,
  854. # "userAssets":[
  855. # {"asset":"MATIC","borrowed":"0.00000000","free":"0.00000000","interest":"0.00000000","locked":"0.00000000","netAsset":"0.00000000"},
  856. # {"asset":"VET","borrowed":"0.00000000","free":"0.00000000","interest":"0.00000000","locked":"0.00000000","netAsset":"0.00000000"},
  857. # {"asset":"USDT","borrowed":"0.00000000","free":"0.00000000","interest":"0.00000000","locked":"0.00000000","netAsset":"0.00000000"}
  858. # ],
  859. # }
  860. #
  861. # futures(fapi)
  862. #
  863. # fapiPrivateGetAccount
  864. #
  865. # {
  866. # "feeTier":0,
  867. # "canTrade":true,
  868. # "canDeposit":true,
  869. # "canWithdraw":true,
  870. # "updateTime":0,
  871. # "totalInitialMargin":"0.00000000",
  872. # "totalMaintMargin":"0.00000000",
  873. # "totalWalletBalance":"4.54000000",
  874. # "totalUnrealizedProfit":"0.00000000",
  875. # "totalMarginBalance":"4.54000000",
  876. # "totalPositionInitialMargin":"0.00000000",
  877. # "totalOpenOrderInitialMargin":"0.00000000",
  878. # "maxWithdrawAmount":"4.54000000",
  879. # "assets":[
  880. # {
  881. # "asset":"USDT",
  882. # "walletBalance":"4.54000000",
  883. # "unrealizedProfit":"0.00000000",
  884. # "marginBalance":"4.54000000",
  885. # "maintMargin":"0.00000000",
  886. # "initialMargin":"0.00000000",
  887. # "positionInitialMargin":"0.00000000",
  888. # "openOrderInitialMargin":"0.00000000",
  889. # "maxWithdrawAmount":"4.54000000"
  890. # }
  891. # ],
  892. # "positions":[
  893. # {
  894. # "symbol":"BTCUSDT",
  895. # "initialMargin":"0.00000",
  896. # "maintMargin":"0.00000",
  897. # "unrealizedProfit":"0.00000000",
  898. # "positionInitialMargin":"0.00000",
  899. # "openOrderInitialMargin":"0.00000"
  900. # }
  901. # ]
  902. # }
  903. #
  904. # fapiPrivateV2GetAccount
  905. #
  906. # {
  907. # "feeTier":0,
  908. # "canTrade":true,
  909. # "canDeposit":true,
  910. # "canWithdraw":true,
  911. # "updateTime":0,
  912. # "totalInitialMargin":"0.00000000",
  913. # "totalMaintMargin":"0.00000000",
  914. # "totalWalletBalance":"0.00000000",
  915. # "totalUnrealizedProfit":"0.00000000",
  916. # "totalMarginBalance":"0.00000000",
  917. # "totalPositionInitialMargin":"0.00000000",
  918. # "totalOpenOrderInitialMargin":"0.00000000",
  919. # "totalCrossWalletBalance":"0.00000000",
  920. # "totalCrossUnPnl":"0.00000000",
  921. # "availableBalance":"0.00000000",
  922. # "maxWithdrawAmount":"0.00000000",
  923. # "assets":[
  924. # {
  925. # "asset":"BNB",
  926. # "walletBalance":"0.01000000",
  927. # "unrealizedProfit":"0.00000000",
  928. # "marginBalance":"0.01000000",
  929. # "maintMargin":"0.00000000",
  930. # "initialMargin":"0.00000000",
  931. # "positionInitialMargin":"0.00000000",
  932. # "openOrderInitialMargin":"0.00000000",
  933. # "maxWithdrawAmount":"0.01000000",
  934. # "crossWalletBalance":"0.01000000",
  935. # "crossUnPnl":"0.00000000",
  936. # "availableBalance":"0.01000000"
  937. # }
  938. # ],
  939. # "positions":[
  940. # {
  941. # "symbol":"BTCUSDT",
  942. # "initialMargin":"0",
  943. # "maintMargin":"0",
  944. # "unrealizedProfit":"0.00000000",
  945. # "positionInitialMargin":"0",
  946. # "openOrderInitialMargin":"0",
  947. # "leverage":"20",
  948. # "isolated":false,
  949. # "entryPrice":"0.00000",
  950. # "maxNotional":"5000000",
  951. # "positionSide":"BOTH"
  952. # },
  953. # ]
  954. # }
  955. #
  956. # fapiPrivateV2GetBalance
  957. #
  958. # [
  959. # {
  960. # "accountAlias":"FzFzXquXXqoC",
  961. # "asset":"BNB",
  962. # "balance":"0.01000000",
  963. # "crossWalletBalance":"0.01000000",
  964. # "crossUnPnl":"0.00000000",
  965. # "availableBalance":"0.01000000",
  966. # "maxWithdrawAmount":"0.01000000"
  967. # }
  968. # ]
  969. #
  970. result = {'info': response}
  971. if (type == 'spot') or (type == 'margin'):
  972. balances = self.safe_value_2(response, 'balances', 'userAssets', [])
  973. for i in range(0, len(balances)):
  974. balance = balances[i]
  975. currencyId = self.safe_string(balance, 'asset')
  976. code = self.safe_currency_code(currencyId)
  977. account = self.account()
  978. account['free'] = self.safe_float(balance, 'free')
  979. account['used'] = self.safe_float(balance, 'locked')
  980. result[code] = account
  981. else:
  982. balances = response
  983. if not isinstance(response, list):
  984. balances = self.safe_value(response, 'assets', [])
  985. for i in range(0, len(balances)):
  986. balance = balances[i]
  987. currencyId = self.safe_string(balance, 'asset')
  988. code = self.safe_currency_code(currencyId)
  989. account = self.account()
  990. account['free'] = self.safe_float(balance, 'availableBalance')
  991. account['used'] = self.safe_float(balance, 'initialMargin')
  992. account['total'] = self.safe_float_2(balance, 'marginBalance', 'balance')
  993. result[code] = account
  994. return self.parse_balance(result)
  995. def fetch_order_book(self, symbol, limit=None, params={}):
  996. self.load_markets()
  997. market = self.market(symbol)
  998. request = {
  999. 'symbol': market['id'],
  1000. }
  1001. if limit is not None:
  1002. request['limit'] = limit # default 100, max 5000, see https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#order-book
  1003. method = 'publicGetDepth'
  1004. if market['future']:
  1005. method = 'fapiPublicGetDepth'
  1006. elif market['delivery']:
  1007. method = 'dapiPublicGetDepth'
  1008. response = getattr(self, method)(self.extend(request, params))
  1009. orderbook = self.parse_order_book(response)
  1010. orderbook['nonce'] = self.safe_integer(response, 'lastUpdateId')
  1011. return orderbook
  1012. def parse_ticker(self, ticker, market=None):
  1013. #
  1014. # {
  1015. # symbol: 'ETHBTC',
  1016. # priceChange: '0.00068700',
  1017. # priceChangePercent: '2.075',
  1018. # weightedAvgPrice: '0.03342681',
  1019. # prevClosePrice: '0.03310300',
  1020. # lastPrice: '0.03378900',
  1021. # lastQty: '0.07700000',
  1022. # bidPrice: '0.03378900',
  1023. # bidQty: '7.16800000',
  1024. # askPrice: '0.03379000',
  1025. # askQty: '24.00000000',
  1026. # openPrice: '0.03310200',
  1027. # highPrice: '0.03388900',
  1028. # lowPrice: '0.03306900',
  1029. # volume: '205478.41000000',
  1030. # quoteVolume: '6868.48826294',
  1031. # openTime: 1601469986932,
  1032. # closeTime: 1601556386932,
  1033. # firstId: 196098772,
  1034. # lastId: 196186315,
  1035. # count: 87544
  1036. # }
  1037. #
  1038. timestamp = self.safe_integer(ticker, 'closeTime')
  1039. marketId = self.safe_string(ticker, 'symbol')
  1040. symbol = self.safe_symbol(marketId, market)
  1041. last = self.safe_float(ticker, 'lastPrice')
  1042. return {
  1043. 'symbol': symbol,
  1044. 'timestamp': timestamp,