ccxt-ir 4.3.46.0.1__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (772) hide show
  1. ccxt/__init__.py +358 -0
  2. ccxt/abantether.py +316 -0
  3. ccxt/abstract/__init__.py +0 -0
  4. ccxt/abstract/abantether.py +5 -0
  5. ccxt/abstract/ace.py +15 -0
  6. ccxt/abstract/afratether.py +6 -0
  7. ccxt/abstract/alpaca.py +70 -0
  8. ccxt/abstract/arzinja.py +5 -0
  9. ccxt/abstract/arzplus.py +7 -0
  10. ccxt/abstract/ascendex.py +77 -0
  11. ccxt/abstract/bequant.py +115 -0
  12. ccxt/abstract/bigone.py +45 -0
  13. ccxt/abstract/binance.py +712 -0
  14. ccxt/abstract/binancecoinm.py +712 -0
  15. ccxt/abstract/binanceus.py +764 -0
  16. ccxt/abstract/binanceusdm.py +712 -0
  17. ccxt/abstract/bingx.py +113 -0
  18. ccxt/abstract/bit2c.py +27 -0
  19. ccxt/abstract/bitbank.py +27 -0
  20. ccxt/abstract/bitbay.py +53 -0
  21. ccxt/abstract/bitbns.py +40 -0
  22. ccxt/abstract/bitcoincom.py +115 -0
  23. ccxt/abstract/bitfinex.py +69 -0
  24. ccxt/abstract/bitfinex2.py +139 -0
  25. ccxt/abstract/bitflyer.py +38 -0
  26. ccxt/abstract/bitget.py +508 -0
  27. ccxt/abstract/bithumb.py +32 -0
  28. ccxt/abstract/bitimen.py +7 -0
  29. ccxt/abstract/bitir.py +7 -0
  30. ccxt/abstract/bitmart.py +99 -0
  31. ccxt/abstract/bitmex.py +97 -0
  32. ccxt/abstract/bitopro.py +29 -0
  33. ccxt/abstract/bitpanda.py +35 -0
  34. ccxt/abstract/bitpin.py +7 -0
  35. ccxt/abstract/bitrue.py +72 -0
  36. ccxt/abstract/bitso.py +43 -0
  37. ccxt/abstract/bitstamp.py +258 -0
  38. ccxt/abstract/bitteam.py +29 -0
  39. ccxt/abstract/bitvavo.py +27 -0
  40. ccxt/abstract/bl3p.py +19 -0
  41. ccxt/abstract/blockchaincom.py +28 -0
  42. ccxt/abstract/blofin.py +37 -0
  43. ccxt/abstract/btcalpha.py +18 -0
  44. ccxt/abstract/btcbox.py +13 -0
  45. ccxt/abstract/btcmarkets.py +39 -0
  46. ccxt/abstract/btcturk.py +20 -0
  47. ccxt/abstract/bybit.py +298 -0
  48. ccxt/abstract/cex.py +33 -0
  49. ccxt/abstract/coinbase.py +94 -0
  50. ccxt/abstract/coinbaseadvanced.py +94 -0
  51. ccxt/abstract/coinbaseexchange.py +67 -0
  52. ccxt/abstract/coinbaseinternational.py +39 -0
  53. ccxt/abstract/coincatch.py +94 -0
  54. ccxt/abstract/coincheck.py +33 -0
  55. ccxt/abstract/coinex.py +237 -0
  56. ccxt/abstract/coinlist.py +54 -0
  57. ccxt/abstract/coinmate.py +62 -0
  58. ccxt/abstract/coinmetro.py +34 -0
  59. ccxt/abstract/coinone.py +67 -0
  60. ccxt/abstract/coinsph.py +54 -0
  61. ccxt/abstract/coinspot.py +28 -0
  62. ccxt/abstract/cryptocom.py +107 -0
  63. ccxt/abstract/currencycom.py +68 -0
  64. ccxt/abstract/delta.py +50 -0
  65. ccxt/abstract/deribit.py +125 -0
  66. ccxt/abstract/digifinex.py +91 -0
  67. ccxt/abstract/eterex.py +5 -0
  68. ccxt/abstract/excoino.py +7 -0
  69. ccxt/abstract/exir.py +8 -0
  70. ccxt/abstract/exmo.py +55 -0
  71. ccxt/abstract/exnovin.py +6 -0
  72. ccxt/abstract/farhadexchange.py +5 -0
  73. ccxt/abstract/fmfwio.py +115 -0
  74. ccxt/abstract/gate.py +265 -0
  75. ccxt/abstract/gateio.py +265 -0
  76. ccxt/abstract/gemini.py +58 -0
  77. ccxt/abstract/hashkey.py +67 -0
  78. ccxt/abstract/hitbtc.py +115 -0
  79. ccxt/abstract/hitbtc3.py +115 -0
  80. ccxt/abstract/hitobit.py +8 -0
  81. ccxt/abstract/hollaex.py +33 -0
  82. ccxt/abstract/htx.py +548 -0
  83. ccxt/abstract/huobi.py +548 -0
  84. ccxt/abstract/huobijp.py +114 -0
  85. ccxt/abstract/hyperliquid.py +6 -0
  86. ccxt/abstract/idex.py +26 -0
  87. ccxt/abstract/independentreserve.py +37 -0
  88. ccxt/abstract/indodax.py +26 -0
  89. ccxt/abstract/jibitex.py +7 -0
  90. ccxt/abstract/kraken.py +57 -0
  91. ccxt/abstract/krakenfutures.py +38 -0
  92. ccxt/abstract/kucoin.py +214 -0
  93. ccxt/abstract/kucoinfutures.py +233 -0
  94. ccxt/abstract/kuna.py +182 -0
  95. ccxt/abstract/latoken.py +56 -0
  96. ccxt/abstract/lbank.py +61 -0
  97. ccxt/abstract/luno.py +37 -0
  98. ccxt/abstract/lykke.py +29 -0
  99. ccxt/abstract/mercado.py +25 -0
  100. ccxt/abstract/mexc.py +178 -0
  101. ccxt/abstract/ndax.py +97 -0
  102. ccxt/abstract/nobitex.py +7 -0
  103. ccxt/abstract/novadax.py +29 -0
  104. ccxt/abstract/oceanex.py +22 -0
  105. ccxt/abstract/okcoin.py +74 -0
  106. ccxt/abstract/okexchange.py +8 -0
  107. ccxt/abstract/okx.py +324 -0
  108. ccxt/abstract/ompfinex.py +7 -0
  109. ccxt/abstract/onetrading.py +35 -0
  110. ccxt/abstract/oxfun.py +34 -0
  111. ccxt/abstract/p2b.py +22 -0
  112. ccxt/abstract/paradex.py +40 -0
  113. ccxt/abstract/paymium.py +28 -0
  114. ccxt/abstract/phemex.py +115 -0
  115. ccxt/abstract/poloniex.py +69 -0
  116. ccxt/abstract/poloniexfutures.py +48 -0
  117. ccxt/abstract/probit.py +23 -0
  118. ccxt/abstract/ramzinex.py +7 -0
  119. ccxt/abstract/sarmayex.py +5 -0
  120. ccxt/abstract/sarrafex.py +7 -0
  121. ccxt/abstract/tabdeal.py +7 -0
  122. ccxt/abstract/tetherland.py +5 -0
  123. ccxt/abstract/timex.py +62 -0
  124. ccxt/abstract/tokocrypto.py +37 -0
  125. ccxt/abstract/tradeogre.py +16 -0
  126. ccxt/abstract/twox.py +5 -0
  127. ccxt/abstract/ubitex.py +7 -0
  128. ccxt/abstract/upbit.py +38 -0
  129. ccxt/abstract/vertex.py +19 -0
  130. ccxt/abstract/wallex.py +8 -0
  131. ccxt/abstract/wavesexchange.py +154 -0
  132. ccxt/abstract/wazirx.py +30 -0
  133. ccxt/abstract/whitebit.py +98 -0
  134. ccxt/abstract/woo.py +83 -0
  135. ccxt/abstract/woofipro.py +119 -0
  136. ccxt/abstract/xt.py +152 -0
  137. ccxt/abstract/yobit.py +16 -0
  138. ccxt/abstract/zaif.py +38 -0
  139. ccxt/abstract/zonda.py +53 -0
  140. ccxt/ace.py +1012 -0
  141. ccxt/afratether.py +293 -0
  142. ccxt/alpaca.py +1083 -0
  143. ccxt/arzinja.py +285 -0
  144. ccxt/arzplus.py +412 -0
  145. ccxt/ascendex.py +3330 -0
  146. ccxt/async_support/__init__.py +337 -0
  147. ccxt/async_support/abantether.py +316 -0
  148. ccxt/async_support/ace.py +1012 -0
  149. ccxt/async_support/afratether.py +293 -0
  150. ccxt/async_support/alpaca.py +1083 -0
  151. ccxt/async_support/arzinja.py +285 -0
  152. ccxt/async_support/arzplus.py +412 -0
  153. ccxt/async_support/ascendex.py +3330 -0
  154. ccxt/async_support/base/__init__.py +1 -0
  155. ccxt/async_support/base/exchange.py +1966 -0
  156. ccxt/async_support/base/throttler.py +50 -0
  157. ccxt/async_support/base/ws/__init__.py +38 -0
  158. ccxt/async_support/base/ws/aiohttp_client.py +125 -0
  159. ccxt/async_support/base/ws/cache.py +212 -0
  160. ccxt/async_support/base/ws/client.py +193 -0
  161. ccxt/async_support/base/ws/fast_client.py +96 -0
  162. ccxt/async_support/base/ws/functions.py +59 -0
  163. ccxt/async_support/base/ws/future.py +58 -0
  164. ccxt/async_support/base/ws/order_book.py +78 -0
  165. ccxt/async_support/base/ws/order_book_side.py +174 -0
  166. ccxt/async_support/bequant.py +33 -0
  167. ccxt/async_support/bigone.py +2113 -0
  168. ccxt/async_support/binance.py +12234 -0
  169. ccxt/async_support/binancecoinm.py +45 -0
  170. ccxt/async_support/binanceus.py +211 -0
  171. ccxt/async_support/binanceusdm.py +58 -0
  172. ccxt/async_support/bingx.py +4325 -0
  173. ccxt/async_support/bit2c.py +866 -0
  174. ccxt/async_support/bitbank.py +1001 -0
  175. ccxt/async_support/bitbay.py +17 -0
  176. ccxt/async_support/bitbns.py +1154 -0
  177. ccxt/async_support/bitcoincom.py +17 -0
  178. ccxt/async_support/bitfinex.py +1617 -0
  179. ccxt/async_support/bitfinex2.py +3552 -0
  180. ccxt/async_support/bitflyer.py +995 -0
  181. ccxt/async_support/bitget.py +8273 -0
  182. ccxt/async_support/bithumb.py +1061 -0
  183. ccxt/async_support/bitimen.py +401 -0
  184. ccxt/async_support/bitir.py +490 -0
  185. ccxt/async_support/bitmart.py +4415 -0
  186. ccxt/async_support/bitmex.py +2756 -0
  187. ccxt/async_support/bitopro.py +1630 -0
  188. ccxt/async_support/bitpanda.py +16 -0
  189. ccxt/async_support/bitpin.py +454 -0
  190. ccxt/async_support/bitrue.py +3027 -0
  191. ccxt/async_support/bitso.py +1670 -0
  192. ccxt/async_support/bitstamp.py +2203 -0
  193. ccxt/async_support/bitteam.py +2239 -0
  194. ccxt/async_support/bitvavo.py +1968 -0
  195. ccxt/async_support/bl3p.py +485 -0
  196. ccxt/async_support/blockchaincom.py +1104 -0
  197. ccxt/async_support/blofin.py +2066 -0
  198. ccxt/async_support/btcalpha.py +891 -0
  199. ccxt/async_support/btcbox.py +544 -0
  200. ccxt/async_support/btcmarkets.py +1221 -0
  201. ccxt/async_support/btcturk.py +911 -0
  202. ccxt/async_support/bybit.py +8159 -0
  203. ccxt/async_support/cex.py +1605 -0
  204. ccxt/async_support/coinbase.py +4475 -0
  205. ccxt/async_support/coinbaseadvanced.py +17 -0
  206. ccxt/async_support/coinbaseexchange.py +1734 -0
  207. ccxt/async_support/coinbaseinternational.py +1899 -0
  208. ccxt/async_support/coincatch.py +5069 -0
  209. ccxt/async_support/coincheck.py +815 -0
  210. ccxt/async_support/coinex.py +5526 -0
  211. ccxt/async_support/coinlist.py +2243 -0
  212. ccxt/async_support/coinmate.py +1067 -0
  213. ccxt/async_support/coinmetro.py +1797 -0
  214. ccxt/async_support/coinone.py +1127 -0
  215. ccxt/async_support/coinsph.py +1850 -0
  216. ccxt/async_support/coinspot.py +534 -0
  217. ccxt/async_support/cryptocom.py +2822 -0
  218. ccxt/async_support/currencycom.py +1950 -0
  219. ccxt/async_support/delta.py +3376 -0
  220. ccxt/async_support/deribit.py +3437 -0
  221. ccxt/async_support/digifinex.py +3960 -0
  222. ccxt/async_support/eterex.py +286 -0
  223. ccxt/async_support/excoino.py +399 -0
  224. ccxt/async_support/exir.py +375 -0
  225. ccxt/async_support/exmo.py +2462 -0
  226. ccxt/async_support/exnovin.py +360 -0
  227. ccxt/async_support/farhadexchange.py +266 -0
  228. ccxt/async_support/fmfwio.py +34 -0
  229. ccxt/async_support/gate.py +6976 -0
  230. ccxt/async_support/gateio.py +16 -0
  231. ccxt/async_support/gemini.py +1825 -0
  232. ccxt/async_support/hashkey.py +4150 -0
  233. ccxt/async_support/hitbtc.py +3423 -0
  234. ccxt/async_support/hitbtc3.py +16 -0
  235. ccxt/async_support/hitobit.py +391 -0
  236. ccxt/async_support/hollaex.py +1813 -0
  237. ccxt/async_support/htx.py +8506 -0
  238. ccxt/async_support/huobi.py +16 -0
  239. ccxt/async_support/huobijp.py +1801 -0
  240. ccxt/async_support/hyperliquid.py +2431 -0
  241. ccxt/async_support/idex.py +1766 -0
  242. ccxt/async_support/independentreserve.py +784 -0
  243. ccxt/async_support/indodax.py +1247 -0
  244. ccxt/async_support/jibitex.py +395 -0
  245. ccxt/async_support/kraken.py +2894 -0
  246. ccxt/async_support/krakenfutures.py +2601 -0
  247. ccxt/async_support/kucoin.py +4602 -0
  248. ccxt/async_support/kucoinfutures.py +2698 -0
  249. ccxt/async_support/kuna.py +1841 -0
  250. ccxt/async_support/latoken.py +1664 -0
  251. ccxt/async_support/lbank.py +2683 -0
  252. ccxt/async_support/luno.py +1067 -0
  253. ccxt/async_support/lykke.py +1270 -0
  254. ccxt/async_support/mercado.py +842 -0
  255. ccxt/async_support/mexc.py +5369 -0
  256. ccxt/async_support/ndax.py +2354 -0
  257. ccxt/async_support/nobitex.py +419 -0
  258. ccxt/async_support/novadax.py +1484 -0
  259. ccxt/async_support/oceanex.py +903 -0
  260. ccxt/async_support/okcoin.py +2936 -0
  261. ccxt/async_support/okexchange.py +349 -0
  262. ccxt/async_support/okx.py +7827 -0
  263. ccxt/async_support/ompfinex.py +472 -0
  264. ccxt/async_support/onetrading.py +1911 -0
  265. ccxt/async_support/oxfun.py +2773 -0
  266. ccxt/async_support/p2b.py +1194 -0
  267. ccxt/async_support/paradex.py +2015 -0
  268. ccxt/async_support/paymium.py +564 -0
  269. ccxt/async_support/phemex.py +4473 -0
  270. ccxt/async_support/poloniex.py +2232 -0
  271. ccxt/async_support/poloniexfutures.py +1717 -0
  272. ccxt/async_support/probit.py +1734 -0
  273. ccxt/async_support/ramzinex.py +476 -0
  274. ccxt/async_support/sarmayex.py +357 -0
  275. ccxt/async_support/sarrafex.py +478 -0
  276. ccxt/async_support/tabdeal.py +364 -0
  277. ccxt/async_support/tetherland.py +349 -0
  278. ccxt/async_support/timex.py +1593 -0
  279. ccxt/async_support/tokocrypto.py +2405 -0
  280. ccxt/async_support/tradeogre.py +608 -0
  281. ccxt/async_support/twox.py +326 -0
  282. ccxt/async_support/ubitex.py +409 -0
  283. ccxt/async_support/upbit.py +1833 -0
  284. ccxt/async_support/vertex.py +2922 -0
  285. ccxt/async_support/wallex.py +445 -0
  286. ccxt/async_support/wavesexchange.py +2473 -0
  287. ccxt/async_support/wazirx.py +1224 -0
  288. ccxt/async_support/whitebit.py +2469 -0
  289. ccxt/async_support/woo.py +3114 -0
  290. ccxt/async_support/woofipro.py +2533 -0
  291. ccxt/async_support/xt.py +4454 -0
  292. ccxt/async_support/yobit.py +1283 -0
  293. ccxt/async_support/zaif.py +725 -0
  294. ccxt/async_support/zonda.py +1828 -0
  295. ccxt/base/__init__.py +27 -0
  296. ccxt/base/decimal_to_precision.py +174 -0
  297. ccxt/base/errors.py +242 -0
  298. ccxt/base/exchange.py +5941 -0
  299. ccxt/base/precise.py +287 -0
  300. ccxt/base/types.py +502 -0
  301. ccxt/bequant.py +33 -0
  302. ccxt/bigone.py +2112 -0
  303. ccxt/binance.py +12233 -0
  304. ccxt/binancecoinm.py +45 -0
  305. ccxt/binanceus.py +211 -0
  306. ccxt/binanceusdm.py +58 -0
  307. ccxt/bingx.py +4324 -0
  308. ccxt/bit2c.py +866 -0
  309. ccxt/bitbank.py +1001 -0
  310. ccxt/bitbay.py +17 -0
  311. ccxt/bitbns.py +1154 -0
  312. ccxt/bitcoincom.py +17 -0
  313. ccxt/bitfinex.py +1617 -0
  314. ccxt/bitfinex2.py +3552 -0
  315. ccxt/bitflyer.py +995 -0
  316. ccxt/bitget.py +8272 -0
  317. ccxt/bithumb.py +1061 -0
  318. ccxt/bitimen.py +401 -0
  319. ccxt/bitir.py +490 -0
  320. ccxt/bitmart.py +4415 -0
  321. ccxt/bitmex.py +2756 -0
  322. ccxt/bitopro.py +1630 -0
  323. ccxt/bitpanda.py +16 -0
  324. ccxt/bitpin.py +454 -0
  325. ccxt/bitrue.py +3026 -0
  326. ccxt/bitso.py +1670 -0
  327. ccxt/bitstamp.py +2203 -0
  328. ccxt/bitteam.py +2239 -0
  329. ccxt/bitvavo.py +1968 -0
  330. ccxt/bl3p.py +485 -0
  331. ccxt/blockchaincom.py +1104 -0
  332. ccxt/blofin.py +2066 -0
  333. ccxt/btcalpha.py +891 -0
  334. ccxt/btcbox.py +544 -0
  335. ccxt/btcmarkets.py +1221 -0
  336. ccxt/btcturk.py +911 -0
  337. ccxt/bybit.py +8158 -0
  338. ccxt/cex.py +1605 -0
  339. ccxt/coinbase.py +4474 -0
  340. ccxt/coinbaseadvanced.py +17 -0
  341. ccxt/coinbaseexchange.py +1734 -0
  342. ccxt/coinbaseinternational.py +1899 -0
  343. ccxt/coincatch.py +5069 -0
  344. ccxt/coincheck.py +815 -0
  345. ccxt/coinex.py +5525 -0
  346. ccxt/coinlist.py +2243 -0
  347. ccxt/coinmate.py +1067 -0
  348. ccxt/coinmetro.py +1797 -0
  349. ccxt/coinone.py +1127 -0
  350. ccxt/coinsph.py +1850 -0
  351. ccxt/coinspot.py +534 -0
  352. ccxt/cryptocom.py +2822 -0
  353. ccxt/currencycom.py +1950 -0
  354. ccxt/delta.py +3376 -0
  355. ccxt/deribit.py +3437 -0
  356. ccxt/digifinex.py +3959 -0
  357. ccxt/eterex.py +286 -0
  358. ccxt/excoino.py +399 -0
  359. ccxt/exir.py +375 -0
  360. ccxt/exmo.py +2462 -0
  361. ccxt/exnovin.py +360 -0
  362. ccxt/farhadexchange.py +266 -0
  363. ccxt/fmfwio.py +34 -0
  364. ccxt/gate.py +6975 -0
  365. ccxt/gateio.py +16 -0
  366. ccxt/gemini.py +1824 -0
  367. ccxt/hashkey.py +4150 -0
  368. ccxt/hitbtc.py +3423 -0
  369. ccxt/hitbtc3.py +16 -0
  370. ccxt/hitobit.py +391 -0
  371. ccxt/hollaex.py +1813 -0
  372. ccxt/htx.py +8505 -0
  373. ccxt/huobi.py +16 -0
  374. ccxt/huobijp.py +1801 -0
  375. ccxt/hyperliquid.py +2430 -0
  376. ccxt/idex.py +1766 -0
  377. ccxt/independentreserve.py +784 -0
  378. ccxt/indodax.py +1247 -0
  379. ccxt/jibitex.py +395 -0
  380. ccxt/kraken.py +2894 -0
  381. ccxt/krakenfutures.py +2601 -0
  382. ccxt/kucoin.py +4601 -0
  383. ccxt/kucoinfutures.py +2698 -0
  384. ccxt/kuna.py +1841 -0
  385. ccxt/latoken.py +1664 -0
  386. ccxt/lbank.py +2682 -0
  387. ccxt/luno.py +1067 -0
  388. ccxt/lykke.py +1270 -0
  389. ccxt/mercado.py +842 -0
  390. ccxt/mexc.py +5369 -0
  391. ccxt/ndax.py +2354 -0
  392. ccxt/nobitex.py +419 -0
  393. ccxt/novadax.py +1484 -0
  394. ccxt/oceanex.py +903 -0
  395. ccxt/okcoin.py +2936 -0
  396. ccxt/okexchange.py +349 -0
  397. ccxt/okx.py +7826 -0
  398. ccxt/ompfinex.py +472 -0
  399. ccxt/onetrading.py +1911 -0
  400. ccxt/oxfun.py +2772 -0
  401. ccxt/p2b.py +1194 -0
  402. ccxt/paradex.py +2015 -0
  403. ccxt/paymium.py +564 -0
  404. ccxt/phemex.py +4473 -0
  405. ccxt/poloniex.py +2232 -0
  406. ccxt/poloniexfutures.py +1717 -0
  407. ccxt/pro/__init__.py +149 -0
  408. ccxt/pro/alpaca.py +685 -0
  409. ccxt/pro/ascendex.py +916 -0
  410. ccxt/pro/bequant.py +38 -0
  411. ccxt/pro/binance.py +3488 -0
  412. ccxt/pro/binancecoinm.py +28 -0
  413. ccxt/pro/binanceus.py +48 -0
  414. ccxt/pro/binanceusdm.py +31 -0
  415. ccxt/pro/bingx.py +1264 -0
  416. ccxt/pro/bitcoincom.py +34 -0
  417. ccxt/pro/bitfinex.py +621 -0
  418. ccxt/pro/bitfinex2.py +1083 -0
  419. ccxt/pro/bitget.py +1692 -0
  420. ccxt/pro/bithumb.py +368 -0
  421. ccxt/pro/bitmart.py +1449 -0
  422. ccxt/pro/bitmex.py +1656 -0
  423. ccxt/pro/bitopro.py +445 -0
  424. ccxt/pro/bitpanda.py +15 -0
  425. ccxt/pro/bitrue.py +447 -0
  426. ccxt/pro/bitstamp.py +522 -0
  427. ccxt/pro/bitvavo.py +1270 -0
  428. ccxt/pro/blockchaincom.py +738 -0
  429. ccxt/pro/blofin.py +692 -0
  430. ccxt/pro/bybit.py +2000 -0
  431. ccxt/pro/cex.py +1440 -0
  432. ccxt/pro/coinbase.py +678 -0
  433. ccxt/pro/coinbaseadvanced.py +16 -0
  434. ccxt/pro/coinbaseexchange.py +895 -0
  435. ccxt/pro/coinbaseinternational.py +620 -0
  436. ccxt/pro/coincatch.py +1464 -0
  437. ccxt/pro/coincheck.py +199 -0
  438. ccxt/pro/coinex.py +1061 -0
  439. ccxt/pro/coinone.py +395 -0
  440. ccxt/pro/cryptocom.py +947 -0
  441. ccxt/pro/currencycom.py +536 -0
  442. ccxt/pro/deribit.py +892 -0
  443. ccxt/pro/exmo.py +629 -0
  444. ccxt/pro/gate.py +1416 -0
  445. ccxt/pro/gateio.py +15 -0
  446. ccxt/pro/gemini.py +865 -0
  447. ccxt/pro/hashkey.py +802 -0
  448. ccxt/pro/hitbtc.py +1216 -0
  449. ccxt/pro/hollaex.py +563 -0
  450. ccxt/pro/htx.py +2215 -0
  451. ccxt/pro/huobi.py +15 -0
  452. ccxt/pro/huobijp.py +570 -0
  453. ccxt/pro/hyperliquid.py +525 -0
  454. ccxt/pro/idex.py +672 -0
  455. ccxt/pro/independentreserve.py +270 -0
  456. ccxt/pro/kraken.py +1356 -0
  457. ccxt/pro/krakenfutures.py +1492 -0
  458. ccxt/pro/kucoin.py +1133 -0
  459. ccxt/pro/kucoinfutures.py +1081 -0
  460. ccxt/pro/lbank.py +843 -0
  461. ccxt/pro/luno.py +303 -0
  462. ccxt/pro/mexc.py +1122 -0
  463. ccxt/pro/ndax.py +506 -0
  464. ccxt/pro/okcoin.py +698 -0
  465. ccxt/pro/okx.py +1851 -0
  466. ccxt/pro/onetrading.py +1275 -0
  467. ccxt/pro/oxfun.py +950 -0
  468. ccxt/pro/p2b.py +419 -0
  469. ccxt/pro/paradex.py +352 -0
  470. ccxt/pro/phemex.py +1441 -0
  471. ccxt/pro/poloniex.py +1166 -0
  472. ccxt/pro/poloniexfutures.py +990 -0
  473. ccxt/pro/probit.py +551 -0
  474. ccxt/pro/upbit.py +520 -0
  475. ccxt/pro/vertex.py +943 -0
  476. ccxt/pro/wazirx.py +749 -0
  477. ccxt/pro/whitebit.py +864 -0
  478. ccxt/pro/woo.py +1078 -0
  479. ccxt/pro/woofipro.py +1183 -0
  480. ccxt/pro/xt.py +1067 -0
  481. ccxt/probit.py +1734 -0
  482. ccxt/ramzinex.py +476 -0
  483. ccxt/sarmayex.py +357 -0
  484. ccxt/sarrafex.py +478 -0
  485. ccxt/static_dependencies/__init__.py +1 -0
  486. ccxt/static_dependencies/ecdsa/__init__.py +14 -0
  487. ccxt/static_dependencies/ecdsa/_version.py +520 -0
  488. ccxt/static_dependencies/ecdsa/curves.py +56 -0
  489. ccxt/static_dependencies/ecdsa/der.py +221 -0
  490. ccxt/static_dependencies/ecdsa/ecdsa.py +310 -0
  491. ccxt/static_dependencies/ecdsa/ellipticcurve.py +197 -0
  492. ccxt/static_dependencies/ecdsa/keys.py +332 -0
  493. ccxt/static_dependencies/ecdsa/numbertheory.py +531 -0
  494. ccxt/static_dependencies/ecdsa/rfc6979.py +100 -0
  495. ccxt/static_dependencies/ecdsa/util.py +266 -0
  496. ccxt/static_dependencies/ethereum/__init__.py +7 -0
  497. ccxt/static_dependencies/ethereum/abi/__init__.py +16 -0
  498. ccxt/static_dependencies/ethereum/abi/abi.py +19 -0
  499. ccxt/static_dependencies/ethereum/abi/base.py +152 -0
  500. ccxt/static_dependencies/ethereum/abi/codec.py +217 -0
  501. ccxt/static_dependencies/ethereum/abi/constants.py +3 -0
  502. ccxt/static_dependencies/ethereum/abi/decoding.py +565 -0
  503. ccxt/static_dependencies/ethereum/abi/encoding.py +720 -0
  504. ccxt/static_dependencies/ethereum/abi/exceptions.py +139 -0
  505. ccxt/static_dependencies/ethereum/abi/grammar.py +443 -0
  506. ccxt/static_dependencies/ethereum/abi/packed.py +13 -0
  507. ccxt/static_dependencies/ethereum/abi/py.typed +0 -0
  508. ccxt/static_dependencies/ethereum/abi/registry.py +643 -0
  509. ccxt/static_dependencies/ethereum/abi/tools/__init__.py +3 -0
  510. ccxt/static_dependencies/ethereum/abi/tools/_strategies.py +230 -0
  511. ccxt/static_dependencies/ethereum/abi/utils/__init__.py +0 -0
  512. ccxt/static_dependencies/ethereum/abi/utils/numeric.py +83 -0
  513. ccxt/static_dependencies/ethereum/abi/utils/padding.py +27 -0
  514. ccxt/static_dependencies/ethereum/abi/utils/string.py +19 -0
  515. ccxt/static_dependencies/ethereum/account/__init__.py +3 -0
  516. ccxt/static_dependencies/ethereum/account/encode_typed_data/__init__.py +4 -0
  517. ccxt/static_dependencies/ethereum/account/encode_typed_data/encoding_and_hashing.py +239 -0
  518. ccxt/static_dependencies/ethereum/account/encode_typed_data/helpers.py +40 -0
  519. ccxt/static_dependencies/ethereum/account/messages.py +263 -0
  520. ccxt/static_dependencies/ethereum/account/py.typed +0 -0
  521. ccxt/static_dependencies/ethereum/hexbytes/__init__.py +5 -0
  522. ccxt/static_dependencies/ethereum/hexbytes/_utils.py +54 -0
  523. ccxt/static_dependencies/ethereum/hexbytes/main.py +65 -0
  524. ccxt/static_dependencies/ethereum/hexbytes/py.typed +0 -0
  525. ccxt/static_dependencies/ethereum/typing/__init__.py +63 -0
  526. ccxt/static_dependencies/ethereum/typing/abi.py +6 -0
  527. ccxt/static_dependencies/ethereum/typing/bls.py +7 -0
  528. ccxt/static_dependencies/ethereum/typing/discovery.py +5 -0
  529. ccxt/static_dependencies/ethereum/typing/encoding.py +7 -0
  530. ccxt/static_dependencies/ethereum/typing/enums.py +17 -0
  531. ccxt/static_dependencies/ethereum/typing/ethpm.py +9 -0
  532. ccxt/static_dependencies/ethereum/typing/evm.py +20 -0
  533. ccxt/static_dependencies/ethereum/typing/networks.py +1122 -0
  534. ccxt/static_dependencies/ethereum/typing/py.typed +0 -0
  535. ccxt/static_dependencies/ethereum/utils/__init__.py +115 -0
  536. ccxt/static_dependencies/ethereum/utils/abi.py +72 -0
  537. ccxt/static_dependencies/ethereum/utils/address.py +171 -0
  538. ccxt/static_dependencies/ethereum/utils/applicators.py +151 -0
  539. ccxt/static_dependencies/ethereum/utils/conversions.py +190 -0
  540. ccxt/static_dependencies/ethereum/utils/currency.py +107 -0
  541. ccxt/static_dependencies/ethereum/utils/curried/__init__.py +269 -0
  542. ccxt/static_dependencies/ethereum/utils/debug.py +20 -0
  543. ccxt/static_dependencies/ethereum/utils/decorators.py +132 -0
  544. ccxt/static_dependencies/ethereum/utils/encoding.py +6 -0
  545. ccxt/static_dependencies/ethereum/utils/exceptions.py +4 -0
  546. ccxt/static_dependencies/ethereum/utils/functional.py +75 -0
  547. ccxt/static_dependencies/ethereum/utils/hexadecimal.py +74 -0
  548. ccxt/static_dependencies/ethereum/utils/humanize.py +188 -0
  549. ccxt/static_dependencies/ethereum/utils/logging.py +159 -0
  550. ccxt/static_dependencies/ethereum/utils/module_loading.py +31 -0
  551. ccxt/static_dependencies/ethereum/utils/numeric.py +43 -0
  552. ccxt/static_dependencies/ethereum/utils/py.typed +0 -0
  553. ccxt/static_dependencies/ethereum/utils/toolz.py +76 -0
  554. ccxt/static_dependencies/ethereum/utils/types.py +54 -0
  555. ccxt/static_dependencies/ethereum/utils/typing/__init__.py +18 -0
  556. ccxt/static_dependencies/ethereum/utils/typing/misc.py +14 -0
  557. ccxt/static_dependencies/ethereum/utils/units.py +31 -0
  558. ccxt/static_dependencies/keccak/__init__.py +3 -0
  559. ccxt/static_dependencies/keccak/keccak.py +197 -0
  560. ccxt/static_dependencies/lark/__init__.py +38 -0
  561. ccxt/static_dependencies/lark/__pyinstaller/__init__.py +6 -0
  562. ccxt/static_dependencies/lark/__pyinstaller/hook-lark.py +14 -0
  563. ccxt/static_dependencies/lark/ast_utils.py +59 -0
  564. ccxt/static_dependencies/lark/common.py +86 -0
  565. ccxt/static_dependencies/lark/exceptions.py +292 -0
  566. ccxt/static_dependencies/lark/grammar.py +130 -0
  567. ccxt/static_dependencies/lark/grammars/__init__.py +0 -0
  568. ccxt/static_dependencies/lark/indenter.py +143 -0
  569. ccxt/static_dependencies/lark/lark.py +658 -0
  570. ccxt/static_dependencies/lark/lexer.py +678 -0
  571. ccxt/static_dependencies/lark/load_grammar.py +1428 -0
  572. ccxt/static_dependencies/lark/parse_tree_builder.py +391 -0
  573. ccxt/static_dependencies/lark/parser_frontends.py +257 -0
  574. ccxt/static_dependencies/lark/parsers/__init__.py +0 -0
  575. ccxt/static_dependencies/lark/parsers/cyk.py +340 -0
  576. ccxt/static_dependencies/lark/parsers/earley.py +314 -0
  577. ccxt/static_dependencies/lark/parsers/earley_common.py +42 -0
  578. ccxt/static_dependencies/lark/parsers/earley_forest.py +801 -0
  579. ccxt/static_dependencies/lark/parsers/grammar_analysis.py +203 -0
  580. ccxt/static_dependencies/lark/parsers/lalr_analysis.py +332 -0
  581. ccxt/static_dependencies/lark/parsers/lalr_interactive_parser.py +158 -0
  582. ccxt/static_dependencies/lark/parsers/lalr_parser.py +122 -0
  583. ccxt/static_dependencies/lark/parsers/lalr_parser_state.py +110 -0
  584. ccxt/static_dependencies/lark/parsers/xearley.py +165 -0
  585. ccxt/static_dependencies/lark/reconstruct.py +107 -0
  586. ccxt/static_dependencies/lark/tools/__init__.py +70 -0
  587. ccxt/static_dependencies/lark/tools/nearley.py +202 -0
  588. ccxt/static_dependencies/lark/tools/serialize.py +32 -0
  589. ccxt/static_dependencies/lark/tools/standalone.py +196 -0
  590. ccxt/static_dependencies/lark/tree.py +267 -0
  591. ccxt/static_dependencies/lark/tree_matcher.py +186 -0
  592. ccxt/static_dependencies/lark/tree_templates.py +180 -0
  593. ccxt/static_dependencies/lark/utils.py +343 -0
  594. ccxt/static_dependencies/lark/visitors.py +596 -0
  595. ccxt/static_dependencies/marshmallow/__init__.py +81 -0
  596. ccxt/static_dependencies/marshmallow/base.py +65 -0
  597. ccxt/static_dependencies/marshmallow/class_registry.py +94 -0
  598. ccxt/static_dependencies/marshmallow/decorators.py +231 -0
  599. ccxt/static_dependencies/marshmallow/error_store.py +60 -0
  600. ccxt/static_dependencies/marshmallow/exceptions.py +71 -0
  601. ccxt/static_dependencies/marshmallow/fields.py +2114 -0
  602. ccxt/static_dependencies/marshmallow/orderedset.py +89 -0
  603. ccxt/static_dependencies/marshmallow/schema.py +1228 -0
  604. ccxt/static_dependencies/marshmallow/types.py +12 -0
  605. ccxt/static_dependencies/marshmallow/utils.py +378 -0
  606. ccxt/static_dependencies/marshmallow/validate.py +678 -0
  607. ccxt/static_dependencies/marshmallow/warnings.py +2 -0
  608. ccxt/static_dependencies/marshmallow_dataclass/__init__.py +1047 -0
  609. ccxt/static_dependencies/marshmallow_dataclass/collection_field.py +51 -0
  610. ccxt/static_dependencies/marshmallow_dataclass/lazy_class_attribute.py +45 -0
  611. ccxt/static_dependencies/marshmallow_dataclass/mypy.py +71 -0
  612. ccxt/static_dependencies/marshmallow_dataclass/typing.py +14 -0
  613. ccxt/static_dependencies/marshmallow_dataclass/union_field.py +82 -0
  614. ccxt/static_dependencies/marshmallow_oneofschema/__init__.py +1 -0
  615. ccxt/static_dependencies/marshmallow_oneofschema/one_of_schema.py +193 -0
  616. ccxt/static_dependencies/msgpack/__init__.py +55 -0
  617. ccxt/static_dependencies/msgpack/exceptions.py +48 -0
  618. ccxt/static_dependencies/msgpack/ext.py +168 -0
  619. ccxt/static_dependencies/msgpack/fallback.py +951 -0
  620. ccxt/static_dependencies/parsimonious/__init__.py +10 -0
  621. ccxt/static_dependencies/parsimonious/exceptions.py +105 -0
  622. ccxt/static_dependencies/parsimonious/expressions.py +479 -0
  623. ccxt/static_dependencies/parsimonious/grammar.py +487 -0
  624. ccxt/static_dependencies/parsimonious/nodes.py +325 -0
  625. ccxt/static_dependencies/parsimonious/utils.py +40 -0
  626. ccxt/static_dependencies/starknet/__init__.py +0 -0
  627. ccxt/static_dependencies/starknet/cairo/__init__.py +0 -0
  628. ccxt/static_dependencies/starknet/cairo/data_types.py +123 -0
  629. ccxt/static_dependencies/starknet/cairo/deprecated_parse/__init__.py +0 -0
  630. ccxt/static_dependencies/starknet/cairo/deprecated_parse/cairo_types.py +77 -0
  631. ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser.py +46 -0
  632. ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser_transformer.py +138 -0
  633. ccxt/static_dependencies/starknet/cairo/felt.py +64 -0
  634. ccxt/static_dependencies/starknet/cairo/type_parser.py +121 -0
  635. ccxt/static_dependencies/starknet/cairo/v1/__init__.py +0 -0
  636. ccxt/static_dependencies/starknet/cairo/v1/type_parser.py +59 -0
  637. ccxt/static_dependencies/starknet/cairo/v2/__init__.py +0 -0
  638. ccxt/static_dependencies/starknet/cairo/v2/type_parser.py +77 -0
  639. ccxt/static_dependencies/starknet/ccxt_utils.py +7 -0
  640. ccxt/static_dependencies/starknet/common.py +15 -0
  641. ccxt/static_dependencies/starknet/constants.py +39 -0
  642. ccxt/static_dependencies/starknet/hash/__init__.py +0 -0
  643. ccxt/static_dependencies/starknet/hash/address.py +79 -0
  644. ccxt/static_dependencies/starknet/hash/compiled_class_hash_objects.py +111 -0
  645. ccxt/static_dependencies/starknet/hash/selector.py +16 -0
  646. ccxt/static_dependencies/starknet/hash/storage.py +12 -0
  647. ccxt/static_dependencies/starknet/hash/utils.py +78 -0
  648. ccxt/static_dependencies/starknet/models/__init__.py +0 -0
  649. ccxt/static_dependencies/starknet/models/typed_data.py +45 -0
  650. ccxt/static_dependencies/starknet/serialization/__init__.py +24 -0
  651. ccxt/static_dependencies/starknet/serialization/_calldata_reader.py +40 -0
  652. ccxt/static_dependencies/starknet/serialization/_context.py +142 -0
  653. ccxt/static_dependencies/starknet/serialization/data_serializers/__init__.py +10 -0
  654. ccxt/static_dependencies/starknet/serialization/data_serializers/_common.py +82 -0
  655. ccxt/static_dependencies/starknet/serialization/data_serializers/array_serializer.py +43 -0
  656. ccxt/static_dependencies/starknet/serialization/data_serializers/bool_serializer.py +37 -0
  657. ccxt/static_dependencies/starknet/serialization/data_serializers/byte_array_serializer.py +66 -0
  658. ccxt/static_dependencies/starknet/serialization/data_serializers/cairo_data_serializer.py +71 -0
  659. ccxt/static_dependencies/starknet/serialization/data_serializers/enum_serializer.py +71 -0
  660. ccxt/static_dependencies/starknet/serialization/data_serializers/felt_serializer.py +50 -0
  661. ccxt/static_dependencies/starknet/serialization/data_serializers/named_tuple_serializer.py +58 -0
  662. ccxt/static_dependencies/starknet/serialization/data_serializers/option_serializer.py +43 -0
  663. ccxt/static_dependencies/starknet/serialization/data_serializers/output_serializer.py +40 -0
  664. ccxt/static_dependencies/starknet/serialization/data_serializers/payload_serializer.py +72 -0
  665. ccxt/static_dependencies/starknet/serialization/data_serializers/struct_serializer.py +36 -0
  666. ccxt/static_dependencies/starknet/serialization/data_serializers/tuple_serializer.py +36 -0
  667. ccxt/static_dependencies/starknet/serialization/data_serializers/uint256_serializer.py +76 -0
  668. ccxt/static_dependencies/starknet/serialization/data_serializers/uint_serializer.py +100 -0
  669. ccxt/static_dependencies/starknet/serialization/data_serializers/unit_serializer.py +32 -0
  670. ccxt/static_dependencies/starknet/serialization/errors.py +10 -0
  671. ccxt/static_dependencies/starknet/serialization/factory.py +229 -0
  672. ccxt/static_dependencies/starknet/serialization/function_serialization_adapter.py +110 -0
  673. ccxt/static_dependencies/starknet/serialization/tuple_dataclass.py +59 -0
  674. ccxt/static_dependencies/starknet/utils/__init__.py +0 -0
  675. ccxt/static_dependencies/starknet/utils/constructor_args_translator.py +86 -0
  676. ccxt/static_dependencies/starknet/utils/iterable.py +13 -0
  677. ccxt/static_dependencies/starknet/utils/schema.py +13 -0
  678. ccxt/static_dependencies/starknet/utils/typed_data.py +182 -0
  679. ccxt/static_dependencies/starkware/__init__.py +0 -0
  680. ccxt/static_dependencies/starkware/crypto/__init__.py +0 -0
  681. ccxt/static_dependencies/starkware/crypto/fast_pedersen_hash.py +50 -0
  682. ccxt/static_dependencies/starkware/crypto/math_utils.py +78 -0
  683. ccxt/static_dependencies/starkware/crypto/signature.py +2344 -0
  684. ccxt/static_dependencies/starkware/crypto/utils.py +63 -0
  685. ccxt/static_dependencies/sympy/__init__.py +0 -0
  686. ccxt/static_dependencies/sympy/core/__init__.py +0 -0
  687. ccxt/static_dependencies/sympy/core/intfunc.py +35 -0
  688. ccxt/static_dependencies/sympy/external/__init__.py +0 -0
  689. ccxt/static_dependencies/sympy/external/gmpy.py +345 -0
  690. ccxt/static_dependencies/sympy/external/importtools.py +187 -0
  691. ccxt/static_dependencies/sympy/external/ntheory.py +637 -0
  692. ccxt/static_dependencies/sympy/external/pythonmpq.py +341 -0
  693. ccxt/static_dependencies/toolz/__init__.py +26 -0
  694. ccxt/static_dependencies/toolz/_signatures.py +784 -0
  695. ccxt/static_dependencies/toolz/_version.py +520 -0
  696. ccxt/static_dependencies/toolz/compatibility.py +30 -0
  697. ccxt/static_dependencies/toolz/curried/__init__.py +101 -0
  698. ccxt/static_dependencies/toolz/curried/exceptions.py +22 -0
  699. ccxt/static_dependencies/toolz/curried/operator.py +22 -0
  700. ccxt/static_dependencies/toolz/dicttoolz.py +339 -0
  701. ccxt/static_dependencies/toolz/functoolz.py +1049 -0
  702. ccxt/static_dependencies/toolz/itertoolz.py +1057 -0
  703. ccxt/static_dependencies/toolz/recipes.py +46 -0
  704. ccxt/static_dependencies/toolz/utils.py +9 -0
  705. ccxt/static_dependencies/typing_inspect/__init__.py +0 -0
  706. ccxt/static_dependencies/typing_inspect/typing_inspect.py +851 -0
  707. ccxt/tabdeal.py +364 -0
  708. ccxt/test/__init__.py +3 -0
  709. ccxt/test/base/__init__.py +29 -0
  710. ccxt/test/base/test_account.py +26 -0
  711. ccxt/test/base/test_balance.py +56 -0
  712. ccxt/test/base/test_borrow_interest.py +35 -0
  713. ccxt/test/base/test_borrow_rate.py +32 -0
  714. ccxt/test/base/test_calculate_fee.py +51 -0
  715. ccxt/test/base/test_crypto.py +127 -0
  716. ccxt/test/base/test_currency.py +76 -0
  717. ccxt/test/base/test_datetime.py +109 -0
  718. ccxt/test/base/test_decimal_to_precision.py +392 -0
  719. ccxt/test/base/test_deep_extend.py +68 -0
  720. ccxt/test/base/test_deposit_withdrawal.py +50 -0
  721. ccxt/test/base/test_exchange_datetime_functions.py +76 -0
  722. ccxt/test/base/test_funding_rate_history.py +29 -0
  723. ccxt/test/base/test_last_price.py +31 -0
  724. ccxt/test/base/test_ledger_entry.py +45 -0
  725. ccxt/test/base/test_ledger_item.py +48 -0
  726. ccxt/test/base/test_leverage_tier.py +33 -0
  727. ccxt/test/base/test_liquidation.py +50 -0
  728. ccxt/test/base/test_margin_mode.py +24 -0
  729. ccxt/test/base/test_margin_modification.py +35 -0
  730. ccxt/test/base/test_market.py +193 -0
  731. ccxt/test/base/test_number.py +411 -0
  732. ccxt/test/base/test_ohlcv.py +33 -0
  733. ccxt/test/base/test_open_interest.py +32 -0
  734. ccxt/test/base/test_order.py +64 -0
  735. ccxt/test/base/test_order_book.py +69 -0
  736. ccxt/test/base/test_position.py +60 -0
  737. ccxt/test/base/test_shared_methods.py +353 -0
  738. ccxt/test/base/test_status.py +24 -0
  739. ccxt/test/base/test_throttle.py +126 -0
  740. ccxt/test/base/test_ticker.py +92 -0
  741. ccxt/test/base/test_trade.py +47 -0
  742. ccxt/test/base/test_trading_fee.py +26 -0
  743. ccxt/test/base/test_transaction.py +39 -0
  744. ccxt/test/test_async.py +1649 -0
  745. ccxt/test/test_sync.py +1648 -0
  746. ccxt/test/tests_async.py +1558 -0
  747. ccxt/test/tests_helpers.py +287 -0
  748. ccxt/test/tests_init.py +39 -0
  749. ccxt/test/tests_sync.py +1555 -0
  750. ccxt/tetherland.py +349 -0
  751. ccxt/timex.py +1593 -0
  752. ccxt/tokocrypto.py +2405 -0
  753. ccxt/tradeogre.py +608 -0
  754. ccxt/twox.py +326 -0
  755. ccxt/ubitex.py +409 -0
  756. ccxt/upbit.py +1833 -0
  757. ccxt/vertex.py +2922 -0
  758. ccxt/wallex.py +445 -0
  759. ccxt/wavesexchange.py +2472 -0
  760. ccxt/wazirx.py +1224 -0
  761. ccxt/whitebit.py +2469 -0
  762. ccxt/woo.py +3114 -0
  763. ccxt/woofipro.py +2533 -0
  764. ccxt/xt.py +4453 -0
  765. ccxt/yobit.py +1283 -0
  766. ccxt/zaif.py +725 -0
  767. ccxt/zonda.py +1828 -0
  768. ccxt_ir-4.3.46.0.1.dist-info/LICENSE.txt +21 -0
  769. ccxt_ir-4.3.46.0.1.dist-info/METADATA +655 -0
  770. ccxt_ir-4.3.46.0.1.dist-info/RECORD +772 -0
  771. ccxt_ir-4.3.46.0.1.dist-info/WHEEL +6 -0
  772. ccxt_ir-4.3.46.0.1.dist-info/top_level.txt +1 -0
ccxt/pro/binance.py ADDED
@@ -0,0 +1,3488 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
4
+ # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
5
+
6
+ import ccxt.async_support
7
+ from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide, ArrayCacheByTimestamp
8
+ import hashlib
9
+ from ccxt.base.types import Balances, Int, Liquidation, Num, Order, OrderBook, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, Trade
10
+ from ccxt.async_support.base.ws.client import Client
11
+ from typing import List
12
+ from ccxt.base.errors import ArgumentsRequired
13
+ from ccxt.base.errors import BadRequest
14
+ from ccxt.base.errors import NotSupported
15
+ from ccxt.base.errors import InvalidNonce
16
+ from ccxt.base.precise import Precise
17
+
18
+
19
+ class binance(ccxt.async_support.binance):
20
+
21
+ def describe(self):
22
+ return self.deep_extend(super(binance, self).describe(), {
23
+ 'has': {
24
+ 'ws': True,
25
+ 'watchBalance': True,
26
+ 'watchLiquidations': True,
27
+ 'watchLiquidationsForSymbols': True,
28
+ 'watchMyLiquidations': True,
29
+ 'watchMyLiquidationsForSymbols': True,
30
+ 'watchBidsAsks': True,
31
+ 'watchMyTrades': True,
32
+ 'watchOHLCV': True,
33
+ 'watchOHLCVForSymbols': False,
34
+ 'watchOrderBook': True,
35
+ 'watchOrderBookForSymbols': True,
36
+ 'watchOrders': True,
37
+ 'watchOrdersForSymbols': True,
38
+ 'watchPositions': True,
39
+ 'watchTicker': True,
40
+ 'watchTickers': True,
41
+ 'watchTrades': True,
42
+ 'watchTradesForSymbols': True,
43
+ 'createOrderWs': True,
44
+ 'editOrderWs': True,
45
+ 'cancelOrderWs': True,
46
+ 'cancelOrdersWs': False,
47
+ 'cancelAllOrdersWs': True,
48
+ 'fetchBalanceWs': True,
49
+ 'fetchDepositsWs': False,
50
+ 'fetchMarketsWs': False,
51
+ 'fetchMyTradesWs': True,
52
+ 'fetchOHLCVWs': True,
53
+ 'fetchOrderBookWs': True,
54
+ 'fetchOpenOrdersWs': True,
55
+ 'fetchOrderWs': True,
56
+ 'fetchOrdersWs': True,
57
+ 'fetchPositionWs': True,
58
+ 'fetchPositionForSymbolWs': True,
59
+ 'fetchPositionsWs': True,
60
+ 'fetchTickerWs': True,
61
+ 'fetchTradesWs': True,
62
+ 'fetchTradingFeesWs': False,
63
+ 'fetchWithdrawalsWs': False,
64
+ },
65
+ 'urls': {
66
+ 'test': {
67
+ 'ws': {
68
+ 'spot': 'wss://testnet.binance.vision/ws',
69
+ 'margin': 'wss://testnet.binance.vision/ws',
70
+ 'future': 'wss://fstream.binancefuture.com/ws',
71
+ 'delivery': 'wss://dstream.binancefuture.com/ws',
72
+ 'ws-api': {
73
+ 'spot': 'wss://testnet.binance.vision/ws-api/v3',
74
+ 'future': 'wss://testnet.binancefuture.com/ws-fapi/v1',
75
+ },
76
+ },
77
+ },
78
+ 'api': {
79
+ 'ws': {
80
+ 'spot': 'wss://stream.binance.com:9443/ws',
81
+ 'margin': 'wss://stream.binance.com:9443/ws',
82
+ 'future': 'wss://fstream.binance.com/ws',
83
+ 'delivery': 'wss://dstream.binance.com/ws',
84
+ 'ws-api': {
85
+ 'spot': 'wss://ws-api.binance.com:443/ws-api/v3',
86
+ 'future': 'wss://ws-fapi.binance.com/ws-fapi/v1',
87
+ },
88
+ 'papi': 'wss://fstream.binance.com/pm/ws',
89
+ },
90
+ },
91
+ },
92
+ 'streaming': {
93
+ 'keepAlive': 180000,
94
+ },
95
+ 'options': {
96
+ 'returnRateLimits': False,
97
+ 'streamLimits': {
98
+ 'spot': 50, # max 1024
99
+ 'margin': 50, # max 1024
100
+ 'future': 50, # max 200
101
+ 'delivery': 50, # max 200
102
+ },
103
+ 'subscriptionLimitByStream': {
104
+ 'spot': 200,
105
+ 'margin': 200,
106
+ 'future': 200,
107
+ 'delivery': 200,
108
+ },
109
+ 'streamBySubscriptionsHash': self.create_safe_dictionary(),
110
+ 'streamIndex': -1,
111
+ # get updates every 1000ms or 100ms
112
+ # or every 0ms in real-time for futures
113
+ 'watchOrderBookRate': 100,
114
+ 'liquidationsLimit': 1000,
115
+ 'myLiquidationsLimit': 1000,
116
+ 'tradesLimit': 1000,
117
+ 'ordersLimit': 1000,
118
+ 'OHLCVLimit': 1000,
119
+ 'requestId': self.create_safe_dictionary(),
120
+ 'watchOrderBookLimit': 1000, # default limit
121
+ 'watchTrades': {
122
+ 'name': 'trade', # 'trade' or 'aggTrade'
123
+ },
124
+ 'watchTicker': {
125
+ 'name': 'ticker', # ticker or miniTicker or ticker_<window_size>
126
+ },
127
+ 'watchTickers': {
128
+ 'name': 'ticker', # ticker or miniTicker or ticker_<window_size>
129
+ },
130
+ 'watchOHLCV': {
131
+ 'name': 'kline', # or indexPriceKline or markPriceKline(coin-m futures)
132
+ },
133
+ 'watchOrderBook': {
134
+ 'maxRetries': 3,
135
+ },
136
+ 'watchBalance': {
137
+ 'fetchBalanceSnapshot': False, # or True
138
+ 'awaitBalanceSnapshot': True, # whether to wait for the balance snapshot before providing updates
139
+ },
140
+ 'watchLiquidationsForSymbols': {
141
+ 'defaultType': 'swap',
142
+ },
143
+ 'watchPositions': {
144
+ 'fetchPositionsSnapshot': True, # or False
145
+ 'awaitPositionsSnapshot': True, # whether to wait for the positions snapshot before providing updates
146
+ },
147
+ 'wallet': 'wb', # wb = wallet balance, cw = cross balance
148
+ 'listenKeyRefreshRate': 1200000, # 20 mins
149
+ 'ws': {
150
+ 'cost': 5,
151
+ },
152
+ 'tickerChannelsMap': {
153
+ '24hrTicker': 'ticker',
154
+ '24hrMiniTicker': 'miniTicker',
155
+ # rolling window tickers
156
+ '1hTicker': 'ticker_1h',
157
+ '4hTicker': 'ticker_4h',
158
+ '1dTicker': 'ticker_1d',
159
+ 'bookTicker': 'bookTicker',
160
+ },
161
+ },
162
+ })
163
+
164
+ def request_id(self, url):
165
+ options = self.safe_dict(self.options, 'requestId', self.create_safe_dictionary())
166
+ previousValue = self.safe_integer(options, url, 0)
167
+ newValue = self.sum(previousValue, 1)
168
+ self.options['requestId'][url] = newValue
169
+ return newValue
170
+
171
+ def stream(self, type: Str, subscriptionHash: Str, numSubscriptions=1):
172
+ streamBySubscriptionsHash = self.safe_dict(self.options, 'streamBySubscriptionsHash', self.create_safe_dictionary())
173
+ stream = self.safe_string(streamBySubscriptionsHash, subscriptionHash)
174
+ if stream is None:
175
+ streamIndex = self.safe_integer(self.options, 'streamIndex', -1)
176
+ streamLimits = self.safe_value(self.options, 'streamLimits')
177
+ streamLimit = self.safe_integer(streamLimits, type)
178
+ streamIndex = streamIndex + 1
179
+ normalizedIndex = streamIndex % streamLimit
180
+ self.options['streamIndex'] = streamIndex
181
+ stream = self.number_to_string(normalizedIndex)
182
+ self.options['streamBySubscriptionsHash'][subscriptionHash] = stream
183
+ subscriptionsByStreams = self.safe_value(self.options, 'numSubscriptionsByStream')
184
+ if subscriptionsByStreams is None:
185
+ self.options['numSubscriptionsByStream'] = self.create_safe_dictionary()
186
+ subscriptionsByStream = self.safe_integer(self.options['numSubscriptionsByStream'], stream, 0)
187
+ newNumSubscriptions = subscriptionsByStream + numSubscriptions
188
+ subscriptionLimitByStream = self.safe_integer(self.options['subscriptionLimitByStream'], type, 200)
189
+ if newNumSubscriptions > subscriptionLimitByStream:
190
+ raise BadRequest(self.id + ' reached the limit of subscriptions by stream. Increase the number of streams, or increase the stream limit or subscription limit by stream if the exchange allows.')
191
+ self.options['numSubscriptionsByStream'][stream] = subscriptionsByStream + numSubscriptions
192
+ return stream
193
+
194
+ async def watch_liquidations(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
195
+ """
196
+ watch the public liquidations of a trading pair
197
+ :see: https://binance-docs.github.io/apidocs/futures/en/#liquidation-order-streams
198
+ :see: https://binance-docs.github.io/apidocs/delivery/en/#liquidation-order-streams
199
+ :param str symbol: unified CCXT market symbol
200
+ :param int [since]: the earliest time in ms to fetch liquidations for
201
+ :param int [limit]: the maximum number of liquidation structures to retrieve
202
+ :param dict [params]: exchange specific parameters for the bitmex api endpoint
203
+ :returns dict: an array of `liquidation structures <https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure>`
204
+ """
205
+ return self.watch_liquidations_for_symbols([symbol], since, limit, params)
206
+
207
+ async def watch_liquidations_for_symbols(self, symbols: List[str] = None, since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
208
+ """
209
+ watch the public liquidations of a trading pair
210
+ :see: https://binance-docs.github.io/apidocs/futures/en/#all-market-liquidation-order-streams
211
+ :see: https://binance-docs.github.io/apidocs/delivery/en/#all-market-liquidation-order-streams
212
+ :param str symbol: unified CCXT market symbol
213
+ :param int [since]: the earliest time in ms to fetch liquidations for
214
+ :param int [limit]: the maximum number of liquidation structures to retrieve
215
+ :param dict [params]: exchange specific parameters for the bitmex api endpoint
216
+ :returns dict: an array of `liquidation structures <https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure>`
217
+ """
218
+ await self.load_markets()
219
+ subscriptionHashes = []
220
+ messageHashes = []
221
+ streamHash = 'liquidations'
222
+ symbols = self.market_symbols(symbols, None, True, True)
223
+ if self.is_empty(symbols):
224
+ subscriptionHashes.append('!' + 'forceOrder@arr')
225
+ messageHashes.append('liquidations')
226
+ else:
227
+ for i in range(0, len(symbols)):
228
+ market = self.market(symbols[i])
229
+ subscriptionHashes.append(market['id'] + '@forceOrder')
230
+ messageHashes.append('liquidations::' + symbols[i])
231
+ streamHash += '::' + ','.join(symbols)
232
+ firstMarket = self.get_market_from_symbols(symbols)
233
+ type = None
234
+ type, params = self.handle_market_type_and_params('watchLiquidationsForSymbols', firstMarket, params)
235
+ if type == 'spot':
236
+ raise BadRequest(self.id + 'watchLiquidationsForSymbols is not supported for swap symbols')
237
+ subType = None
238
+ subType, params = self.handle_sub_type_and_params('watchLiquidationsForSymbols', firstMarket, params)
239
+ if self.isLinear(type, subType):
240
+ type = 'future'
241
+ elif self.isInverse(type, subType):
242
+ type = 'delivery'
243
+ numSubscriptions = len(subscriptionHashes)
244
+ url = self.urls['api']['ws'][type] + '/' + self.stream(type, streamHash, numSubscriptions)
245
+ requestId = self.request_id(url)
246
+ request = {
247
+ 'method': 'SUBSCRIBE',
248
+ 'params': subscriptionHashes,
249
+ 'id': requestId,
250
+ }
251
+ subscribe = {
252
+ 'id': requestId,
253
+ }
254
+ newLiquidations = await self.watch_multiple(url, messageHashes, self.extend(request, params), subscriptionHashes, subscribe)
255
+ if self.newUpdates:
256
+ return newLiquidations
257
+ return self.filter_by_symbols_since_limit(self.liquidations, symbols, since, limit, True)
258
+
259
+ def handle_liquidation(self, client: Client, message):
260
+ #
261
+ # future
262
+ # {
263
+ # "e":"forceOrder",
264
+ # "E":1698871323061,
265
+ # "o":{
266
+ # "s":"BTCUSDT",
267
+ # "S":"BUY",
268
+ # "o":"LIMIT",
269
+ # "f":"IOC",
270
+ # "q":"1.437",
271
+ # "p":"35100.81",
272
+ # "ap":"34959.70",
273
+ # "X":"FILLED",
274
+ # "l":"1.437",
275
+ # "z":"1.437",
276
+ # "T":1698871323059
277
+ # }
278
+ # }
279
+ # delivery
280
+ # {
281
+ # "e":"forceOrder", # Event Type
282
+ # "E": 1591154240950, # Event Time
283
+ # "o":{
284
+ # "s":"BTCUSD_200925", # Symbol
285
+ # "ps": "BTCUSD", # Pair
286
+ # "S":"SELL", # Side
287
+ # "o":"LIMIT", # Order Type
288
+ # "f":"IOC", # Time in Force
289
+ # "q":"1", # Original Quantity
290
+ # "p":"9425.5", # Price
291
+ # "ap":"9496.5", # Average Price
292
+ # "X":"FILLED", # Order Status
293
+ # "l":"1", # Order Last Filled Quantity
294
+ # "z":"1", # Order Filled Accumulated Quantity
295
+ # "T": 1591154240949, # Order Trade Time
296
+ # }
297
+ # }
298
+ #
299
+ rawLiquidation = self.safe_value(message, 'o', {})
300
+ marketId = self.safe_string(rawLiquidation, 's')
301
+ market = self.safe_market(marketId, None, '', 'contract')
302
+ symbol = market['symbol']
303
+ liquidation = self.parse_ws_liquidation(rawLiquidation, market)
304
+ liquidations = self.safe_value(self.liquidations, symbol)
305
+ if liquidations is None:
306
+ limit = self.safe_integer(self.options, 'liquidationsLimit', 1000)
307
+ liquidations = ArrayCache(limit)
308
+ liquidations.append(liquidation)
309
+ self.liquidations[symbol] = liquidations
310
+ client.resolve([liquidation], 'liquidations')
311
+ client.resolve([liquidation], 'liquidations::' + symbol)
312
+
313
+ def parse_ws_liquidation(self, liquidation, market=None):
314
+ #
315
+ # future
316
+ # {
317
+ # "s":"BTCUSDT",
318
+ # "S":"BUY",
319
+ # "o":"LIMIT",
320
+ # "f":"IOC",
321
+ # "q":"1.437",
322
+ # "p":"35100.81",
323
+ # "ap":"34959.70",
324
+ # "X":"FILLED",
325
+ # "l":"1.437",
326
+ # "z":"1.437",
327
+ # "T":1698871323059
328
+ # }
329
+ # delivery
330
+ # {
331
+ # "s":"BTCUSD_200925", # Symbol
332
+ # "ps": "BTCUSD", # Pair
333
+ # "S":"SELL", # Side
334
+ # "o":"LIMIT", # Order Type
335
+ # "f":"IOC", # Time in Force
336
+ # "q":"1", # Original Quantity
337
+ # "p":"9425.5", # Price
338
+ # "ap":"9496.5", # Average Price
339
+ # "X":"FILLED", # Order Status
340
+ # "l":"1", # Order Last Filled Quantity
341
+ # "z":"1", # Order Filled Accumulated Quantity
342
+ # "T": 1591154240949, # Order Trade Time
343
+ # }
344
+ # myLiquidation
345
+ # {
346
+ # "s":"BTCUSDT", # Symbol
347
+ # "c":"TEST", # Client Order Id
348
+ # # special client order id:
349
+ # # starts with "autoclose-": liquidation order
350
+ # # "adl_autoclose": ADL auto close order
351
+ # # "settlement_autoclose-": settlement order for delisting or delivery
352
+ # "S":"SELL", # Side
353
+ # "o":"TRAILING_STOP_MARKET", # Order Type
354
+ # "f":"GTC", # Time in Force
355
+ # "q":"0.001", # Original Quantity
356
+ # "p":"0", # Original Price
357
+ # "ap":"0", # Average Price
358
+ # "sp":"7103.04", # Stop Price. Please ignore with TRAILING_STOP_MARKET order
359
+ # "x":"NEW", # Execution Type
360
+ # "X":"NEW", # Order Status
361
+ # "i":8886774, # Order Id
362
+ # "l":"0", # Order Last Filled Quantity
363
+ # "z":"0", # Order Filled Accumulated Quantity
364
+ # "L":"0", # Last Filled Price
365
+ # "N":"USDT", # Commission Asset, will not push if no commission
366
+ # "n":"0", # Commission, will not push if no commission
367
+ # "T":1568879465650, # Order Trade Time
368
+ # "t":0, # Trade Id
369
+ # "b":"0", # Bids Notional
370
+ # "a":"9.91", # Ask Notional
371
+ # "m":false, # Is self trade the maker side?
372
+ # "R":false, # Is self reduce only
373
+ # "wt":"CONTRACT_PRICE", # Stop Price Working Type
374
+ # "ot":"TRAILING_STOP_MARKET",// Original Order Type
375
+ # "ps":"LONG", # Position Side
376
+ # "cp":false, # If Close-All, pushed with conditional order
377
+ # "AP":"7476.89", # Activation Price, only puhed with TRAILING_STOP_MARKET order
378
+ # "cr":"5.0", # Callback Rate, only puhed with TRAILING_STOP_MARKET order
379
+ # "pP": False, # If price protection is turned on
380
+ # "si": 0, # ignore
381
+ # "ss": 0, # ignore
382
+ # "rp":"0", # Realized Profit of the trade
383
+ # "V":"EXPIRE_TAKER", # STP mode
384
+ # "pm":"OPPONENT", # Price match mode
385
+ # "gtd":0 # TIF GTD order auto cancel time
386
+ # }
387
+ #
388
+ marketId = self.safe_string(liquidation, 's')
389
+ market = self.safe_market(marketId, market)
390
+ timestamp = self.safe_integer(liquidation, 'T')
391
+ return self.safe_liquidation({
392
+ 'info': liquidation,
393
+ 'symbol': self.safe_symbol(marketId, market),
394
+ 'contracts': self.safe_number(liquidation, 'l'),
395
+ 'contractSize': self.safe_number(market, 'contractSize'),
396
+ 'price': self.safe_number(liquidation, 'ap'),
397
+ 'baseValue': None,
398
+ 'quoteValue': None,
399
+ 'timestamp': timestamp,
400
+ 'datetime': self.iso8601(timestamp),
401
+ })
402
+
403
+ async def watch_my_liquidations(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
404
+ """
405
+ watch the private liquidations of a trading pair
406
+ :see: https://binance-docs.github.io/apidocs/futures/en/#event-order-update
407
+ :see: https://binance-docs.github.io/apidocs/delivery/en/#event-order-update
408
+ :param str symbol: unified CCXT market symbol
409
+ :param int [since]: the earliest time in ms to fetch liquidations for
410
+ :param int [limit]: the maximum number of liquidation structures to retrieve
411
+ :param dict [params]: exchange specific parameters for the bitmex api endpoint
412
+ :returns dict: an array of `liquidation structures <https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure>`
413
+ """
414
+ return self.watch_my_liquidations_for_symbols([symbol], since, limit, params)
415
+
416
+ async def watch_my_liquidations_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
417
+ """
418
+ watch the private liquidations of a trading pair
419
+ :see: https://binance-docs.github.io/apidocs/futures/en/#event-order-update
420
+ :see: https://binance-docs.github.io/apidocs/delivery/en/#event-order-update
421
+ :param str symbol: unified CCXT market symbol
422
+ :param int [since]: the earliest time in ms to fetch liquidations for
423
+ :param int [limit]: the maximum number of liquidation structures to retrieve
424
+ :param dict [params]: exchange specific parameters for the bitmex api endpoint
425
+ :returns dict: an array of `liquidation structures <https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure>`
426
+ """
427
+ await self.load_markets()
428
+ symbols = self.market_symbols(symbols, None, True, True, True)
429
+ market = self.get_market_from_symbols(symbols)
430
+ messageHashes = ['myLiquidations']
431
+ if not self.is_empty(symbols):
432
+ for i in range(0, len(symbols)):
433
+ symbol = symbols[i]
434
+ messageHashes.append('myLiquidations::' + symbol)
435
+ type = None
436
+ type, params = self.handle_market_type_and_params('watchMyLiquidationsForSymbols', market, params)
437
+ subType = None
438
+ subType, params = self.handle_sub_type_and_params('watchMyLiquidationsForSymbols', market, params)
439
+ if self.isLinear(type, subType):
440
+ type = 'future'
441
+ elif self.isInverse(type, subType):
442
+ type = 'delivery'
443
+ await self.authenticate(params)
444
+ url = self.urls['api']['ws'][type] + '/' + self.options[type]['listenKey']
445
+ message = None
446
+ newLiquidations = await self.watch_multiple(url, messageHashes, message, [type])
447
+ if self.newUpdates:
448
+ return newLiquidations
449
+ return self.filter_by_symbols_since_limit(self.liquidations, symbols, since, limit)
450
+
451
+ def handle_my_liquidation(self, client: Client, message):
452
+ #
453
+ # {
454
+ # "s":"BTCUSDT", # Symbol
455
+ # "c":"TEST", # Client Order Id
456
+ # # special client order id:
457
+ # # starts with "autoclose-": liquidation order
458
+ # # "adl_autoclose": ADL auto close order
459
+ # # "settlement_autoclose-": settlement order for delisting or delivery
460
+ # "S":"SELL", # Side
461
+ # "o":"TRAILING_STOP_MARKET", # Order Type
462
+ # "f":"GTC", # Time in Force
463
+ # "q":"0.001", # Original Quantity
464
+ # "p":"0", # Original Price
465
+ # "ap":"0", # Average Price
466
+ # "sp":"7103.04", # Stop Price. Please ignore with TRAILING_STOP_MARKET order
467
+ # "x":"NEW", # Execution Type
468
+ # "X":"NEW", # Order Status
469
+ # "i":8886774, # Order Id
470
+ # "l":"0", # Order Last Filled Quantity
471
+ # "z":"0", # Order Filled Accumulated Quantity
472
+ # "L":"0", # Last Filled Price
473
+ # "N":"USDT", # Commission Asset, will not push if no commission
474
+ # "n":"0", # Commission, will not push if no commission
475
+ # "T":1568879465650, # Order Trade Time
476
+ # "t":0, # Trade Id
477
+ # "b":"0", # Bids Notional
478
+ # "a":"9.91", # Ask Notional
479
+ # "m":false, # Is self trade the maker side?
480
+ # "R":false, # Is self reduce only
481
+ # "wt":"CONTRACT_PRICE", # Stop Price Working Type
482
+ # "ot":"TRAILING_STOP_MARKET",// Original Order Type
483
+ # "ps":"LONG", # Position Side
484
+ # "cp":false, # If Close-All, pushed with conditional order
485
+ # "AP":"7476.89", # Activation Price, only puhed with TRAILING_STOP_MARKET order
486
+ # "cr":"5.0", # Callback Rate, only puhed with TRAILING_STOP_MARKET order
487
+ # "pP": False, # If price protection is turned on
488
+ # "si": 0, # ignore
489
+ # "ss": 0, # ignore
490
+ # "rp":"0", # Realized Profit of the trade
491
+ # "V":"EXPIRE_TAKER", # STP mode
492
+ # "pm":"OPPONENT", # Price match mode
493
+ # "gtd":0 # TIF GTD order auto cancel time
494
+ # }
495
+ #
496
+ orderType = self.safe_string(message, 'o')
497
+ if orderType != 'LIQUIDATION':
498
+ return
499
+ marketId = self.safe_string(message, 's')
500
+ market = self.safe_market(marketId)
501
+ symbol = self.safe_symbol(marketId)
502
+ liquidation = self.parse_ws_liquidation(message, market)
503
+ myLiquidations = self.safe_value(self.myLiquidations, symbol)
504
+ if myLiquidations is None:
505
+ limit = self.safe_integer(self.options, 'myLiquidationsLimit', 1000)
506
+ myLiquidations = ArrayCache(limit)
507
+ myLiquidations.append(liquidation)
508
+ self.myLiquidations[symbol] = myLiquidations
509
+ client.resolve([liquidation], 'myLiquidations')
510
+ client.resolve([liquidation], 'myLiquidations::' + symbol)
511
+
512
+ async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
513
+ """
514
+ watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
515
+ :param str symbol: unified symbol of the market to fetch the order book for
516
+ :param int [limit]: the maximum amount of order book entries to return
517
+ :param dict [params]: extra parameters specific to the exchange API endpoint
518
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
519
+ """
520
+ #
521
+ # todo add support for <levels>-snapshots(depth)
522
+ # https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md#partial-book-depth-streams # <symbol>@depth<levels>@100ms or <symbol>@depth<levels>(1000ms)
523
+ # valid <levels> are 5, 10, or 20
524
+ #
525
+ # default 100, max 1000, valid limits 5, 10, 20, 50, 100, 500, 1000
526
+ #
527
+ # notice the differences between trading futures and spot trading
528
+ # the algorithms use different urls in step 1
529
+ # delta caching and merging also differs in steps 4, 5, 6
530
+ #
531
+ # spot/margin
532
+ # https://binance-docs.github.io/apidocs/spot/en/#how-to-manage-a-local-order-book-correctly
533
+ #
534
+ # 1. Open a stream to wss://stream.binance.com:9443/ws/bnbbtc@depth.
535
+ # 2. Buffer the events you receive from the stream.
536
+ # 3. Get a depth snapshot from https://www.binance.com/api/v1/depth?symbol=BNBBTC&limit=1000 .
537
+ # 4. Drop any event where u is <= lastUpdateId in the snapshot.
538
+ # 5. The first processed event should have U <= lastUpdateId+1 AND u >= lastUpdateId+1.
539
+ # 6. While listening to the stream, each new event's U should be equal to the previous event's u+1.
540
+ # 7. The data in each event is the absolute quantity for a price level.
541
+ # 8. If the quantity is 0, remove the price level.
542
+ # 9. Receiving an event that removes a price level that is not in your local order book can happen and is normal.
543
+ #
544
+ # futures
545
+ # https://binance-docs.github.io/apidocs/futures/en/#how-to-manage-a-local-order-book-correctly
546
+ #
547
+ # 1. Open a stream to wss://fstream.binance.com/stream?streams=btcusdt@depth.
548
+ # 2. Buffer the events you receive from the stream. For same price, latest received update covers the previous one.
549
+ # 3. Get a depth snapshot from https://fapi.binance.com/fapi/v1/depth?symbol=BTCUSDT&limit=1000 .
550
+ # 4. Drop any event where u is < lastUpdateId in the snapshot.
551
+ # 5. The first processed event should have U <= lastUpdateId AND u >= lastUpdateId
552
+ # 6. While listening to the stream, each new event's pu should be equal to the previous event's u, otherwise initialize the process from step 3.
553
+ # 7. The data in each event is the absolute quantity for a price level.
554
+ # 8. If the quantity is 0, remove the price level.
555
+ # 9. Receiving an event that removes a price level that is not in your local order book can happen and is normal.
556
+ #
557
+ return await self.watch_order_book_for_symbols([symbol], limit, params)
558
+
559
+ async def watch_order_book_for_symbols(self, symbols: List[str], limit: Int = None, params={}) -> OrderBook:
560
+ """
561
+ watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
562
+ :param str[] symbols: unified array of symbols
563
+ :param int [limit]: the maximum amount of order book entries to return
564
+ :param dict [params]: extra parameters specific to the exchange API endpoint
565
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
566
+ """
567
+ await self.load_markets()
568
+ symbols = self.market_symbols(symbols, None, False, True, True)
569
+ firstMarket = self.market(symbols[0])
570
+ type = firstMarket['type']
571
+ if firstMarket['contract']:
572
+ type = 'future' if firstMarket['linear'] else 'delivery'
573
+ name = 'depth'
574
+ streamHash = 'multipleOrderbook'
575
+ if symbols is not None:
576
+ symbolsLength = len(symbols)
577
+ if symbolsLength > 200:
578
+ raise BadRequest(self.id + ' watchOrderBookForSymbols() accepts 200 symbols at most. To watch more symbols call watchOrderBookForSymbols() multiple times')
579
+ streamHash += '::' + ','.join(symbols)
580
+ watchOrderBookRate = self.safe_string(self.options, 'watchOrderBookRate', '100')
581
+ subParams = []
582
+ messageHashes = []
583
+ for i in range(0, len(symbols)):
584
+ symbol = symbols[i]
585
+ market = self.market(symbol)
586
+ messageHash = market['lowercaseId'] + '@' + name
587
+ messageHashes.append(messageHash)
588
+ symbolHash = messageHash + '@' + watchOrderBookRate + 'ms'
589
+ subParams.append(symbolHash)
590
+ messageHashesLength = len(messageHashes)
591
+ url = self.urls['api']['ws'][type] + '/' + self.stream(type, streamHash, messageHashesLength)
592
+ requestId = self.request_id(url)
593
+ request: dict = {
594
+ 'method': 'SUBSCRIBE',
595
+ 'params': subParams,
596
+ 'id': requestId,
597
+ }
598
+ subscription: dict = {
599
+ 'id': str(requestId),
600
+ 'name': name,
601
+ 'symbols': symbols,
602
+ 'method': self.handle_order_book_subscription,
603
+ 'limit': limit,
604
+ 'type': type,
605
+ 'params': params,
606
+ }
607
+ message = self.extend(request, params)
608
+ orderbook = await self.watch_multiple(url, messageHashes, message, messageHashes, subscription)
609
+ return orderbook.limit()
610
+
611
+ async def fetch_order_book_ws(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
612
+ """
613
+ fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
614
+ :see: https://binance-docs.github.io/apidocs/futures/en/#order-book-2
615
+ :param str symbol: unified symbol of the market to fetch the order book for
616
+ :param int [limit]: the maximum amount of order book entries to return
617
+ :param dict [params]: extra parameters specific to the exchange API endpoint
618
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
619
+ """
620
+ await self.load_markets()
621
+ market = self.market(symbol)
622
+ payload: dict = {
623
+ 'symbol': market['id'],
624
+ }
625
+ if limit is not None:
626
+ payload['limit'] = limit
627
+ marketType = self.get_market_type('fetchOrderBookWs', market, params)
628
+ if marketType != 'future':
629
+ raise BadRequest(self.id + ' fetchOrderBookWs only supports swap markets')
630
+ url = self.urls['api']['ws']['ws-api'][marketType]
631
+ requestId = self.request_id(url)
632
+ messageHash = str(requestId)
633
+ returnRateLimits = False
634
+ returnRateLimits, params = self.handle_option_and_params(params, 'createOrderWs', 'returnRateLimits', False)
635
+ payload['returnRateLimits'] = returnRateLimits
636
+ params = self.omit(params, 'test')
637
+ message: dict = {
638
+ 'id': messageHash,
639
+ 'method': 'depth',
640
+ 'params': self.sign_params(self.extend(payload, params)),
641
+ }
642
+ subscription: dict = {
643
+ 'method': self.handle_fetch_order_book,
644
+ }
645
+ orderbook = await self.watch(url, messageHash, message, messageHash, subscription)
646
+ orderbook['symbol'] = market['symbol']
647
+ return orderbook
648
+
649
+ def handle_fetch_order_book(self, client: Client, message):
650
+ #
651
+ # {
652
+ # "id":"51e2affb-0aba-4821-ba75-f2625006eb43",
653
+ # "status":200,
654
+ # "result":{
655
+ # "lastUpdateId":1027024,
656
+ # "E":1589436922972,
657
+ # "T":1589436922959,
658
+ # "bids":[
659
+ # [
660
+ # "4.00000000",
661
+ # "431.00000000"
662
+ # ]
663
+ # ],
664
+ # "asks":[
665
+ # [
666
+ # "4.00000200",
667
+ # "12.00000000"
668
+ # ]
669
+ # ]
670
+ # }
671
+ # }
672
+ #
673
+ messageHash = self.safe_string(message, 'id')
674
+ result = self.safe_dict(message, 'result')
675
+ timestamp = self.safe_integer(result, 'T')
676
+ orderbook = self.parse_order_book(result, None, timestamp)
677
+ orderbook['nonce'] = self.safe_integer_2(result, 'lastUpdateId', 'u')
678
+ client.resolve(orderbook, messageHash)
679
+
680
+ async def fetch_order_book_snapshot(self, client, message, subscription):
681
+ name = self.safe_string(subscription, 'name')
682
+ symbol = self.safe_string(subscription, 'symbol')
683
+ market = self.market(symbol)
684
+ messageHash = market['lowercaseId'] + '@' + name
685
+ try:
686
+ defaultLimit = self.safe_integer(self.options, 'watchOrderBookLimit', 1000)
687
+ type = self.safe_value(subscription, 'type')
688
+ limit = self.safe_integer(subscription, 'limit', defaultLimit)
689
+ params = self.safe_value(subscription, 'params')
690
+ # 3. Get a depth snapshot from https://www.binance.com/api/v1/depth?symbol=BNBBTC&limit=1000 .
691
+ # todo: self is a synch blocking call - make it async
692
+ # default 100, max 1000, valid limits 5, 10, 20, 50, 100, 500, 1000
693
+ snapshot = await self.fetch_rest_order_book_safe(symbol, limit, params)
694
+ if self.safe_value(self.orderbooks, symbol) is None:
695
+ # if the orderbook is dropped before the snapshot is received
696
+ return
697
+ orderbook = self.orderbooks[symbol]
698
+ orderbook.reset(snapshot)
699
+ # unroll the accumulated deltas
700
+ messages = orderbook.cache
701
+ for i in range(0, len(messages)):
702
+ messageItem = messages[i]
703
+ U = self.safe_integer(messageItem, 'U')
704
+ u = self.safe_integer(messageItem, 'u')
705
+ pu = self.safe_integer(messageItem, 'pu')
706
+ if type == 'future':
707
+ # 4. Drop any event where u is < lastUpdateId in the snapshot
708
+ if u < orderbook['nonce']:
709
+ continue
710
+ # 5. The first processed event should have U <= lastUpdateId AND u >= lastUpdateId
711
+ if (U <= orderbook['nonce']) and (u >= orderbook['nonce']) or (pu == orderbook['nonce']):
712
+ self.handle_order_book_message(client, messageItem, orderbook)
713
+ else:
714
+ # 4. Drop any event where u is <= lastUpdateId in the snapshot
715
+ if u <= orderbook['nonce']:
716
+ continue
717
+ # 5. The first processed event should have U <= lastUpdateId+1 AND u >= lastUpdateId+1
718
+ if ((U - 1) <= orderbook['nonce']) and ((u - 1) >= orderbook['nonce']):
719
+ self.handle_order_book_message(client, messageItem, orderbook)
720
+ self.orderbooks[symbol] = orderbook
721
+ client.resolve(orderbook, messageHash)
722
+ except Exception as e:
723
+ del client.subscriptions[messageHash]
724
+ client.reject(e, messageHash)
725
+
726
+ def handle_delta(self, bookside, delta):
727
+ price = self.safe_float(delta, 0)
728
+ amount = self.safe_float(delta, 1)
729
+ bookside.store(price, amount)
730
+
731
+ def handle_deltas(self, bookside, deltas):
732
+ for i in range(0, len(deltas)):
733
+ self.handle_delta(bookside, deltas[i])
734
+
735
+ def handle_order_book_message(self, client: Client, message, orderbook):
736
+ u = self.safe_integer(message, 'u')
737
+ self.handle_deltas(orderbook['asks'], self.safe_value(message, 'a', []))
738
+ self.handle_deltas(orderbook['bids'], self.safe_value(message, 'b', []))
739
+ orderbook['nonce'] = u
740
+ timestamp = self.safe_integer(message, 'E')
741
+ orderbook['timestamp'] = timestamp
742
+ orderbook['datetime'] = self.iso8601(timestamp)
743
+ return orderbook
744
+
745
+ def handle_order_book(self, client: Client, message):
746
+ #
747
+ # initial snapshot is fetched with ccxt's fetchOrderBook
748
+ # the feed does not include a snapshot, just the deltas
749
+ #
750
+ # {
751
+ # "e": "depthUpdate", # Event type
752
+ # "E": 1577554482280, # Event time
753
+ # "s": "BNBBTC", # Symbol
754
+ # "U": 157, # First update ID in event
755
+ # "u": 160, # Final update ID in event
756
+ # "b": [ # bids
757
+ # ["0.0024", "10"], # price, size
758
+ # ],
759
+ # "a": [ # asks
760
+ # ["0.0026", "100"], # price, size
761
+ # ]
762
+ # }
763
+ #
764
+ isTestnetSpot = client.url.find('testnet') > 0
765
+ isSpotMainNet = client.url.find('/stream.binance.') > 0
766
+ isSpot = isTestnetSpot or isSpotMainNet
767
+ marketType = 'spot' if isSpot else 'contract'
768
+ marketId = self.safe_string(message, 's')
769
+ market = self.safe_market(marketId, None, None, marketType)
770
+ symbol = market['symbol']
771
+ name = 'depth'
772
+ messageHash = market['lowercaseId'] + '@' + name
773
+ if not (symbol in self.orderbooks):
774
+ #
775
+ # https://github.com/ccxt/ccxt/issues/6672
776
+ #
777
+ # Sometimes Binance sends the first delta before the subscription
778
+ # confirmation arrives. At that point the orderbook is not
779
+ # initialized yet and the snapshot has not been requested yet
780
+ # therefore it is safe to drop these premature messages.
781
+ #
782
+ return
783
+ orderbook = self.orderbooks[symbol]
784
+ nonce = self.safe_integer(orderbook, 'nonce')
785
+ if nonce is None:
786
+ # 2. Buffer the events you receive from the stream.
787
+ orderbook.cache.append(message)
788
+ else:
789
+ try:
790
+ U = self.safe_integer(message, 'U')
791
+ u = self.safe_integer(message, 'u')
792
+ pu = self.safe_integer(message, 'pu')
793
+ if pu is None:
794
+ # spot
795
+ # 4. Drop any event where u is <= lastUpdateId in the snapshot
796
+ if u > orderbook['nonce']:
797
+ timestamp = self.safe_integer(orderbook, 'timestamp')
798
+ conditional = None
799
+ if timestamp is None:
800
+ # 5. The first processed event should have U <= lastUpdateId+1 AND u >= lastUpdateId+1
801
+ conditional = ((U - 1) <= orderbook['nonce']) and ((u - 1) >= orderbook['nonce'])
802
+ else:
803
+ # 6. While listening to the stream, each new event's U should be equal to the previous event's u+1.
804
+ conditional = ((U - 1) == orderbook['nonce'])
805
+ if conditional:
806
+ self.handle_order_book_message(client, message, orderbook)
807
+ if nonce < orderbook['nonce']:
808
+ client.resolve(orderbook, messageHash)
809
+ else:
810
+ checksum = self.safe_bool(self.options, 'checksum', True)
811
+ if checksum:
812
+ # todo: client.reject from handleOrderBookMessage properly
813
+ raise InvalidNonce(self.id + ' handleOrderBook received an out-of-order nonce')
814
+ else:
815
+ # future
816
+ # 4. Drop any event where u is < lastUpdateId in the snapshot
817
+ if u >= orderbook['nonce']:
818
+ # 5. The first processed event should have U <= lastUpdateId AND u >= lastUpdateId
819
+ # 6. While listening to the stream, each new event's pu should be equal to the previous event's u, otherwise initialize the process from step 3
820
+ if (U <= orderbook['nonce']) or (pu == orderbook['nonce']):
821
+ self.handle_order_book_message(client, message, orderbook)
822
+ if nonce <= orderbook['nonce']:
823
+ client.resolve(orderbook, messageHash)
824
+ else:
825
+ checksum = self.safe_bool(self.options, 'checksum', True)
826
+ if checksum:
827
+ # todo: client.reject from handleOrderBookMessage properly
828
+ raise InvalidNonce(self.id + ' handleOrderBook received an out-of-order nonce')
829
+ except Exception as e:
830
+ del self.orderbooks[symbol]
831
+ del client.subscriptions[messageHash]
832
+ client.reject(e, messageHash)
833
+
834
+ def handle_order_book_subscription(self, client: Client, message, subscription):
835
+ defaultLimit = self.safe_integer(self.options, 'watchOrderBookLimit', 1000)
836
+ # messageHash = self.safe_string(subscription, 'messageHash')
837
+ symbolOfSubscription = self.safe_string(subscription, 'symbol') # watchOrderBook
838
+ symbols = self.safe_value(subscription, 'symbols', [symbolOfSubscription]) # watchOrderBookForSymbols
839
+ limit = self.safe_integer(subscription, 'limit', defaultLimit)
840
+ # handle list of symbols
841
+ for i in range(0, len(symbols)):
842
+ symbol = symbols[i]
843
+ if symbol in self.orderbooks:
844
+ del self.orderbooks[symbol]
845
+ self.orderbooks[symbol] = self.order_book({}, limit)
846
+ subscription = self.extend(subscription, {'symbol': symbol})
847
+ # fetch the snapshot in a separate async call
848
+ self.spawn(self.fetch_order_book_snapshot, client, message, subscription)
849
+
850
+ def handle_subscription_status(self, client: Client, message):
851
+ #
852
+ # {
853
+ # "result": null,
854
+ # "id": 1574649734450
855
+ # }
856
+ #
857
+ id = self.safe_string(message, 'id')
858
+ subscriptionsById = self.index_by(client.subscriptions, 'id')
859
+ subscription = self.safe_value(subscriptionsById, id, {})
860
+ method = self.safe_value(subscription, 'method')
861
+ if method is not None:
862
+ method(client, message, subscription)
863
+ return message
864
+
865
+ async def watch_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Trade]:
866
+ """
867
+ get the list of most recent trades for a list of symbols
868
+ :param str[] symbols: unified symbol of the market to fetch trades for
869
+ :param int [since]: timestamp in ms of the earliest trade to fetch
870
+ :param int [limit]: the maximum amount of trades to fetch
871
+ :param dict [params]: extra parameters specific to the exchange API endpoint
872
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
873
+ """
874
+ await self.load_markets()
875
+ symbols = self.market_symbols(symbols, None, False, True, True)
876
+ streamHash = 'multipleTrades'
877
+ if symbols is not None:
878
+ symbolsLength = len(symbols)
879
+ if symbolsLength > 200:
880
+ raise BadRequest(self.id + ' watchTradesForSymbols() accepts 200 symbols at most. To watch more symbols call watchTradesForSymbols() multiple times')
881
+ streamHash += '::' + ','.join(symbols)
882
+ options = self.safe_value(self.options, 'watchTradesForSymbols', {})
883
+ name = self.safe_string(options, 'name', 'trade')
884
+ firstMarket = self.market(symbols[0])
885
+ type = firstMarket['type']
886
+ if firstMarket['contract']:
887
+ type = 'future' if firstMarket['linear'] else 'delivery'
888
+ subParams = []
889
+ for i in range(0, len(symbols)):
890
+ symbol = symbols[i]
891
+ market = self.market(symbol)
892
+ currentMessageHash = market['lowercaseId'] + '@' + name
893
+ subParams.append(currentMessageHash)
894
+ query = self.omit(params, 'type')
895
+ subParamsLength = len(subParams)
896
+ url = self.urls['api']['ws'][type] + '/' + self.stream(type, streamHash, subParamsLength)
897
+ requestId = self.request_id(url)
898
+ request: dict = {
899
+ 'method': 'SUBSCRIBE',
900
+ 'params': subParams,
901
+ 'id': requestId,
902
+ }
903
+ subscribe: dict = {
904
+ 'id': requestId,
905
+ }
906
+ trades = await self.watch_multiple(url, subParams, self.extend(request, query), subParams, subscribe)
907
+ if self.newUpdates:
908
+ first = self.safe_value(trades, 0)
909
+ tradeSymbol = self.safe_string(first, 'symbol')
910
+ limit = trades.getLimit(tradeSymbol, limit)
911
+ return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
912
+
913
+ async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
914
+ """
915
+ get the list of most recent trades for a particular symbol
916
+ :param str symbol: unified symbol of the market to fetch trades for
917
+ :param int [since]: timestamp in ms of the earliest trade to fetch
918
+ :param int [limit]: the maximum amount of trades to fetch
919
+ :param dict [params]: extra parameters specific to the exchange API endpoint
920
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
921
+ """
922
+ return await self.watch_trades_for_symbols([symbol], since, limit, params)
923
+
924
+ def parse_ws_trade(self, trade, market=None) -> Trade:
925
+ #
926
+ # public watchTrades
927
+ #
928
+ # {
929
+ # "e": "trade", # event type
930
+ # "E": 1579481530911, # event time
931
+ # "s": "ETHBTC", # symbol
932
+ # "t": 158410082, # trade id
933
+ # "p": "0.01914100", # price
934
+ # "q": "0.00700000", # quantity
935
+ # "b": 586187049, # buyer order id
936
+ # "a": 586186710, # seller order id
937
+ # "T": 1579481530910, # trade time
938
+ # "m": False, # is the buyer the market maker
939
+ # "M": True # binance docs say it should be ignored
940
+ # }
941
+ #
942
+ # {
943
+ # "e": "aggTrade", # Event type
944
+ # "E": 123456789, # Event time
945
+ # "s": "BNBBTC", # Symbol
946
+ # "a": 12345, # Aggregate trade ID
947
+ # "p": "0.001", # Price
948
+ # "q": "100", # Quantity
949
+ # "f": 100, # First trade ID
950
+ # "l": 105, # Last trade ID
951
+ # "T": 123456785, # Trade time
952
+ # "m": True, # Is the buyer the market maker?
953
+ # "M": True # Ignore
954
+ # }
955
+ #
956
+ # private watchMyTrades spot
957
+ #
958
+ # {
959
+ # "e": "executionReport",
960
+ # "E": 1611063861489,
961
+ # "s": "BNBUSDT",
962
+ # "c": "m4M6AD5MF3b1ERe65l4SPq",
963
+ # "S": "BUY",
964
+ # "o": "MARKET",
965
+ # "f": "GTC",
966
+ # "q": "2.00000000",
967
+ # "p": "0.00000000",
968
+ # "P": "0.00000000",
969
+ # "F": "0.00000000",
970
+ # "g": -1,
971
+ # "C": '',
972
+ # "x": "TRADE",
973
+ # "X": "PARTIALLY_FILLED",
974
+ # "r": "NONE",
975
+ # "i": 1296882607,
976
+ # "l": "0.33200000",
977
+ # "z": "0.33200000",
978
+ # "L": "46.86600000",
979
+ # "n": "0.00033200",
980
+ # "N": "BNB",
981
+ # "T": 1611063861488,
982
+ # "t": 109747654,
983
+ # "I": 2696953381,
984
+ # "w": False,
985
+ # "m": False,
986
+ # "M": True,
987
+ # "O": 1611063861488,
988
+ # "Z": "15.55951200",
989
+ # "Y": "15.55951200",
990
+ # "Q": "0.00000000"
991
+ # }
992
+ #
993
+ # private watchMyTrades future/delivery
994
+ #
995
+ # {
996
+ # "s": "BTCUSDT",
997
+ # "c": "pb2jD6ZQHpfzSdUac8VqMK",
998
+ # "S": "SELL",
999
+ # "o": "MARKET",
1000
+ # "f": "GTC",
1001
+ # "q": "0.001",
1002
+ # "p": "0",
1003
+ # "ap": "33468.46000",
1004
+ # "sp": "0",
1005
+ # "x": "TRADE",
1006
+ # "X": "FILLED",
1007
+ # "i": 13351197194,
1008
+ # "l": "0.001",
1009
+ # "z": "0.001",
1010
+ # "L": "33468.46",
1011
+ # "n": "0.00027086",
1012
+ # "N": "BNB",
1013
+ # "T": 1612095165362,
1014
+ # "t": 458032604,
1015
+ # "b": "0",
1016
+ # "a": "0",
1017
+ # "m": False,
1018
+ # "R": False,
1019
+ # "wt": "CONTRACT_PRICE",
1020
+ # "ot": "MARKET",
1021
+ # "ps": "BOTH",
1022
+ # "cp": False,
1023
+ # "rp": "0.00335000",
1024
+ # "pP": False,
1025
+ # "si": 0,
1026
+ # "ss": 0
1027
+ # }
1028
+ #
1029
+ executionType = self.safe_string(trade, 'x')
1030
+ isTradeExecution = (executionType == 'TRADE')
1031
+ if not isTradeExecution:
1032
+ return self.parse_trade(trade, market)
1033
+ id = self.safe_string_2(trade, 't', 'a')
1034
+ timestamp = self.safe_integer(trade, 'T')
1035
+ price = self.safe_string_2(trade, 'L', 'p')
1036
+ amount = self.safe_string(trade, 'q')
1037
+ if isTradeExecution:
1038
+ amount = self.safe_string(trade, 'l', amount)
1039
+ cost = self.safe_string(trade, 'Y')
1040
+ if cost is None:
1041
+ if (price is not None) and (amount is not None):
1042
+ cost = Precise.string_mul(price, amount)
1043
+ marketId = self.safe_string(trade, 's')
1044
+ marketType = 'contract' if ('ps' in trade) else 'spot'
1045
+ symbol = self.safe_symbol(marketId, None, None, marketType)
1046
+ side = self.safe_string_lower(trade, 'S')
1047
+ takerOrMaker = None
1048
+ orderId = self.safe_string(trade, 'i')
1049
+ if 'm' in trade:
1050
+ if side is None:
1051
+ side = 'sell' if trade['m'] else 'buy' # self is reversed intentionally
1052
+ takerOrMaker = 'maker' if trade['m'] else 'taker'
1053
+ fee = None
1054
+ feeCost = self.safe_string(trade, 'n')
1055
+ if feeCost is not None:
1056
+ feeCurrencyId = self.safe_string(trade, 'N')
1057
+ feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
1058
+ fee = {
1059
+ 'cost': feeCost,
1060
+ 'currency': feeCurrencyCode,
1061
+ }
1062
+ type = self.safe_string_lower(trade, 'o')
1063
+ return self.safe_trade({
1064
+ 'info': trade,
1065
+ 'timestamp': timestamp,
1066
+ 'datetime': self.iso8601(timestamp),
1067
+ 'symbol': symbol,
1068
+ 'id': id,
1069
+ 'order': orderId,
1070
+ 'type': type,
1071
+ 'takerOrMaker': takerOrMaker,
1072
+ 'side': side,
1073
+ 'price': price,
1074
+ 'amount': amount,
1075
+ 'cost': cost,
1076
+ 'fee': fee,
1077
+ })
1078
+
1079
+ def handle_trade(self, client: Client, message):
1080
+ # the trade streams push raw trade information in real-time
1081
+ # each trade has a unique buyer and seller
1082
+ isSpot = ((client.url.find('wss://stream.binance.com') > -1) or (client.url.find('/testnet.binance') > -1))
1083
+ marketType = 'spot' if (isSpot) else 'contract'
1084
+ marketId = self.safe_string(message, 's')
1085
+ market = self.safe_market(marketId, None, None, marketType)
1086
+ symbol = market['symbol']
1087
+ lowerCaseId = self.safe_string_lower(message, 's')
1088
+ event = self.safe_string(message, 'e')
1089
+ messageHash = lowerCaseId + '@' + event
1090
+ trade = self.parse_ws_trade(message, market)
1091
+ tradesArray = self.safe_value(self.trades, symbol)
1092
+ if tradesArray is None:
1093
+ limit = self.safe_integer(self.options, 'tradesLimit', 1000)
1094
+ tradesArray = ArrayCache(limit)
1095
+ tradesArray.append(trade)
1096
+ self.trades[symbol] = tradesArray
1097
+ client.resolve(tradesArray, messageHash)
1098
+
1099
+ async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
1100
+ """
1101
+ watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
1102
+ :param str symbol: unified symbol of the market to fetch OHLCV data for
1103
+ :param str timeframe: the length of time each candle represents
1104
+ :param int [since]: timestamp in ms of the earliest candle to fetch
1105
+ :param int [limit]: the maximum amount of candles to fetch
1106
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1107
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
1108
+ """
1109
+ await self.load_markets()
1110
+ market = self.market(symbol)
1111
+ marketId = market['lowercaseId']
1112
+ interval = self.safe_string(self.timeframes, timeframe, timeframe)
1113
+ options = self.safe_value(self.options, 'watchOHLCV', {})
1114
+ nameOption = self.safe_string(options, 'name', 'kline')
1115
+ name = self.safe_string(params, 'name', nameOption)
1116
+ if name == 'indexPriceKline':
1117
+ marketId = marketId.replace('_perp', '')
1118
+ # weird behavior for index price kline we can't use the perp suffix
1119
+ params = self.omit(params, 'name')
1120
+ messageHash = marketId + '@' + name + '_' + interval
1121
+ type = market['type']
1122
+ if market['contract']:
1123
+ type = 'future' if market['linear'] else 'delivery'
1124
+ url = self.urls['api']['ws'][type] + '/' + self.stream(type, messageHash)
1125
+ requestId = self.request_id(url)
1126
+ request: dict = {
1127
+ 'method': 'SUBSCRIBE',
1128
+ 'params': [
1129
+ messageHash,
1130
+ ],
1131
+ 'id': requestId,
1132
+ }
1133
+ subscribe: dict = {
1134
+ 'id': requestId,
1135
+ }
1136
+ ohlcv = await self.watch(url, messageHash, self.extend(request, params), messageHash, subscribe)
1137
+ if self.newUpdates:
1138
+ limit = ohlcv.getLimit(symbol, limit)
1139
+ return self.filter_by_since_limit(ohlcv, since, limit, 0, True)
1140
+
1141
+ def handle_ohlcv(self, client: Client, message):
1142
+ #
1143
+ # {
1144
+ # "e": "kline",
1145
+ # "E": 1579482921215,
1146
+ # "s": "ETHBTC",
1147
+ # "k": {
1148
+ # "t": 1579482900000,
1149
+ # "T": 1579482959999,
1150
+ # "s": "ETHBTC",
1151
+ # "i": "1m",
1152
+ # "f": 158411535,
1153
+ # "L": 158411550,
1154
+ # "o": "0.01913200",
1155
+ # "c": "0.01913500",
1156
+ # "h": "0.01913700",
1157
+ # "l": "0.01913200",
1158
+ # "v": "5.08400000",
1159
+ # "n": 16,
1160
+ # "x": False,
1161
+ # "q": "0.09728060",
1162
+ # "V": "3.30200000",
1163
+ # "Q": "0.06318500",
1164
+ # "B": "0"
1165
+ # }
1166
+ # }
1167
+ #
1168
+ event = self.safe_string(message, 'e')
1169
+ eventMap: dict = {
1170
+ 'indexPrice_kline': 'indexPriceKline',
1171
+ 'markPrice_kline': 'markPriceKline',
1172
+ }
1173
+ event = self.safe_string(eventMap, event, event)
1174
+ kline = self.safe_value(message, 'k')
1175
+ marketId = self.safe_string_2(kline, 's', 'ps')
1176
+ if event == 'indexPriceKline':
1177
+ # indexPriceKline doesn't have the _PERP suffix
1178
+ marketId = self.safe_string(message, 'ps')
1179
+ lowercaseMarketId = marketId.lower()
1180
+ interval = self.safe_string(kline, 'i')
1181
+ # use a reverse lookup in a static map instead
1182
+ timeframe = self.find_timeframe(interval)
1183
+ messageHash = lowercaseMarketId + '@' + event + '_' + interval
1184
+ parsed = [
1185
+ self.safe_integer(kline, 't'),
1186
+ self.safe_float(kline, 'o'),
1187
+ self.safe_float(kline, 'h'),
1188
+ self.safe_float(kline, 'l'),
1189
+ self.safe_float(kline, 'c'),
1190
+ self.safe_float(kline, 'v'),
1191
+ ]
1192
+ isSpot = ((client.url.find('/stream') > -1) or (client.url.find('/testnet.binance') > -1))
1193
+ marketType = 'spot' if (isSpot) else 'contract'
1194
+ symbol = self.safe_symbol(marketId, None, None, marketType)
1195
+ self.ohlcvs[symbol] = self.safe_value(self.ohlcvs, symbol, {})
1196
+ stored = self.safe_value(self.ohlcvs[symbol], timeframe)
1197
+ if stored is None:
1198
+ limit = self.safe_integer(self.options, 'OHLCVLimit', 1000)
1199
+ stored = ArrayCacheByTimestamp(limit)
1200
+ self.ohlcvs[symbol][timeframe] = stored
1201
+ stored.append(parsed)
1202
+ client.resolve(stored, messageHash)
1203
+
1204
+ async def fetch_ticker_ws(self, symbol: str, params={}) -> Ticker:
1205
+ """
1206
+ fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
1207
+ :see: https://binance-docs.github.io/apidocs/voptions/en/#24hr-ticker-price-change-statistics
1208
+ :param str symbol: unified symbol of the market to fetch the ticker for
1209
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1210
+ :param str [params.method]: method to use can be ticker.price or ticker.book
1211
+ :param boolean [params.returnRateLimits]: return the rate limits for the exchange
1212
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
1213
+ """
1214
+ await self.load_markets()
1215
+ market = self.market(symbol)
1216
+ payload: dict = {
1217
+ 'symbol': market['id'],
1218
+ }
1219
+ type = self.get_market_type('fetchTickerWs', market, params)
1220
+ if type != 'future':
1221
+ raise BadRequest(self.id + ' fetchTickerWs only supports swap markets')
1222
+ url = self.urls['api']['ws']['ws-api'][type]
1223
+ requestId = self.request_id(url)
1224
+ messageHash = str(requestId)
1225
+ subscription: dict = {
1226
+ 'method': self.handle_ticker_ws,
1227
+ }
1228
+ returnRateLimits = False
1229
+ returnRateLimits, params = self.handle_option_and_params(params, 'fetchTickerWs', 'returnRateLimits', False)
1230
+ payload['returnRateLimits'] = returnRateLimits
1231
+ params = self.omit(params, 'test')
1232
+ method = None
1233
+ method, params = self.handle_option_and_params(params, 'fetchTickerWs', 'method', 'ticker.book')
1234
+ message: dict = {
1235
+ 'id': messageHash,
1236
+ 'method': method,
1237
+ 'params': self.sign_params(self.extend(payload, params)),
1238
+ }
1239
+ ticker = await self.watch(url, messageHash, message, messageHash, subscription)
1240
+ return ticker
1241
+
1242
+ async def fetch_ohlcv_ws(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
1243
+ """
1244
+ :see: https://binance-docs.github.io/apidocs/websocket_api/en/#klines
1245
+ query historical candlestick data containing the open, high, low, and close price, and the volume of a market
1246
+ :param str symbol: unified symbol of the market to query OHLCV data for
1247
+ :param str timeframe: the length of time each candle represents
1248
+ :param int since: timestamp in ms of the earliest candle to fetch
1249
+ :param int limit: the maximum amount of candles to fetch
1250
+ :param dict params: extra parameters specific to the exchange API endpoint
1251
+ :param int params['until']: timestamp in ms of the earliest candle to fetch
1252
+ *
1253
+ * EXCHANGE SPECIFIC PARAMETERS
1254
+ :param str params['timeZone']: default=0(UTC)
1255
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
1256
+ """
1257
+ await self.load_markets()
1258
+ market = self.market(symbol)
1259
+ marketType = self.get_market_type('fetchOHLCVWs', market, params)
1260
+ if marketType != 'spot' and marketType != 'future':
1261
+ raise BadRequest(self.id + ' fetchOHLCVWs only supports spot or swap markets')
1262
+ url = self.urls['api']['ws']['ws-api'][marketType]
1263
+ requestId = self.request_id(url)
1264
+ messageHash = str(requestId)
1265
+ returnRateLimits = False
1266
+ returnRateLimits, params = self.handle_option_and_params(params, 'fetchOHLCVWs', 'returnRateLimits', False)
1267
+ payload: dict = {
1268
+ 'symbol': self.market_id(symbol),
1269
+ 'returnRateLimits': returnRateLimits,
1270
+ 'interval': self.timeframes[timeframe],
1271
+ }
1272
+ until = self.safe_integer(params, 'until')
1273
+ params = self.omit(params, 'until')
1274
+ if since is not None:
1275
+ payload['startTime'] = since
1276
+ if limit is not None:
1277
+ payload['limit'] = limit
1278
+ if until is not None:
1279
+ payload['endTime'] = until
1280
+ message: dict = {
1281
+ 'id': messageHash,
1282
+ 'method': 'klines',
1283
+ 'params': self.extend(payload, params),
1284
+ }
1285
+ subscription: dict = {
1286
+ 'method': self.handle_fetch_ohlcv,
1287
+ }
1288
+ return await self.watch(url, messageHash, message, messageHash, subscription)
1289
+
1290
+ def handle_fetch_ohlcv(self, client: Client, message):
1291
+ #
1292
+ # {
1293
+ # "id": "1dbbeb56-8eea-466a-8f6e-86bdcfa2fc0b",
1294
+ # "status": 200,
1295
+ # "result": [
1296
+ # [
1297
+ # 1655971200000, # Kline open time
1298
+ # "0.01086000", # Open price
1299
+ # "0.01086600", # High price
1300
+ # "0.01083600", # Low price
1301
+ # "0.01083800", # Close price
1302
+ # "2290.53800000", # Volume
1303
+ # 1655974799999, # Kline close time
1304
+ # "24.85074442", # Quote asset volume
1305
+ # 2283, # Number of trades
1306
+ # "1171.64000000", # Taker buy base asset volume
1307
+ # "12.71225884", # Taker buy quote asset volume
1308
+ # "0" # Unused field, ignore
1309
+ # ]
1310
+ # ],
1311
+ # "rateLimits": [
1312
+ # {
1313
+ # "rateLimitType": "REQUEST_WEIGHT",
1314
+ # "interval": "MINUTE",
1315
+ # "intervalNum": 1,
1316
+ # "limit": 6000,
1317
+ # "count": 2
1318
+ # }
1319
+ # ]
1320
+ # }
1321
+ #
1322
+ result = self.safe_list(message, 'result')
1323
+ parsed = self.parse_ohlcvs(result)
1324
+ # use a reverse lookup in a static map instead
1325
+ messageHash = self.safe_string(message, 'id')
1326
+ client.resolve(parsed, messageHash)
1327
+
1328
+ async def watch_ticker(self, symbol: str, params={}) -> Ticker:
1329
+ """
1330
+ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
1331
+ :param str symbol: unified symbol of the market to fetch the ticker for
1332
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1333
+ :param str [params.name]: stream to use can be ticker or miniTicker
1334
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
1335
+ """
1336
+ await self.load_markets()
1337
+ symbol = self.symbol(symbol)
1338
+ tickers = await self.watch_tickers([symbol], self.extend(params, {'callerMethodName': 'watchTicker'}))
1339
+ return tickers[symbol]
1340
+
1341
+ async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
1342
+ """
1343
+ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
1344
+ :param str[] symbols: unified symbol of the market to fetch the ticker for
1345
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1346
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
1347
+ """
1348
+ channelName = None
1349
+ channelName, params = self.handle_option_and_params(params, 'watchTickers', 'name', 'ticker')
1350
+ if channelName == 'bookTicker':
1351
+ raise BadRequest(self.id + ' deprecation notice - to subscribe for bids-asks, use watch_bids_asks() method instead')
1352
+ newTickers = await self.watch_multi_ticker_helper('watchTickers', channelName, symbols, params)
1353
+ if self.newUpdates:
1354
+ return newTickers
1355
+ return self.filter_by_array(self.tickers, 'symbol', symbols)
1356
+
1357
+ async def watch_bids_asks(self, symbols: Strings = None, params={}) -> Tickers:
1358
+ """
1359
+ :see: https://binance-docs.github.io/apidocs/spot/en/#individual-symbol-book-ticker-streams
1360
+ :see: https://binance-docs.github.io/apidocs/futures/en/#all-book-tickers-stream
1361
+ :see: https://binance-docs.github.io/apidocs/delivery/en/#all-book-tickers-stream
1362
+ watches best bid & ask for symbols
1363
+ :param str[] symbols: unified symbol of the market to fetch the ticker for
1364
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1365
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
1366
+ """
1367
+ result = await self.watch_multi_ticker_helper('watchBidsAsks', 'bookTicker', symbols, params)
1368
+ if self.newUpdates:
1369
+ return result
1370
+ return self.filter_by_array(self.tickers, 'symbol', symbols)
1371
+
1372
+ async def watch_multi_ticker_helper(self, methodName, channelName: str, symbols: Strings = None, params={}):
1373
+ await self.load_markets()
1374
+ symbols = self.market_symbols(symbols, None, True, False, True)
1375
+ firstMarket = None
1376
+ marketType = None
1377
+ symbolsDefined = (symbols is not None)
1378
+ if symbolsDefined:
1379
+ firstMarket = self.market(symbols[0])
1380
+ marketType, params = self.handle_market_type_and_params(methodName, firstMarket, params)
1381
+ subType = None
1382
+ subType, params = self.handle_sub_type_and_params(methodName, firstMarket, params)
1383
+ rawMarketType = None
1384
+ if self.isLinear(marketType, subType):
1385
+ rawMarketType = 'future'
1386
+ elif self.isInverse(marketType, subType):
1387
+ rawMarketType = 'delivery'
1388
+ elif marketType == 'spot':
1389
+ rawMarketType = marketType
1390
+ else:
1391
+ raise NotSupported(self.id + ' ' + methodName + '() does not support options markets')
1392
+ isBidAsk = (channelName == 'bookTicker')
1393
+ subscriptionArgs = []
1394
+ messageHashes = []
1395
+ if symbolsDefined:
1396
+ for i in range(0, len(symbols)):
1397
+ symbol = symbols[i]
1398
+ market = self.market(symbol)
1399
+ subscriptionArgs.append(market['lowercaseId'] + '@' + channelName)
1400
+ messageHashes.append(self.get_message_hash(channelName, market['symbol'], isBidAsk))
1401
+ else:
1402
+ if isBidAsk:
1403
+ if marketType == 'spot':
1404
+ raise ArgumentsRequired(self.id + ' ' + methodName + '() requires symbols for self channel for spot markets')
1405
+ subscriptionArgs.append('!' + channelName)
1406
+ else:
1407
+ subscriptionArgs.append('!' + channelName + '@arr')
1408
+ messageHashes.append(self.get_message_hash(channelName, None, isBidAsk))
1409
+ streamHash = channelName
1410
+ if symbolsDefined:
1411
+ streamHash = channelName + '::' + ','.join(symbols)
1412
+ url = self.urls['api']['ws'][rawMarketType] + '/' + self.stream(rawMarketType, streamHash)
1413
+ requestId = self.request_id(url)
1414
+ request: dict = {
1415
+ 'method': 'SUBSCRIBE',
1416
+ 'params': subscriptionArgs,
1417
+ 'id': requestId,
1418
+ }
1419
+ subscribe: dict = {
1420
+ 'id': requestId,
1421
+ }
1422
+ result = await self.watch_multiple(url, messageHashes, self.deep_extend(request, params), subscriptionArgs, subscribe)
1423
+ # for efficiency, we have two type of returned structure here - if symbols array was provided, then individual
1424
+ # ticker dict comes in, otherwise all-tickers dict comes in
1425
+ if not symbolsDefined:
1426
+ return result
1427
+ else:
1428
+ newDict: dict = {}
1429
+ newDict[result['symbol']] = result
1430
+ return newDict
1431
+
1432
+ def parse_ws_ticker(self, message, marketType):
1433
+ #
1434
+ # ticker
1435
+ # {
1436
+ # "e": "24hrTicker", # event type
1437
+ # "E": 1579485598569, # event time
1438
+ # "s": "ETHBTC", # symbol
1439
+ # "p": "-0.00004000", # price change
1440
+ # "P": "-0.209", # price change percent
1441
+ # "w": "0.01920495", # weighted average price
1442
+ # "x": "0.01916500", # the price of the first trade before the 24hr rolling window
1443
+ # "c": "0.01912500", # last(closing) price
1444
+ # "Q": "0.10400000", # last quantity
1445
+ # "b": "0.01912200", # best bid
1446
+ # "B": "4.10400000", # best bid quantity
1447
+ # "a": "0.01912500", # best ask
1448
+ # "A": "0.00100000", # best ask quantity
1449
+ # "o": "0.01916500", # open price
1450
+ # "h": "0.01956500", # high price
1451
+ # "l": "0.01887700", # low price
1452
+ # "v": "173518.11900000", # base volume
1453
+ # "q": "3332.40703994", # quote volume
1454
+ # "O": 1579399197842, # open time
1455
+ # "C": 1579485597842, # close time
1456
+ # "F": 158251292, # first trade id
1457
+ # "L": 158414513, # last trade id
1458
+ # "n": 163222, # total number of trades
1459
+ # }
1460
+ #
1461
+ # miniTicker
1462
+ # {
1463
+ # "e": "24hrMiniTicker",
1464
+ # "E": 1671617114585,
1465
+ # "s": "MOBBUSD",
1466
+ # "c": "0.95900000",
1467
+ # "o": "0.91200000",
1468
+ # "h": "1.04000000",
1469
+ # "l": "0.89400000",
1470
+ # "v": "2109995.32000000",
1471
+ # "q": "2019254.05788000"
1472
+ # }
1473
+ # fetchTickerWs
1474
+ # {
1475
+ # "symbol":"BTCUSDT",
1476
+ # "price":"72606.70",
1477
+ # "time":1712526204284
1478
+ # }
1479
+ # fetchTickerWs - ticker.book
1480
+ # {
1481
+ # "lastUpdateId":1027024,
1482
+ # "symbol":"BTCUSDT",
1483
+ # "bidPrice":"4.00000000",
1484
+ # "bidQty":"431.00000000",
1485
+ # "askPrice":"4.00000200",
1486
+ # "askQty":"9.00000000",
1487
+ # "time":1589437530011,
1488
+ # }
1489
+ #
1490
+ event = self.safe_string(message, 'e', 'bookTicker')
1491
+ if event == '24hrTicker':
1492
+ event = 'ticker'
1493
+ timestamp = None
1494
+ if event == 'bookTicker':
1495
+ # take the event timestamp, if available, for spot tickers it is not
1496
+ timestamp = self.safe_integer_2(message, 'E', 'time')
1497
+ else:
1498
+ # take the timestamp of the closing price for candlestick streams
1499
+ timestamp = self.safe_integer_n(message, ['C', 'E', 'time'])
1500
+ marketId = self.safe_string_2(message, 's', 'symbol')
1501
+ symbol = self.safe_symbol(marketId, None, None, marketType)
1502
+ market = self.safe_market(marketId, None, None, marketType)
1503
+ last = self.safe_string_2(message, 'c', 'price')
1504
+ return self.safe_ticker({
1505
+ 'symbol': symbol,
1506
+ 'timestamp': timestamp,
1507
+ 'datetime': self.iso8601(timestamp),
1508
+ 'high': self.safe_string(message, 'h'),
1509
+ 'low': self.safe_string(message, 'l'),
1510
+ 'bid': self.safe_string_2(message, 'b', 'bidPrice'),
1511
+ 'bidVolume': self.safe_string_2(message, 'B', 'bidQty'),
1512
+ 'ask': self.safe_string_2(message, 'a', 'askPrice'),
1513
+ 'askVolume': self.safe_string_2(message, 'A', 'askQty'),
1514
+ 'vwap': self.safe_string(message, 'w'),
1515
+ 'open': self.safe_string(message, 'o'),
1516
+ 'close': last,
1517
+ 'last': last,
1518
+ 'previousClose': self.safe_string(message, 'x'), # previous day close
1519
+ 'change': self.safe_string(message, 'p'),
1520
+ 'percentage': self.safe_string(message, 'P'),
1521
+ 'average': None,
1522
+ 'baseVolume': self.safe_string(message, 'v'),
1523
+ 'quoteVolume': self.safe_string(message, 'q'),
1524
+ 'info': message,
1525
+ }, market)
1526
+
1527
+ def handle_ticker_ws(self, client: Client, message):
1528
+ #
1529
+ # ticker.price
1530
+ # {
1531
+ # "id":"1",
1532
+ # "status":200,
1533
+ # "result":{
1534
+ # "symbol":"BTCUSDT",
1535
+ # "price":"73178.50",
1536
+ # "time":1712527052374
1537
+ # }
1538
+ # }
1539
+ # ticker.book
1540
+ # {
1541
+ # "id":"9d32157c-a556-4d27-9866-66760a174b57",
1542
+ # "status":200,
1543
+ # "result":{
1544
+ # "lastUpdateId":1027024,
1545
+ # "symbol":"BTCUSDT",
1546
+ # "bidPrice":"4.00000000",
1547
+ # "bidQty":"431.00000000",
1548
+ # "askPrice":"4.00000200",
1549
+ # "askQty":"9.00000000",
1550
+ # "time":1589437530011 # Transaction time
1551
+ # }
1552
+ # }
1553
+ #
1554
+ messageHash = self.safe_string(message, 'id')
1555
+ result = self.safe_value(message, 'result', {})
1556
+ ticker = self.parse_ws_ticker(result, 'future')
1557
+ client.resolve(ticker, messageHash)
1558
+
1559
+ def handle_bids_asks(self, client: Client, message):
1560
+ #
1561
+ # arrives one symbol dict or array of symbol dicts
1562
+ #
1563
+ # {
1564
+ # "u": 7488717758,
1565
+ # "s": "BTCUSDT",
1566
+ # "b": "28621.74000000",
1567
+ # "B": "1.43278800",
1568
+ # "a": "28621.75000000",
1569
+ # "A": "2.52500800"
1570
+ # }
1571
+ #
1572
+ self.handle_tickers_and_bids_asks(client, message, 'bidasks')
1573
+
1574
+ def handle_tickers(self, client: Client, message):
1575
+ #
1576
+ # arrives one symbol dict or array of symbol dicts
1577
+ #
1578
+ # {
1579
+ # "e": "24hrTicker", # event type
1580
+ # "E": 1579485598569, # event time
1581
+ # "s": "ETHBTC", # symbol
1582
+ # "p": "-0.00004000", # price change
1583
+ # "P": "-0.209", # price change percent
1584
+ # "w": "0.01920495", # weighted average price
1585
+ # "x": "0.01916500", # the price of the first trade before the 24hr rolling window
1586
+ # "c": "0.01912500", # last(closing) price
1587
+ # "Q": "0.10400000", # last quantity
1588
+ # "b": "0.01912200", # best bid
1589
+ # "B": "4.10400000", # best bid quantity
1590
+ # "a": "0.01912500", # best ask
1591
+ # "A": "0.00100000", # best ask quantity
1592
+ # "o": "0.01916500", # open price
1593
+ # "h": "0.01956500", # high price
1594
+ # "l": "0.01887700", # low price
1595
+ # "v": "173518.11900000", # base volume
1596
+ # "q": "3332.40703994", # quote volume
1597
+ # "O": 1579399197842, # open time
1598
+ # "C": 1579485597842, # close time
1599
+ # "F": 158251292, # first trade id
1600
+ # "L": 158414513, # last trade id
1601
+ # "n": 163222, # total number of trades
1602
+ # }
1603
+ #
1604
+ self.handle_tickers_and_bids_asks(client, message, 'tickers')
1605
+
1606
+ def handle_tickers_and_bids_asks(self, client: Client, message, methodType):
1607
+ isSpot = ((client.url.find('/stream') > -1) or (client.url.find('/testnet.binance') > -1))
1608
+ marketType = 'spot' if (isSpot) else 'contract'
1609
+ isBidAsk = (methodType == 'bidasks')
1610
+ channelName = None
1611
+ resolvedMessageHashes = []
1612
+ rawTickers = []
1613
+ newTickers: dict = {}
1614
+ if isinstance(message, list):
1615
+ rawTickers = message
1616
+ else:
1617
+ rawTickers.append(message)
1618
+ for i in range(0, len(rawTickers)):
1619
+ ticker = rawTickers[i]
1620
+ event = self.safe_string(ticker, 'e')
1621
+ if isBidAsk:
1622
+ event = 'bookTicker' # in `handleMessage`, bookTicker doesn't have identifier, so manually set here
1623
+ channelName = self.safe_string(self.options['tickerChannelsMap'], event, event)
1624
+ if channelName is None:
1625
+ continue
1626
+ parsedTicker = self.parse_ws_ticker(ticker, marketType)
1627
+ symbol = parsedTicker['symbol']
1628
+ newTickers[symbol] = parsedTicker
1629
+ if isBidAsk:
1630
+ self.bidsasks[symbol] = parsedTicker
1631
+ else:
1632
+ self.tickers[symbol] = parsedTicker
1633
+ messageHash = self.get_message_hash(channelName, symbol, isBidAsk)
1634
+ resolvedMessageHashes.append(messageHash)
1635
+ client.resolve(parsedTicker, messageHash)
1636
+ # resolve batch endpoint
1637
+ length = len(resolvedMessageHashes)
1638
+ if length > 0:
1639
+ batchMessageHash = self.get_message_hash(channelName, None, isBidAsk)
1640
+ client.resolve(newTickers, batchMessageHash)
1641
+
1642
+ def get_message_hash(self, channelName: str, symbol: Str, isBidAsk: bool):
1643
+ prefix = 'bidask' if isBidAsk else 'ticker'
1644
+ if symbol is not None:
1645
+ return prefix + ':' + channelName + '@' + symbol
1646
+ else:
1647
+ return prefix + 's' + ':' + channelName
1648
+
1649
+ def sign_params(self, params={}):
1650
+ self.check_required_credentials()
1651
+ extendedParams = self.extend({
1652
+ 'timestamp': self.nonce(),
1653
+ 'apiKey': self.apiKey,
1654
+ }, params)
1655
+ defaultRecvWindow = self.safe_integer(self.options, 'recvWindow')
1656
+ if defaultRecvWindow is not None:
1657
+ params['recvWindow'] = defaultRecvWindow
1658
+ recvWindow = self.safe_integer(params, 'recvWindow')
1659
+ if recvWindow is not None:
1660
+ params['recvWindow'] = recvWindow
1661
+ extendedParams = self.keysort(extendedParams)
1662
+ query = self.urlencode(extendedParams)
1663
+ signature = None
1664
+ if self.secret.find('PRIVATE KEY') > -1:
1665
+ if len(self.secret) > 120:
1666
+ signature = self.rsa(query, self.secret, 'sha256')
1667
+ else:
1668
+ signature = self.eddsa(self.encode(query), self.secret, 'ed25519')
1669
+ else:
1670
+ signature = self.hmac(self.encode(query), self.encode(self.secret), hashlib.sha256)
1671
+ extendedParams['signature'] = signature
1672
+ return extendedParams
1673
+
1674
+ async def authenticate(self, params={}):
1675
+ time = self.milliseconds()
1676
+ type = None
1677
+ type, params = self.handle_market_type_and_params('authenticate', None, params)
1678
+ subType = None
1679
+ subType, params = self.handle_sub_type_and_params('authenticate', None, params)
1680
+ isPortfolioMargin = None
1681
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'authenticate', 'papi', 'portfolioMargin', False)
1682
+ if self.isLinear(type, subType):
1683
+ type = 'future'
1684
+ elif self.isInverse(type, subType):
1685
+ type = 'delivery'
1686
+ marginMode = None
1687
+ marginMode, params = self.handle_margin_mode_and_params('authenticate', params)
1688
+ isIsolatedMargin = (marginMode == 'isolated')
1689
+ isCrossMargin = (marginMode == 'cross') or (marginMode is None)
1690
+ symbol = self.safe_string(params, 'symbol')
1691
+ params = self.omit(params, 'symbol')
1692
+ options = self.safe_value(self.options, type, {})
1693
+ lastAuthenticatedTime = self.safe_integer(options, 'lastAuthenticatedTime', 0)
1694
+ listenKeyRefreshRate = self.safe_integer(self.options, 'listenKeyRefreshRate', 1200000)
1695
+ delay = self.sum(listenKeyRefreshRate, 10000)
1696
+ if time - lastAuthenticatedTime > delay:
1697
+ response = None
1698
+ if isPortfolioMargin:
1699
+ response = await self.papiPostListenKey(params)
1700
+ elif type == 'future':
1701
+ response = await self.fapiPrivatePostListenKey(params)
1702
+ elif type == 'delivery':
1703
+ response = await self.dapiPrivatePostListenKey(params)
1704
+ elif type == 'margin' and isCrossMargin:
1705
+ response = await self.sapiPostUserDataStream(params)
1706
+ elif isIsolatedMargin:
1707
+ if symbol is None:
1708
+ raise ArgumentsRequired(self.id + ' authenticate() requires a symbol argument for isolated margin mode')
1709
+ marketId = self.market_id(symbol)
1710
+ params = self.extend(params, {'symbol': marketId})
1711
+ response = await self.sapiPostUserDataStreamIsolated(params)
1712
+ else:
1713
+ response = await self.publicPostUserDataStream(params)
1714
+ self.options[type] = self.extend(options, {
1715
+ 'listenKey': self.safe_string(response, 'listenKey'),
1716
+ 'lastAuthenticatedTime': time,
1717
+ })
1718
+ self.delay(listenKeyRefreshRate, self.keep_alive_listen_key, params)
1719
+
1720
+ async def keep_alive_listen_key(self, params={}):
1721
+ # https://binance-docs.github.io/apidocs/spot/en/#listen-key-spot
1722
+ type = self.safe_string_2(self.options, 'defaultType', 'authenticate', 'spot')
1723
+ type = self.safe_string(params, 'type', type)
1724
+ isPortfolioMargin = None
1725
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'keepAliveListenKey', 'papi', 'portfolioMargin', False)
1726
+ subTypeInfo = self.handle_sub_type_and_params('keepAliveListenKey', None, params)
1727
+ subType = subTypeInfo[0]
1728
+ if self.isLinear(type, subType):
1729
+ type = 'future'
1730
+ elif self.isInverse(type, subType):
1731
+ type = 'delivery'
1732
+ options = self.safe_value(self.options, type, {})
1733
+ listenKey = self.safe_string(options, 'listenKey')
1734
+ if listenKey is None:
1735
+ # A network error happened: we can't renew a listen key that does not exist.
1736
+ return
1737
+ request: dict = {}
1738
+ symbol = self.safe_string(params, 'symbol')
1739
+ params = self.omit(params, ['type', 'symbol'])
1740
+ time = self.milliseconds()
1741
+ try:
1742
+ if isPortfolioMargin:
1743
+ await self.papiPutListenKey(self.extend(request, params))
1744
+ elif type == 'future':
1745
+ await self.fapiPrivatePutListenKey(self.extend(request, params))
1746
+ elif type == 'delivery':
1747
+ await self.dapiPrivatePutListenKey(self.extend(request, params))
1748
+ else:
1749
+ request['listenKey'] = listenKey
1750
+ if type == 'margin':
1751
+ request['symbol'] = symbol
1752
+ await self.sapiPutUserDataStream(self.extend(request, params))
1753
+ else:
1754
+ await self.publicPutUserDataStream(self.extend(request, params))
1755
+ except Exception as error:
1756
+ urlType = type
1757
+ if isPortfolioMargin:
1758
+ urlType = 'papi'
1759
+ url = self.urls['api']['ws'][urlType] + '/' + self.options[type]['listenKey']
1760
+ client = self.client(url)
1761
+ messageHashes = list(client.futures.keys())
1762
+ for i in range(0, len(messageHashes)):
1763
+ messageHash = messageHashes[i]
1764
+ client.reject(error, messageHash)
1765
+ self.options[type] = self.extend(options, {
1766
+ 'listenKey': None,
1767
+ 'lastAuthenticatedTime': 0,
1768
+ })
1769
+ return
1770
+ self.options[type] = self.extend(options, {
1771
+ 'listenKey': listenKey,
1772
+ 'lastAuthenticatedTime': time,
1773
+ })
1774
+ # whether or not to schedule another listenKey keepAlive request
1775
+ clients = list(self.clients.values())
1776
+ listenKeyRefreshRate = self.safe_integer(self.options, 'listenKeyRefreshRate', 1200000)
1777
+ for i in range(0, len(clients)):
1778
+ client = clients[i]
1779
+ subscriptionKeys = list(client.subscriptions.keys())
1780
+ for j in range(0, len(subscriptionKeys)):
1781
+ subscribeType = subscriptionKeys[j]
1782
+ if subscribeType == type:
1783
+ self.delay(listenKeyRefreshRate, self.keep_alive_listen_key, params)
1784
+ return
1785
+
1786
+ def set_balance_cache(self, client: Client, type, isPortfolioMargin=False):
1787
+ if type in client.subscriptions:
1788
+ return
1789
+ options = self.safe_value(self.options, 'watchBalance')
1790
+ fetchBalanceSnapshot = self.safe_bool(options, 'fetchBalanceSnapshot', False)
1791
+ if fetchBalanceSnapshot:
1792
+ messageHash = type + ':fetchBalanceSnapshot'
1793
+ if not (messageHash in client.futures):
1794
+ client.future(messageHash)
1795
+ self.spawn(self.load_balance_snapshot, client, messageHash, type, isPortfolioMargin)
1796
+ else:
1797
+ self.balance[type] = {}
1798
+
1799
+ async def load_balance_snapshot(self, client, messageHash, type, isPortfolioMargin):
1800
+ params: dict = {
1801
+ 'type': type,
1802
+ }
1803
+ if isPortfolioMargin:
1804
+ params['portfolioMargin'] = True
1805
+ response = await self.fetch_balance(params)
1806
+ self.balance[type] = self.extend(response, self.safe_value(self.balance, type, {}))
1807
+ # don't remove the future from the .futures cache
1808
+ future = client.futures[messageHash]
1809
+ future.resolve()
1810
+ client.resolve(self.balance[type], type + ':balance')
1811
+
1812
+ async def fetch_balance_ws(self, params={}) -> Balances:
1813
+ """
1814
+ fetch balance and get the amount of funds available for trading or funds locked in orders
1815
+ :see: https://binance-docs.github.io/apidocs/websocket_api/en/#account-information-user_data
1816
+ :see: https://binance-docs.github.io/apidocs/futures/en/#account-information-user_data
1817
+ :see: https://binance-docs.github.io/apidocs/futures/en/#futures-account-balance-user_data
1818
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1819
+ :param str|None [params.type]: 'future', 'delivery', 'savings', 'funding', or 'spot'
1820
+ :param str|None [params.marginMode]: 'cross' or 'isolated', for margin trading, uses self.options.defaultMarginMode if not passed, defaults to None/None/None
1821
+ :param str[]|None [params.symbols]: unified market symbols, only used in isolated margin mode
1822
+ :param str|None [params.method]: method to use. Can be account.balance or account.status
1823
+ :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
1824
+ """
1825
+ await self.load_markets()
1826
+ type = self.get_market_type('fetchBalanceWs', None, params)
1827
+ if type != 'spot' and type != 'future':
1828
+ raise BadRequest(self.id + ' fetchBalanceWs only supports spot or swap markets')
1829
+ url = self.urls['api']['ws']['ws-api'][type]
1830
+ requestId = self.request_id(url)
1831
+ messageHash = str(requestId)
1832
+ returnRateLimits = False
1833
+ returnRateLimits, params = self.handle_option_and_params(params, 'fetchBalanceWs', 'returnRateLimits', False)
1834
+ payload: dict = {
1835
+ 'returnRateLimits': returnRateLimits,
1836
+ }
1837
+ method = None
1838
+ method, params = self.handle_option_and_params(params, 'fetchBalanceWs', 'method', 'account.status')
1839
+ message: dict = {
1840
+ 'id': messageHash,
1841
+ 'method': method,
1842
+ 'params': self.sign_params(self.extend(payload, params)),
1843
+ }
1844
+ subscription: dict = {
1845
+ 'method': self.handle_account_status_ws if (method == 'account.status') else self.handle_balance_ws,
1846
+ }
1847
+ return await self.watch(url, messageHash, message, messageHash, subscription)
1848
+
1849
+ def handle_balance_ws(self, client: Client, message):
1850
+ #
1851
+ #
1852
+ messageHash = self.safe_string(message, 'id')
1853
+ result = self.safe_dict(message, 'result', {})
1854
+ rawBalance = self.safe_list(result, 0, [])
1855
+ parsedBalances = self.parseBalanceCustom(rawBalance)
1856
+ client.resolve(parsedBalances, messageHash)
1857
+
1858
+ def handle_account_status_ws(self, client: Client, message):
1859
+ #
1860
+ # spot
1861
+ # {
1862
+ # "id": "605a6d20-6588-4cb9-afa0-b0ab087507ba",
1863
+ # "status": 200,
1864
+ # "result": {
1865
+ # "makerCommission": 15,
1866
+ # "takerCommission": 15,
1867
+ # "buyerCommission": 0,
1868
+ # "sellerCommission": 0,
1869
+ # "canTrade": True,
1870
+ # "canWithdraw": True,
1871
+ # "canDeposit": True,
1872
+ # "commissionRates": {
1873
+ # "maker": "0.00150000",
1874
+ # "taker": "0.00150000",
1875
+ # "buyer": "0.00000000",
1876
+ # "seller": "0.00000000"
1877
+ # },
1878
+ # "brokered": False,
1879
+ # "requireSelfTradePrevention": False,
1880
+ # "updateTime": 1660801833000,
1881
+ # "accountType": "SPOT",
1882
+ # "balances": [{
1883
+ # "asset": "BNB",
1884
+ # "free": "0.00000000",
1885
+ # "locked": "0.00000000"
1886
+ # },
1887
+ # {
1888
+ # "asset": "BTC",
1889
+ # "free": "1.3447112",
1890
+ # "locked": "0.08600000"
1891
+ # },
1892
+ # {
1893
+ # "asset": "USDT",
1894
+ # "free": "1021.21000000",
1895
+ # "locked": "0.00000000"
1896
+ # }
1897
+ # ],
1898
+ # "permissions": [
1899
+ # "SPOT"
1900
+ # ]
1901
+ # }
1902
+ # }
1903
+ # swap
1904
+ #
1905
+ messageHash = self.safe_string(message, 'id')
1906
+ result = self.safe_dict(message, 'result', {})
1907
+ parsedBalances = self.parseBalanceCustom(result)
1908
+ client.resolve(parsedBalances, messageHash)
1909
+
1910
+ async def fetch_position_ws(self, symbol: str, params={}) -> List[Position]:
1911
+ """
1912
+ :see: https://binance-docs.github.io/apidocs/futures/en/#position-information-user_data
1913
+ fetch data on an open position
1914
+ :param str symbol: unified market symbol of the market the position is held in
1915
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1916
+ :returns dict: a `position structure <https://docs.ccxt.com/#/?id=position-structure>`
1917
+ """
1918
+ return await self.fetch_positions_ws([symbol], params)
1919
+
1920
+ async def fetch_positions_ws(self, symbols: Strings = None, params={}) -> List[Position]:
1921
+ """
1922
+ fetch all open positions
1923
+ :see: https://binance-docs.github.io/apidocs/futures/en/#position-information-user_data
1924
+ :param str[] [symbols]: list of unified market symbols
1925
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1926
+ :param boolean [params.returnRateLimits]: set to True to return rate limit informations, defaults to False.
1927
+ :returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
1928
+ """
1929
+ await self.load_markets()
1930
+ symbols = self.market_symbols(symbols, 'swap', True, True, True)
1931
+ url = self.urls['api']['ws']['ws-api']['future']
1932
+ requestId = self.request_id(url)
1933
+ messageHash = str(requestId)
1934
+ payload: dict = {}
1935
+ if symbols is not None:
1936
+ symbolsLength = len(symbols)
1937
+ if symbolsLength == 1:
1938
+ payload['symbol'] = self.market_id(symbols[0])
1939
+ returnRateLimits = False
1940
+ returnRateLimits, params = self.handle_option_and_params(params, 'fetchPositionsWs', 'returnRateLimits', False)
1941
+ payload['returnRateLimits'] = returnRateLimits
1942
+ message: dict = {
1943
+ 'id': messageHash,
1944
+ 'method': 'account.position',
1945
+ 'params': self.sign_params(self.extend(payload, params)),
1946
+ }
1947
+ subscription: dict = {
1948
+ 'method': self.handle_positions_ws,
1949
+ }
1950
+ result = await self.watch(url, messageHash, message, messageHash, subscription)
1951
+ return self.filter_by_array_positions(result, 'symbol', symbols, False)
1952
+
1953
+ def handle_positions_ws(self, client: Client, message):
1954
+ #
1955
+ # {
1956
+ # id: '1',
1957
+ # status: 200,
1958
+ # result: [
1959
+ # {
1960
+ # symbol: 'BTCUSDT',
1961
+ # positionAmt: '-0.014',
1962
+ # entryPrice: '42901.1',
1963
+ # breakEvenPrice: '30138.83333142',
1964
+ # markPrice: '71055.98470333',
1965
+ # unRealizedProfit: '-394.16838584',
1966
+ # liquidationPrice: '137032.02272908',
1967
+ # leverage: '123',
1968
+ # maxNotionalValue: '50000',
1969
+ # marginType: 'cross',
1970
+ # isolatedMargin: '0.00000000',
1971
+ # isAutoAddMargin: 'false',
1972
+ # positionSide: 'BOTH',
1973
+ # notional: '-994.78378584',
1974
+ # isolatedWallet: '0',
1975
+ # updateTime: 1708906343111,
1976
+ # isolated: False,
1977
+ # adlQuantile: 2
1978
+ # },
1979
+ # ...
1980
+ # ]
1981
+ # }
1982
+ #
1983
+ #
1984
+ messageHash = self.safe_string(message, 'id')
1985
+ result = self.safe_list(message, 'result', [])
1986
+ positions = []
1987
+ for i in range(0, len(result)):
1988
+ parsed = self.parse_position_risk(result[i])
1989
+ entryPrice = self.safe_string(parsed, 'entryPrice')
1990
+ if (entryPrice != '0') and (entryPrice != '0.0') and (entryPrice != '0.00000000'):
1991
+ positions.append(parsed)
1992
+ client.resolve(positions, messageHash)
1993
+
1994
+ async def watch_balance(self, params={}) -> Balances:
1995
+ """
1996
+ watch balance and get the amount of funds available for trading or funds locked in orders
1997
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1998
+ :param boolean [params.portfolioMargin]: set to True if you would like to watch the balance of a portfolio margin account
1999
+ :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
2000
+ """
2001
+ await self.load_markets()
2002
+ await self.authenticate(params)
2003
+ defaultType = self.safe_string(self.options, 'defaultType', 'spot')
2004
+ type = self.safe_string(params, 'type', defaultType)
2005
+ subType = None
2006
+ subType, params = self.handle_sub_type_and_params('watchBalance', None, params)
2007
+ isPortfolioMargin = None
2008
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'watchBalance', 'papi', 'portfolioMargin', False)
2009
+ urlType = type
2010
+ if isPortfolioMargin:
2011
+ urlType = 'papi'
2012
+ if self.isLinear(type, subType):
2013
+ type = 'future'
2014
+ elif self.isInverse(type, subType):
2015
+ type = 'delivery'
2016
+ url = self.urls['api']['ws'][urlType] + '/' + self.options[type]['listenKey']
2017
+ client = self.client(url)
2018
+ self.set_balance_cache(client, type, isPortfolioMargin)
2019
+ self.set_positions_cache(client, type, None, isPortfolioMargin)
2020
+ options = self.safe_dict(self.options, 'watchBalance')
2021
+ fetchBalanceSnapshot = self.safe_bool(options, 'fetchBalanceSnapshot', False)
2022
+ awaitBalanceSnapshot = self.safe_bool(options, 'awaitBalanceSnapshot', True)
2023
+ if fetchBalanceSnapshot and awaitBalanceSnapshot:
2024
+ await client.future(type + ':fetchBalanceSnapshot')
2025
+ messageHash = type + ':balance'
2026
+ message = None
2027
+ return await self.watch(url, messageHash, message, type)
2028
+
2029
+ def handle_balance(self, client: Client, message):
2030
+ #
2031
+ # sent upon a balance update not related to orders
2032
+ #
2033
+ # {
2034
+ # "e": "balanceUpdate",
2035
+ # "E": 1629352505586,
2036
+ # "a": "IOTX",
2037
+ # "d": "0.43750000",
2038
+ # "T": 1629352505585
2039
+ # }
2040
+ #
2041
+ # sent upon creating or filling an order
2042
+ #
2043
+ # {
2044
+ # "e": "outboundAccountPosition", # Event type
2045
+ # "E": 1564034571105, # Event Time
2046
+ # "u": 1564034571073, # Time of last account update
2047
+ # "B": [ # Balances Array
2048
+ # {
2049
+ # "a": "ETH", # Asset
2050
+ # "f": "10000.000000", # Free
2051
+ # "l": "0.000000" # Locked
2052
+ # }
2053
+ # ]
2054
+ # }
2055
+ #
2056
+ # future/delivery
2057
+ #
2058
+ # {
2059
+ # "e": "ACCOUNT_UPDATE", # Event Type
2060
+ # "E": 1564745798939, # Event Time
2061
+ # "T": 1564745798938 , # Transaction
2062
+ # "i": "SfsR", # Account Alias
2063
+ # "a": { # Update Data
2064
+ # "m":"ORDER", # Event reason type
2065
+ # "B":[ # Balances
2066
+ # {
2067
+ # "a":"BTC", # Asset
2068
+ # "wb":"122624.12345678", # Wallet Balance
2069
+ # "cw":"100.12345678" # Cross Wallet Balance
2070
+ # },
2071
+ # ],
2072
+ # "P":[
2073
+ # {
2074
+ # "s":"BTCUSD_200925", # Symbol
2075
+ # "pa":"0", # Position Amount
2076
+ # "ep":"0.0", # Entry Price
2077
+ # "cr":"200", #(Pre-fee) Accumulated Realized
2078
+ # "up":"0", # Unrealized PnL
2079
+ # "mt":"isolated", # Margin Type
2080
+ # "iw":"0.00000000", # Isolated Wallet(if isolated position)
2081
+ # "ps":"BOTH" # Position Side
2082
+ # },
2083
+ # ]
2084
+ # }
2085
+ # }
2086
+ #
2087
+ wallet = self.safe_string(self.options, 'wallet', 'wb') # cw for cross wallet
2088
+ # each account is connected to a different endpoint
2089
+ # and has exactly one subscriptionhash which is the account type
2090
+ subscriptions = list(client.subscriptions.keys())
2091
+ accountType = subscriptions[0]
2092
+ messageHash = accountType + ':balance'
2093
+ if self.balance[accountType] is None:
2094
+ self.balance[accountType] = {}
2095
+ self.balance[accountType]['info'] = message
2096
+ event = self.safe_string(message, 'e')
2097
+ if event == 'balanceUpdate':
2098
+ currencyId = self.safe_string(message, 'a')
2099
+ code = self.safe_currency_code(currencyId)
2100
+ account = self.account()
2101
+ delta = self.safe_string(message, 'd')
2102
+ if code in self.balance[accountType]:
2103
+ previousValue = self.balance[accountType][code]['free']
2104
+ if not isinstance(previousValue, str):
2105
+ previousValue = self.number_to_string(previousValue)
2106
+ account['free'] = Precise.string_add(previousValue, delta)
2107
+ else:
2108
+ account['free'] = delta
2109
+ self.balance[accountType][code] = account
2110
+ else:
2111
+ message = self.safe_dict(message, 'a', message)
2112
+ B = self.safe_list(message, 'B')
2113
+ for i in range(0, len(B)):
2114
+ entry = B[i]
2115
+ currencyId = self.safe_string(entry, 'a')
2116
+ code = self.safe_currency_code(currencyId)
2117
+ account = self.account()
2118
+ account['free'] = self.safe_string(entry, 'f')
2119
+ account['used'] = self.safe_string(entry, 'l')
2120
+ account['total'] = self.safe_string(entry, wallet)
2121
+ self.balance[accountType][code] = account
2122
+ timestamp = self.safe_integer(message, 'E')
2123
+ self.balance[accountType]['timestamp'] = timestamp
2124
+ self.balance[accountType]['datetime'] = self.iso8601(timestamp)
2125
+ self.balance[accountType] = self.safe_balance(self.balance[accountType])
2126
+ client.resolve(self.balance[accountType], messageHash)
2127
+
2128
+ def get_market_type(self, method, market, params={}):
2129
+ type = None
2130
+ type, params = self.handle_market_type_and_params(method, market, params)
2131
+ subType = None
2132
+ subType, params = self.handle_sub_type_and_params(method, market, params)
2133
+ if self.isLinear(type, subType):
2134
+ type = 'future'
2135
+ elif self.isInverse(type, subType):
2136
+ type = 'delivery'
2137
+ return type
2138
+
2139
+ async def create_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}) -> Order:
2140
+ """
2141
+ :see: https://binance-docs.github.io/apidocs/websocket_api/en/#place-new-order-trade
2142
+ :see: https://binance-docs.github.io/apidocs/futures/en/#new-order-trade-2
2143
+ create a trade order
2144
+ :param str symbol: unified symbol of the market to create an order in
2145
+ :param str type: 'market' or 'limit'
2146
+ :param str side: 'buy' or 'sell'
2147
+ :param float amount: how much of currency you want to trade in units of base currency
2148
+ :param float|None [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
2149
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2150
+ :param boolean params['test']: test order, default False
2151
+ :param boolean params['returnRateLimits']: set to True to return rate limit information, default False
2152
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2153
+ """
2154
+ await self.load_markets()
2155
+ market = self.market(symbol)
2156
+ marketType = self.get_market_type('createOrderWs', market, params)
2157
+ if marketType != 'spot' and marketType != 'future':
2158
+ raise BadRequest(self.id + ' createOrderWs only supports spot or swap markets')
2159
+ url = self.urls['api']['ws']['ws-api'][marketType]
2160
+ requestId = self.request_id(url)
2161
+ messageHash = str(requestId)
2162
+ sor = self.safe_bool_2(params, 'sor', 'SOR', False)
2163
+ params = self.omit(params, 'sor', 'SOR')
2164
+ payload = self.create_order_request(symbol, type, side, amount, price, params)
2165
+ returnRateLimits = False
2166
+ returnRateLimits, params = self.handle_option_and_params(params, 'createOrderWs', 'returnRateLimits', False)
2167
+ payload['returnRateLimits'] = returnRateLimits
2168
+ test = self.safe_bool(params, 'test', False)
2169
+ params = self.omit(params, 'test')
2170
+ message: dict = {
2171
+ 'id': messageHash,
2172
+ 'method': 'order.place',
2173
+ 'params': self.sign_params(self.extend(payload, params)),
2174
+ }
2175
+ if test:
2176
+ if sor:
2177
+ message['method'] = 'sor.order.test'
2178
+ else:
2179
+ message['method'] = 'order.test'
2180
+ subscription: dict = {
2181
+ 'method': self.handle_order_ws,
2182
+ }
2183
+ return await self.watch(url, messageHash, message, messageHash, subscription)
2184
+
2185
+ def handle_order_ws(self, client: Client, message):
2186
+ #
2187
+ # {
2188
+ # "id": 1,
2189
+ # "status": 200,
2190
+ # "result": {
2191
+ # "symbol": "BTCUSDT",
2192
+ # "orderId": 7663053,
2193
+ # "orderListId": -1,
2194
+ # "clientOrderId": "x-R4BD3S82d8959d0f5114499487a614",
2195
+ # "transactTime": 1687642291434,
2196
+ # "price": "25000.00000000",
2197
+ # "origQty": "0.00100000",
2198
+ # "executedQty": "0.00000000",
2199
+ # "cummulativeQuoteQty": "0.00000000",
2200
+ # "status": "NEW",
2201
+ # "timeInForce": "GTC",
2202
+ # "type": "LIMIT",
2203
+ # "side": "BUY",
2204
+ # "workingTime": 1687642291434,
2205
+ # "fills": [],
2206
+ # "selfTradePreventionMode": "NONE"
2207
+ # },
2208
+ # "rateLimits": [
2209
+ # {
2210
+ # "rateLimitType": "ORDERS",
2211
+ # "interval": "SECOND",
2212
+ # "intervalNum": 10,
2213
+ # "limit": 50,
2214
+ # "count": 1
2215
+ # },
2216
+ # {
2217
+ # "rateLimitType": "ORDERS",
2218
+ # "interval": "DAY",
2219
+ # "intervalNum": 1,
2220
+ # "limit": 160000,
2221
+ # "count": 1
2222
+ # },
2223
+ # {
2224
+ # "rateLimitType": "REQUEST_WEIGHT",
2225
+ # "interval": "MINUTE",
2226
+ # "intervalNum": 1,
2227
+ # "limit": 1200,
2228
+ # "count": 12
2229
+ # }
2230
+ # ]
2231
+ # }
2232
+ #
2233
+ messageHash = self.safe_string(message, 'id')
2234
+ result = self.safe_dict(message, 'result', {})
2235
+ order = self.parse_order(result)
2236
+ client.resolve(order, messageHash)
2237
+
2238
+ def handle_orders_ws(self, client: Client, message):
2239
+ #
2240
+ # {
2241
+ # "id": 1,
2242
+ # "status": 200,
2243
+ # "result": [{
2244
+ # "symbol": "BTCUSDT",
2245
+ # "orderId": 7665584,
2246
+ # "orderListId": -1,
2247
+ # "clientOrderId": "x-R4BD3S82b54769abdd3e4b57874c52",
2248
+ # "price": "26000.00000000",
2249
+ # "origQty": "0.00100000",
2250
+ # "executedQty": "0.00000000",
2251
+ # "cummulativeQuoteQty": "0.00000000",
2252
+ # "status": "NEW",
2253
+ # "timeInForce": "GTC",
2254
+ # "type": "LIMIT",
2255
+ # "side": "BUY",
2256
+ # "stopPrice": "0.00000000",
2257
+ # "icebergQty": "0.00000000",
2258
+ # "time": 1687642884646,
2259
+ # "updateTime": 1687642884646,
2260
+ # "isWorking": True,
2261
+ # "workingTime": 1687642884646,
2262
+ # "origQuoteOrderQty": "0.00000000",
2263
+ # "selfTradePreventionMode": "NONE"
2264
+ # },
2265
+ # ...
2266
+ # ],
2267
+ # "rateLimits": [{
2268
+ # "rateLimitType": "REQUEST_WEIGHT",
2269
+ # "interval": "MINUTE",
2270
+ # "intervalNum": 1,
2271
+ # "limit": 1200,
2272
+ # "count": 14
2273
+ # }]
2274
+ # }
2275
+ #
2276
+ messageHash = self.safe_string(message, 'id')
2277
+ result = self.safe_list(message, 'result', [])
2278
+ orders = self.parse_orders(result)
2279
+ client.resolve(orders, messageHash)
2280
+
2281
+ async def edit_order_ws(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}) -> Order:
2282
+ """
2283
+ edit a trade order
2284
+ :see: https://binance-docs.github.io/apidocs/websocket_api/en/#cancel-and-replace-order-trade
2285
+ :see: https://binance-docs.github.io/apidocs/futures/en/#modify-order-trade-2
2286
+ :param str id: order id
2287
+ :param str symbol: unified symbol of the market to create an order in
2288
+ :param str type: 'market' or 'limit'
2289
+ :param str side: 'buy' or 'sell'
2290
+ :param float amount: how much of the currency you want to trade in units of the base currency
2291
+ :param float|None [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
2292
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2293
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2294
+ """
2295
+ await self.load_markets()
2296
+ market = self.market(symbol)
2297
+ marketType = self.get_market_type('editOrderWs', market, params)
2298
+ if marketType != 'spot' and marketType != 'future':
2299
+ raise BadRequest(self.id + ' editOrderWs only supports spot or swap markets')
2300
+ url = self.urls['api']['ws']['ws-api'][marketType]
2301
+ requestId = self.request_id(url)
2302
+ messageHash = str(requestId)
2303
+ payload = None
2304
+ if marketType == 'spot':
2305
+ payload = self.editSpotOrderRequest(id, symbol, type, side, amount, price, params)
2306
+ elif marketType == 'future':
2307
+ payload = self.editContractOrderRequest(id, symbol, type, side, amount, price, params)
2308
+ returnRateLimits = False
2309
+ returnRateLimits, params = self.handle_option_and_params(params, 'editOrderWs', 'returnRateLimits', False)
2310
+ payload['returnRateLimits'] = returnRateLimits
2311
+ message: dict = {
2312
+ 'id': messageHash,
2313
+ 'method': 'order.modify' if (marketType == 'future') else 'order.cancelReplace',
2314
+ 'params': self.sign_params(self.extend(payload, params)),
2315
+ }
2316
+ subscription: dict = {
2317
+ 'method': self.handle_edit_order_ws,
2318
+ }
2319
+ return await self.watch(url, messageHash, message, messageHash, subscription)
2320
+
2321
+ def handle_edit_order_ws(self, client: Client, message):
2322
+ #
2323
+ # spot
2324
+ # {
2325
+ # "id": 1,
2326
+ # "status": 200,
2327
+ # "result": {
2328
+ # "cancelResult": "SUCCESS",
2329
+ # "newOrderResult": "SUCCESS",
2330
+ # "cancelResponse": {
2331
+ # "symbol": "BTCUSDT",
2332
+ # "origClientOrderId": "x-R4BD3S82813c5d7ffa594104917de2",
2333
+ # "orderId": 7665177,
2334
+ # "orderListId": -1,
2335
+ # "clientOrderId": "mbrnbQsQhtCXCLY45d5q7S",
2336
+ # "price": "26000.00000000",
2337
+ # "origQty": "0.00100000",
2338
+ # "executedQty": "0.00000000",
2339
+ # "cummulativeQuoteQty": "0.00000000",
2340
+ # "status": "CANCELED",
2341
+ # "timeInForce": "GTC",
2342
+ # "type": "LIMIT",
2343
+ # "side": "BUY",
2344
+ # "selfTradePreventionMode": "NONE"
2345
+ # },
2346
+ # "newOrderResponse": {
2347
+ # "symbol": "BTCUSDT",
2348
+ # "orderId": 7665584,
2349
+ # "orderListId": -1,
2350
+ # "clientOrderId": "x-R4BD3S82b54769abdd3e4b57874c52",
2351
+ # "transactTime": 1687642884646,
2352
+ # "price": "26000.00000000",
2353
+ # "origQty": "0.00100000",
2354
+ # "executedQty": "0.00000000",
2355
+ # "cummulativeQuoteQty": "0.00000000",
2356
+ # "status": "NEW",
2357
+ # "timeInForce": "GTC",
2358
+ # "type": "LIMIT",
2359
+ # "side": "BUY",
2360
+ # "workingTime": 1687642884646,
2361
+ # "fills": [],
2362
+ # "selfTradePreventionMode": "NONE"
2363
+ # }
2364
+ # },
2365
+ # "rateLimits": [{
2366
+ # "rateLimitType": "ORDERS",
2367
+ # "interval": "SECOND",
2368
+ # "intervalNum": 10,
2369
+ # "limit": 50,
2370
+ # "count": 1
2371
+ # },
2372
+ # {
2373
+ # "rateLimitType": "ORDERS",
2374
+ # "interval": "DAY",
2375
+ # "intervalNum": 1,
2376
+ # "limit": 160000,
2377
+ # "count": 3
2378
+ # },
2379
+ # {
2380
+ # "rateLimitType": "REQUEST_WEIGHT",
2381
+ # "interval": "MINUTE",
2382
+ # "intervalNum": 1,
2383
+ # "limit": 1200,
2384
+ # "count": 12
2385
+ # }
2386
+ # ]
2387
+ # }
2388
+ # swap
2389
+ # {
2390
+ # "id":"1",
2391
+ # "status":200,
2392
+ # "result":{
2393
+ # "orderId":667061487,
2394
+ # "symbol":"LTCUSDT",
2395
+ # "status":"NEW",
2396
+ # "clientOrderId":"x-xcKtGhcu91a74c818749ee42c0f70",
2397
+ # "price":"82.00",
2398
+ # "avgPrice":"0.00",
2399
+ # "origQty":"1.000",
2400
+ # "executedQty":"0.000",
2401
+ # "cumQty":"0.000",
2402
+ # "cumQuote":"0.00000",
2403
+ # "timeInForce":"GTC",
2404
+ # "type":"LIMIT",
2405
+ # "reduceOnly":false,
2406
+ # "closePosition":false,
2407
+ # "side":"BUY",
2408
+ # "positionSide":"BOTH",
2409
+ # "stopPrice":"0.00",
2410
+ # "workingType":"CONTRACT_PRICE",
2411
+ # "priceProtect":false,
2412
+ # "origType":"LIMIT",
2413
+ # "priceMatch":"NONE",
2414
+ # "selfTradePreventionMode":"NONE",
2415
+ # "goodTillDate":0,
2416
+ # "updateTime":1712918927511
2417
+ # }
2418
+ # }
2419
+ #
2420
+ messageHash = self.safe_string(message, 'id')
2421
+ result = self.safe_dict(message, 'result', {})
2422
+ newSpotOrder = self.safe_dict(result, 'newOrderResponse')
2423
+ order = None
2424
+ if newSpotOrder is not None:
2425
+ order = self.parse_order(newSpotOrder)
2426
+ else:
2427
+ order = self.parse_order(result)
2428
+ client.resolve(order, messageHash)
2429
+
2430
+ async def cancel_order_ws(self, id: str, symbol: Str = None, params={}) -> Order:
2431
+ """
2432
+ :see: https://binance-docs.github.io/apidocs/websocket_api/en/#cancel-order-trade
2433
+ :see: https://binance-docs.github.io/apidocs/futures/en/#cancel-order-trade-2
2434
+ cancel multiple orders
2435
+ :param str id: order id
2436
+ :param str symbol: unified market symbol, default is None
2437
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2438
+ :param str|None [params.cancelRestrictions]: Supported values: ONLY_NEW - Cancel will succeed if the order status is NEW. ONLY_PARTIALLY_FILLED - Cancel will succeed if order status is PARTIALLY_FILLED.
2439
+ :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2440
+ """
2441
+ await self.load_markets()
2442
+ if symbol is None:
2443
+ raise BadRequest(self.id + ' cancelOrderWs requires a symbol')
2444
+ market = self.market(symbol)
2445
+ type = self.get_market_type('cancelOrderWs', market, params)
2446
+ url = self.urls['api']['ws']['ws-api'][type]
2447
+ requestId = self.request_id(url)
2448
+ messageHash = str(requestId)
2449
+ returnRateLimits = False
2450
+ returnRateLimits, params = self.handle_option_and_params(params, 'cancelOrderWs', 'returnRateLimits', False)
2451
+ payload: dict = {
2452
+ 'symbol': self.market_id(symbol),
2453
+ 'returnRateLimits': returnRateLimits,
2454
+ }
2455
+ clientOrderId = self.safe_string_2(params, 'origClientOrderId', 'clientOrderId')
2456
+ if clientOrderId is not None:
2457
+ payload['origClientOrderId'] = clientOrderId
2458
+ else:
2459
+ payload['orderId'] = self.parse_to_int(id)
2460
+ params = self.omit(params, ['origClientOrderId', 'clientOrderId'])
2461
+ message: dict = {
2462
+ 'id': messageHash,
2463
+ 'method': 'order.cancel',
2464
+ 'params': self.sign_params(self.extend(payload, params)),
2465
+ }
2466
+ subscription: dict = {
2467
+ 'method': self.handle_order_ws,
2468
+ }
2469
+ return await self.watch(url, messageHash, message, messageHash, subscription)
2470
+
2471
+ async def cancel_all_orders_ws(self, symbol: Str = None, params={}):
2472
+ """
2473
+ :see: https://binance-docs.github.io/apidocs/websocket_api/en/#current-open-orders-user_data
2474
+ cancel all open orders in a market
2475
+ :param str symbol: unified market symbol of the market to cancel orders in
2476
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2477
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2478
+ """
2479
+ await self.load_markets()
2480
+ market = self.market(symbol)
2481
+ type = self.get_market_type('cancelAllOrdersWs', market, params)
2482
+ if type != 'spot' and type != 'future':
2483
+ raise BadRequest(self.id + ' cancelAllOrdersWs only supports spot or swap markets')
2484
+ url = self.urls['api']['ws']['ws-api'][type]
2485
+ requestId = self.request_id(url)
2486
+ messageHash = str(requestId)
2487
+ returnRateLimits = False
2488
+ returnRateLimits, params = self.handle_option_and_params(params, 'cancelAllOrdersWs', 'returnRateLimits', False)
2489
+ payload: dict = {
2490
+ 'symbol': self.market_id(symbol),
2491
+ 'returnRateLimits': returnRateLimits,
2492
+ }
2493
+ message: dict = {
2494
+ 'id': messageHash,
2495
+ 'method': 'order.cancel',
2496
+ 'params': self.sign_params(self.extend(payload, params)),
2497
+ }
2498
+ subscription: dict = {
2499
+ 'method': self.handle_orders_ws,
2500
+ }
2501
+ return await self.watch(url, messageHash, message, messageHash, subscription)
2502
+
2503
+ async def fetch_order_ws(self, id: str, symbol: Str = None, params={}) -> Order:
2504
+ """
2505
+ :see: https://binance-docs.github.io/apidocs/websocket_api/en/#query-order-user_data
2506
+ :see: https://binance-docs.github.io/apidocs/futures/en/#query-order-user_data-2
2507
+ fetches information on an order made by the user
2508
+ :param str symbol: unified symbol of the market the order was made in
2509
+ :param dict params: extra parameters specific to the exchange API endpoint
2510
+ :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2511
+ """
2512
+ await self.load_markets()
2513
+ if symbol is None:
2514
+ raise BadRequest(self.id + ' cancelOrderWs requires a symbol')
2515
+ market = self.market(symbol)
2516
+ type = self.get_market_type('fetchOrderWs', market, params)
2517
+ if type != 'spot' and type != 'future':
2518
+ raise BadRequest(self.id + ' fetchOrderWs only supports spot or swap markets')
2519
+ url = self.urls['api']['ws']['ws-api'][type]
2520
+ requestId = self.request_id(url)
2521
+ messageHash = str(requestId)
2522
+ returnRateLimits = False
2523
+ returnRateLimits, params = self.handle_option_and_params(params, 'fetchOrderWs', 'returnRateLimits', False)
2524
+ payload: dict = {
2525
+ 'symbol': self.market_id(symbol),
2526
+ 'returnRateLimits': returnRateLimits,
2527
+ }
2528
+ clientOrderId = self.safe_string_2(params, 'origClientOrderId', 'clientOrderId')
2529
+ if clientOrderId is not None:
2530
+ payload['origClientOrderId'] = clientOrderId
2531
+ else:
2532
+ payload['orderId'] = self.parse_to_int(id)
2533
+ message: dict = {
2534
+ 'id': messageHash,
2535
+ 'method': 'order.status',
2536
+ 'params': self.sign_params(self.extend(payload, params)),
2537
+ }
2538
+ subscription: dict = {
2539
+ 'method': self.handle_order_ws,
2540
+ }
2541
+ return await self.watch(url, messageHash, message, messageHash, subscription)
2542
+
2543
+ async def fetch_orders_ws(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
2544
+ """
2545
+ :see: https://binance-docs.github.io/apidocs/websocket_api/en/#account-order-history-user_data
2546
+ fetches information on multiple orders made by the user
2547
+ :param str symbol: unified market symbol of the market orders were made in
2548
+ :param int|None [since]: the earliest time in ms to fetch orders for
2549
+ :param int|None [limit]: the maximum number of order structures to retrieve
2550
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2551
+ :param int [params.orderId]: order id to begin at
2552
+ :param int [params.startTime]: earliest time in ms to retrieve orders for
2553
+ :param int [params.endTime]: latest time in ms to retrieve orders for
2554
+ :param int [params.limit]: the maximum number of order structures to retrieve
2555
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2556
+ """
2557
+ await self.load_markets()
2558
+ if symbol is None:
2559
+ raise BadRequest(self.id + ' fetchOrdersWs requires a symbol')
2560
+ market = self.market(symbol)
2561
+ type = self.get_market_type('fetchOrdersWs', market, params)
2562
+ if type != 'spot':
2563
+ raise BadRequest(self.id + ' fetchOrdersWs only supports spot markets')
2564
+ url = self.urls['api']['ws']['ws-api'][type]
2565
+ requestId = self.request_id(url)
2566
+ messageHash = str(requestId)
2567
+ returnRateLimits = False
2568
+ returnRateLimits, params = self.handle_option_and_params(params, 'fetchOrderWs', 'returnRateLimits', False)
2569
+ payload: dict = {
2570
+ 'symbol': self.market_id(symbol),
2571
+ 'returnRateLimits': returnRateLimits,
2572
+ }
2573
+ message: dict = {
2574
+ 'id': messageHash,
2575
+ 'method': 'allOrders',
2576
+ 'params': self.sign_params(self.extend(payload, params)),
2577
+ }
2578
+ subscription: dict = {
2579
+ 'method': self.handle_orders_ws,
2580
+ }
2581
+ orders = await self.watch(url, messageHash, message, messageHash, subscription)
2582
+ return self.filter_by_symbol_since_limit(orders, symbol, since, limit)
2583
+
2584
+ async def fetch_closed_orders_ws(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
2585
+ """
2586
+ :see: https://binance-docs.github.io/apidocs/websocket_api/en/#account-order-history-user_data
2587
+ fetch closed orders
2588
+ :param str symbol: unified market symbol
2589
+ :param int [since]: the earliest time in ms to fetch open orders for
2590
+ :param int [limit]: the maximum number of open orders structures to retrieve
2591
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2592
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2593
+ """
2594
+ orders = await self.fetch_orders_ws(symbol, since, limit, params)
2595
+ closedOrders = []
2596
+ for i in range(0, len(orders)):
2597
+ order = orders[i]
2598
+ if order['status'] == 'closed':
2599
+ closedOrders.append(order)
2600
+ return closedOrders
2601
+
2602
+ async def fetch_open_orders_ws(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
2603
+ """
2604
+ :see: https://binance-docs.github.io/apidocs/websocket_api/en/#current-open-orders-user_data
2605
+ fetch all unfilled currently open orders
2606
+ :param str symbol: unified market symbol
2607
+ :param int|None [since]: the earliest time in ms to fetch open orders for
2608
+ :param int|None [limit]: the maximum number of open orders structures to retrieve
2609
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2610
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2611
+ """
2612
+ await self.load_markets()
2613
+ market = self.market(symbol)
2614
+ type = self.get_market_type('fetchOpenOrdersWs', market, params)
2615
+ if type != 'spot' and type != 'future':
2616
+ raise BadRequest(self.id + ' fetchOpenOrdersWs only supports spot or swap markets')
2617
+ url = self.urls['api']['ws']['ws-api'][type]
2618
+ requestId = self.request_id(url)
2619
+ messageHash = str(requestId)
2620
+ returnRateLimits = False
2621
+ returnRateLimits, params = self.handle_option_and_params(params, 'fetchOrderWs', 'returnRateLimits', False)
2622
+ payload: dict = {
2623
+ 'returnRateLimits': returnRateLimits,
2624
+ }
2625
+ if symbol is not None:
2626
+ payload['symbol'] = self.market_id(symbol)
2627
+ message: dict = {
2628
+ 'id': messageHash,
2629
+ 'method': 'openOrders.status',
2630
+ 'params': self.sign_params(self.extend(payload, params)),
2631
+ }
2632
+ subscription: dict = {
2633
+ 'method': self.handle_orders_ws,
2634
+ }
2635
+ orders = await self.watch(url, messageHash, message, messageHash, subscription)
2636
+ return self.filter_by_symbol_since_limit(orders, symbol, since, limit)
2637
+
2638
+ async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
2639
+ """
2640
+ watches information on multiple orders made by the user
2641
+ :see: https://binance-docs.github.io/apidocs/spot/en/#payload-order-update
2642
+ :see: https://binance-docs.github.io/apidocs/pm/en/#event-futures-order-update
2643
+ :see: https://binance-docs.github.io/apidocs/pm/en/#event-margin-order-update
2644
+ :param str symbol: unified market symbol of the market the orders were made in
2645
+ :param int [since]: the earliest time in ms to fetch orders for
2646
+ :param int [limit]: the maximum number of order structures to retrieve
2647
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2648
+ :param str|None [params.marginMode]: 'cross' or 'isolated', for spot margin
2649
+ :param boolean [params.portfolioMargin]: set to True if you would like to watch portfolio margin account orders
2650
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2651
+ """
2652
+ await self.load_markets()
2653
+ messageHash = 'orders'
2654
+ market = None
2655
+ if symbol is not None:
2656
+ market = self.market(symbol)
2657
+ symbol = market['symbol']
2658
+ messageHash += ':' + symbol
2659
+ type = None
2660
+ type, params = self.handle_market_type_and_params('watchOrders', market, params)
2661
+ subType = None
2662
+ subType, params = self.handle_sub_type_and_params('watchOrders', market, params)
2663
+ if self.isLinear(type, subType):
2664
+ type = 'future'
2665
+ elif self.isInverse(type, subType):
2666
+ type = 'delivery'
2667
+ params = self.extend(params, {'type': type, 'symbol': symbol}) # needed inside authenticate for isolated margin
2668
+ await self.authenticate(params)
2669
+ marginMode = None
2670
+ marginMode, params = self.handle_margin_mode_and_params('watchOrders', params)
2671
+ urlType = type
2672
+ if (type == 'margin') or ((type == 'spot') and (marginMode is not None)):
2673
+ urlType = 'spot' # spot-margin shares the same stream spot
2674
+ isPortfolioMargin = None
2675
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'watchOrders', 'papi', 'portfolioMargin', False)
2676
+ if isPortfolioMargin:
2677
+ urlType = 'papi'
2678
+ url = self.urls['api']['ws'][urlType] + '/' + self.options[type]['listenKey']
2679
+ client = self.client(url)
2680
+ self.set_balance_cache(client, type, isPortfolioMargin)
2681
+ self.set_positions_cache(client, type, None, isPortfolioMargin)
2682
+ message = None
2683
+ orders = await self.watch(url, messageHash, message, type)
2684
+ if self.newUpdates:
2685
+ limit = orders.getLimit(symbol, limit)
2686
+ return self.filter_by_symbol_since_limit(orders, symbol, since, limit, True)
2687
+
2688
+ def parse_ws_order(self, order, market=None):
2689
+ #
2690
+ # spot
2691
+ #
2692
+ # {
2693
+ # "e": "executionReport", # Event type
2694
+ # "E": 1499405658658, # Event time
2695
+ # "s": "ETHBTC", # Symbol
2696
+ # "c": "mUvoqJxFIILMdfAW5iGSOW", # Client order ID
2697
+ # "S": "BUY", # Side
2698
+ # "o": "LIMIT", # Order type
2699
+ # "f": "GTC", # Time in force
2700
+ # "q": "1.00000000", # Order quantity
2701
+ # "p": "0.10264410", # Order price
2702
+ # "P": "0.00000000", # Stop price
2703
+ # "F": "0.00000000", # Iceberg quantity
2704
+ # "g": -1, # OrderListId
2705
+ # "C": null, # Original client order ID; This is the ID of the order being canceled
2706
+ # "x": "NEW", # Current execution type
2707
+ # "X": "NEW", # Current order status
2708
+ # "r": "NONE", # Order reject reason; will be an error code.
2709
+ # "i": 4293153, # Order ID
2710
+ # "l": "0.00000000", # Last executed quantity
2711
+ # "z": "0.00000000", # Cumulative filled quantity
2712
+ # "L": "0.00000000", # Last executed price
2713
+ # "n": "0", # Commission amount
2714
+ # "N": null, # Commission asset
2715
+ # "T": 1499405658657, # Transaction time
2716
+ # "t": -1, # Trade ID
2717
+ # "I": 8641984, # Ignore
2718
+ # "w": True, # Is the order on the book?
2719
+ # "m": False, # Is self trade the maker side?
2720
+ # "M": False, # Ignore
2721
+ # "O": 1499405658657, # Order creation time
2722
+ # "Z": "0.00000000", # Cumulative quote asset transacted quantity
2723
+ # "Y": "0.00000000" # Last quote asset transacted quantity(i.e. lastPrice * lastQty),
2724
+ # "Q": "0.00000000" # Quote Order Qty
2725
+ # }
2726
+ #
2727
+ # future
2728
+ #
2729
+ # {
2730
+ # "s":"BTCUSDT", # Symbol
2731
+ # "c":"TEST", # Client Order Id
2732
+ # # special client order id:
2733
+ # # starts with "autoclose-": liquidation order
2734
+ # # "adl_autoclose": ADL auto close order
2735
+ # "S":"SELL", # Side
2736
+ # "o":"TRAILING_STOP_MARKET", # Order Type
2737
+ # "f":"GTC", # Time in Force
2738
+ # "q":"0.001", # Original Quantity
2739
+ # "p":"0", # Original Price
2740
+ # "ap":"0", # Average Price
2741
+ # "sp":"7103.04", # Stop Price. Please ignore with TRAILING_STOP_MARKET order
2742
+ # "x":"NEW", # Execution Type
2743
+ # "X":"NEW", # Order Status
2744
+ # "i":8886774, # Order Id
2745
+ # "l":"0", # Order Last Filled Quantity
2746
+ # "z":"0", # Order Filled Accumulated Quantity
2747
+ # "L":"0", # Last Filled Price
2748
+ # "N":"USDT", # Commission Asset, will not push if no commission
2749
+ # "n":"0", # Commission, will not push if no commission
2750
+ # "T":1568879465651, # Order Trade Time
2751
+ # "t":0, # Trade Id
2752
+ # "b":"0", # Bids Notional
2753
+ # "a":"9.91", # Ask Notional
2754
+ # "m":false, # Is self trade the maker side?
2755
+ # "R":false, # Is self reduce only
2756
+ # "wt":"CONTRACT_PRICE", # Stop Price Working Type
2757
+ # "ot":"TRAILING_STOP_MARKET", # Original Order Type
2758
+ # "ps":"LONG", # Position Side
2759
+ # "cp":false, # If Close-All, pushed with conditional order
2760
+ # "AP":"7476.89", # Activation Price, only puhed with TRAILING_STOP_MARKET order
2761
+ # "cr":"5.0", # Callback Rate, only puhed with TRAILING_STOP_MARKET order
2762
+ # "rp":"0" # Realized Profit of the trade
2763
+ # }
2764
+ #
2765
+ executionType = self.safe_string(order, 'x')
2766
+ orderId = self.safe_string(order, 'i')
2767
+ marketId = self.safe_string(order, 's')
2768
+ marketType = 'contract' if ('ps' in order) else 'spot'
2769
+ symbol = self.safe_symbol(marketId, None, None, marketType)
2770
+ timestamp = self.safe_integer(order, 'O')
2771
+ T = self.safe_integer(order, 'T')
2772
+ lastTradeTimestamp = None
2773
+ if executionType == 'NEW' or executionType == 'AMENDMENT' or executionType == 'CANCELED':
2774
+ if timestamp is None:
2775
+ timestamp = T
2776
+ elif executionType == 'TRADE':
2777
+ lastTradeTimestamp = T
2778
+ lastUpdateTimestamp = T
2779
+ fee = None
2780
+ feeCost = self.safe_string(order, 'n')
2781
+ if (feeCost is not None) and (Precise.string_gt(feeCost, '0')):
2782
+ feeCurrencyId = self.safe_string(order, 'N')
2783
+ feeCurrency = self.safe_currency_code(feeCurrencyId)
2784
+ fee = {
2785
+ 'cost': feeCost,
2786
+ 'currency': feeCurrency,
2787
+ }
2788
+ price = self.safe_string(order, 'p')
2789
+ amount = self.safe_string(order, 'q')
2790
+ side = self.safe_string_lower(order, 'S')
2791
+ type = self.safe_string_lower(order, 'o')
2792
+ filled = self.safe_string(order, 'z')
2793
+ cost = self.safe_string(order, 'Z')
2794
+ average = self.safe_string(order, 'ap')
2795
+ rawStatus = self.safe_string(order, 'X')
2796
+ status = self.parse_order_status(rawStatus)
2797
+ trades = None
2798
+ clientOrderId = self.safe_string(order, 'C')
2799
+ if (clientOrderId is None) or (len(clientOrderId) == 0):
2800
+ clientOrderId = self.safe_string(order, 'c')
2801
+ stopPrice = self.safe_string_2(order, 'P', 'sp')
2802
+ timeInForce = self.safe_string(order, 'f')
2803
+ if timeInForce == 'GTX':
2804
+ # GTX means "Good Till Crossing" and is an equivalent way of saying Post Only
2805
+ timeInForce = 'PO'
2806
+ return self.safe_order({
2807
+ 'info': order,
2808
+ 'symbol': symbol,
2809
+ 'id': orderId,
2810
+ 'clientOrderId': clientOrderId,
2811
+ 'timestamp': timestamp,
2812
+ 'datetime': self.iso8601(timestamp),
2813
+ 'lastTradeTimestamp': lastTradeTimestamp,
2814
+ 'lastUpdateTimestamp': lastUpdateTimestamp,
2815
+ 'type': type,
2816
+ 'timeInForce': timeInForce,
2817
+ 'postOnly': None,
2818
+ 'reduceOnly': self.safe_bool(order, 'R'),
2819
+ 'side': side,
2820
+ 'price': price,
2821
+ 'stopPrice': stopPrice,
2822
+ 'triggerPrice': stopPrice,
2823
+ 'amount': amount,
2824
+ 'cost': cost,
2825
+ 'average': average,
2826
+ 'filled': filled,
2827
+ 'remaining': None,
2828
+ 'status': status,
2829
+ 'fee': fee,
2830
+ 'trades': trades,
2831
+ })
2832
+
2833
+ def handle_order_update(self, client: Client, message):
2834
+ #
2835
+ # spot
2836
+ #
2837
+ # {
2838
+ # "e": "executionReport", # Event type
2839
+ # "E": 1499405658658, # Event time
2840
+ # "s": "ETHBTC", # Symbol
2841
+ # "c": "mUvoqJxFIILMdfAW5iGSOW", # Client order ID
2842
+ # "S": "BUY", # Side
2843
+ # "o": "LIMIT", # Order type
2844
+ # "f": "GTC", # Time in force
2845
+ # "q": "1.00000000", # Order quantity
2846
+ # "p": "0.10264410", # Order price
2847
+ # "P": "0.00000000", # Stop price
2848
+ # "F": "0.00000000", # Iceberg quantity
2849
+ # "g": -1, # OrderListId
2850
+ # "C": null, # Original client order ID; This is the ID of the order being canceled
2851
+ # "x": "NEW", # Current execution type
2852
+ # "X": "NEW", # Current order status
2853
+ # "r": "NONE", # Order reject reason; will be an error code.
2854
+ # "i": 4293153, # Order ID
2855
+ # "l": "0.00000000", # Last executed quantity
2856
+ # "z": "0.00000000", # Cumulative filled quantity
2857
+ # "L": "0.00000000", # Last executed price
2858
+ # "n": "0", # Commission amount
2859
+ # "N": null, # Commission asset
2860
+ # "T": 1499405658657, # Transaction time
2861
+ # "t": -1, # Trade ID
2862
+ # "I": 8641984, # Ignore
2863
+ # "w": True, # Is the order on the book?
2864
+ # "m": False, # Is self trade the maker side?
2865
+ # "M": False, # Ignore
2866
+ # "O": 1499405658657, # Order creation time
2867
+ # "Z": "0.00000000", # Cumulative quote asset transacted quantity
2868
+ # "Y": "0.00000000" # Last quote asset transacted quantity(i.e. lastPrice * lastQty),
2869
+ # "Q": "0.00000000" # Quote Order Qty
2870
+ # }
2871
+ #
2872
+ # future
2873
+ #
2874
+ # {
2875
+ # "e":"ORDER_TRADE_UPDATE", # Event Type
2876
+ # "E":1568879465651, # Event Time
2877
+ # "T":1568879465650, # Trasaction Time
2878
+ # "o": {
2879
+ # "s":"BTCUSDT", # Symbol
2880
+ # "c":"TEST", # Client Order Id
2881
+ # # special client order id:
2882
+ # # starts with "autoclose-": liquidation order
2883
+ # # "adl_autoclose": ADL auto close order
2884
+ # "S":"SELL", # Side
2885
+ # "o":"TRAILING_STOP_MARKET", # Order Type
2886
+ # "f":"GTC", # Time in Force
2887
+ # "q":"0.001", # Original Quantity
2888
+ # "p":"0", # Original Price
2889
+ # "ap":"0", # Average Price
2890
+ # "sp":"7103.04", # Stop Price. Please ignore with TRAILING_STOP_MARKET order
2891
+ # "x":"NEW", # Execution Type
2892
+ # "X":"NEW", # Order Status
2893
+ # "i":8886774, # Order Id
2894
+ # "l":"0", # Order Last Filled Quantity
2895
+ # "z":"0", # Order Filled Accumulated Quantity
2896
+ # "L":"0", # Last Filled Price
2897
+ # "N":"USDT", # Commission Asset, will not push if no commission
2898
+ # "n":"0", # Commission, will not push if no commission
2899
+ # "T":1568879465651, # Order Trade Time
2900
+ # "t":0, # Trade Id
2901
+ # "b":"0", # Bids Notional
2902
+ # "a":"9.91", # Ask Notional
2903
+ # "m":false, # Is self trade the maker side?
2904
+ # "R":false, # Is self reduce only
2905
+ # "wt":"CONTRACT_PRICE", # Stop Price Working Type
2906
+ # "ot":"TRAILING_STOP_MARKET", # Original Order Type
2907
+ # "ps":"LONG", # Position Side
2908
+ # "cp":false, # If Close-All, pushed with conditional order
2909
+ # "AP":"7476.89", # Activation Price, only puhed with TRAILING_STOP_MARKET order
2910
+ # "cr":"5.0", # Callback Rate, only puhed with TRAILING_STOP_MARKET order
2911
+ # "rp":"0" # Realized Profit of the trade
2912
+ # }
2913
+ # }
2914
+ #
2915
+ e = self.safe_string(message, 'e')
2916
+ if e == 'ORDER_TRADE_UPDATE':
2917
+ message = self.safe_dict(message, 'o', message)
2918
+ self.handle_my_trade(client, message)
2919
+ self.handle_order(client, message)
2920
+ self.handle_my_liquidation(client, message)
2921
+
2922
+ async def watch_positions(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]:
2923
+ """
2924
+ watch all open positions
2925
+ :param str[]|None symbols: list of unified market symbols
2926
+ :param dict params: extra parameters specific to the exchange API endpoint
2927
+ :param boolean [params.portfolioMargin]: set to True if you would like to watch positions in a portfolio margin account
2928
+ :returns dict[]: a list of `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
2929
+ """
2930
+ await self.load_markets()
2931
+ market = None
2932
+ messageHash = ''
2933
+ symbols = self.market_symbols(symbols)
2934
+ if not self.is_empty(symbols):
2935
+ market = self.get_market_from_symbols(symbols)
2936
+ messageHash = '::' + ','.join(symbols)
2937
+ marketTypeObject: dict = {}
2938
+ if market is not None:
2939
+ marketTypeObject['type'] = market['type']
2940
+ marketTypeObject['subType'] = market['subType']
2941
+ await self.authenticate(self.extend(marketTypeObject, params))
2942
+ type = None
2943
+ type, params = self.handle_market_type_and_params('watchPositions', market, params)
2944
+ if type == 'spot' or type == 'margin':
2945
+ type = 'future'
2946
+ subType = None
2947
+ subType, params = self.handle_sub_type_and_params('watchPositions', market, params)
2948
+ if self.isLinear(type, subType):
2949
+ type = 'future'
2950
+ elif self.isInverse(type, subType):
2951
+ type = 'delivery'
2952
+ messageHash = type + ':positions' + messageHash
2953
+ isPortfolioMargin = None
2954
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'watchPositions', 'papi', 'portfolioMargin', False)
2955
+ urlType = type
2956
+ if isPortfolioMargin:
2957
+ urlType = 'papi'
2958
+ url = self.urls['api']['ws'][urlType] + '/' + self.options[type]['listenKey']
2959
+ client = self.client(url)
2960
+ self.set_balance_cache(client, type, isPortfolioMargin)
2961
+ self.set_positions_cache(client, type, symbols, isPortfolioMargin)
2962
+ fetchPositionsSnapshot = self.handle_option('watchPositions', 'fetchPositionsSnapshot', True)
2963
+ awaitPositionsSnapshot = self.safe_bool('watchPositions', 'awaitPositionsSnapshot', True)
2964
+ cache = self.safe_value(self.positions, type)
2965
+ if fetchPositionsSnapshot and awaitPositionsSnapshot and cache is None:
2966
+ snapshot = await client.future(type + ':fetchPositionsSnapshot')
2967
+ return self.filter_by_symbols_since_limit(snapshot, symbols, since, limit, True)
2968
+ newPositions = await self.watch(url, messageHash, None, type)
2969
+ if self.newUpdates:
2970
+ return newPositions
2971
+ return self.filter_by_symbols_since_limit(cache, symbols, since, limit, True)
2972
+
2973
+ def set_positions_cache(self, client: Client, type, symbols: Strings = None, isPortfolioMargin=False):
2974
+ if type == 'spot':
2975
+ return
2976
+ if self.positions is None:
2977
+ self.positions = {}
2978
+ if type in self.positions:
2979
+ return
2980
+ fetchPositionsSnapshot = self.handle_option('watchPositions', 'fetchPositionsSnapshot', False)
2981
+ if fetchPositionsSnapshot:
2982
+ messageHash = type + ':fetchPositionsSnapshot'
2983
+ if not (messageHash in client.futures):
2984
+ client.future(messageHash)
2985
+ self.spawn(self.load_positions_snapshot, client, messageHash, type, isPortfolioMargin)
2986
+ else:
2987
+ self.positions[type] = ArrayCacheBySymbolBySide()
2988
+
2989
+ async def load_positions_snapshot(self, client, messageHash, type, isPortfolioMargin):
2990
+ params: dict = {
2991
+ 'type': type,
2992
+ }
2993
+ if isPortfolioMargin:
2994
+ params['portfolioMargin'] = True
2995
+ positions = await self.fetch_positions(None, params)
2996
+ self.positions[type] = ArrayCacheBySymbolBySide()
2997
+ cache = self.positions[type]
2998
+ for i in range(0, len(positions)):
2999
+ position = positions[i]
3000
+ contracts = self.safe_number(position, 'contracts', 0)
3001
+ if contracts > 0:
3002
+ cache.append(position)
3003
+ # don't remove the future from the .futures cache
3004
+ future = client.futures[messageHash]
3005
+ future.resolve(cache)
3006
+ client.resolve(cache, type + ':position')
3007
+
3008
+ def handle_positions(self, client, message):
3009
+ #
3010
+ # {
3011
+ # e: 'ACCOUNT_UPDATE',
3012
+ # T: 1667881353112,
3013
+ # E: 1667881353115,
3014
+ # a: {
3015
+ # B: [{
3016
+ # a: 'USDT',
3017
+ # wb: '1127.95750089',
3018
+ # cw: '1040.82091149',
3019
+ # bc: '0'
3020
+ # }],
3021
+ # P: [{
3022
+ # s: 'BTCUSDT',
3023
+ # pa: '-0.089',
3024
+ # ep: '19700.03933',
3025
+ # cr: '-1260.24809979',
3026
+ # up: '1.53058860',
3027
+ # mt: 'isolated',
3028
+ # iw: '87.13658940',
3029
+ # ps: 'BOTH',
3030
+ # ma: 'USDT'
3031
+ # }],
3032
+ # m: 'ORDER'
3033
+ # }
3034
+ # }
3035
+ #
3036
+ # each account is connected to a different endpoint
3037
+ # and has exactly one subscriptionhash which is the account type
3038
+ subscriptions = list(client.subscriptions.keys())
3039
+ accountType = subscriptions[0]
3040
+ if self.positions is None:
3041
+ self.positions = {}
3042
+ if not (accountType in self.positions):
3043
+ self.positions[accountType] = ArrayCacheBySymbolBySide()
3044
+ cache = self.positions[accountType]
3045
+ data = self.safe_dict(message, 'a', {})
3046
+ rawPositions = self.safe_list(data, 'P', [])
3047
+ newPositions = []
3048
+ for i in range(0, len(rawPositions)):
3049
+ rawPosition = rawPositions[i]
3050
+ position = self.parse_ws_position(rawPosition)
3051
+ timestamp = self.safe_integer(message, 'E')
3052
+ position['timestamp'] = timestamp
3053
+ position['datetime'] = self.iso8601(timestamp)
3054
+ newPositions.append(position)
3055
+ cache.append(position)
3056
+ messageHashes = self.find_message_hashes(client, accountType + ':positions::')
3057
+ for i in range(0, len(messageHashes)):
3058
+ messageHash = messageHashes[i]
3059
+ parts = messageHash.split('::')
3060
+ symbolsString = parts[1]
3061
+ symbols = symbolsString.split(',')
3062
+ positions = self.filter_by_array(newPositions, 'symbol', symbols, False)
3063
+ if not self.is_empty(positions):
3064
+ client.resolve(positions, messageHash)
3065
+ client.resolve(newPositions, accountType + ':positions')
3066
+
3067
+ def parse_ws_position(self, position, market=None):
3068
+ #
3069
+ # {
3070
+ # "s": "BTCUSDT", # Symbol
3071
+ # "pa": "0", # Position Amount
3072
+ # "ep": "0.00000", # Entry Price
3073
+ # "cr": "200", #(Pre-fee) Accumulated Realized
3074
+ # "up": "0", # Unrealized PnL
3075
+ # "mt": "isolated", # Margin Type
3076
+ # "iw": "0.00000000", # Isolated Wallet(if isolated position)
3077
+ # "ps": "BOTH" # Position Side
3078
+ # }
3079
+ #
3080
+ marketId = self.safe_string(position, 's')
3081
+ contracts = self.safe_string(position, 'pa')
3082
+ contractsAbs = Precise.string_abs(self.safe_string(position, 'pa'))
3083
+ positionSide = self.safe_string_lower(position, 'ps')
3084
+ hedged = True
3085
+ if positionSide == 'both':
3086
+ hedged = False
3087
+ if not Precise.string_eq(contracts, '0'):
3088
+ if Precise.string_lt(contracts, '0'):
3089
+ positionSide = 'short'
3090
+ else:
3091
+ positionSide = 'long'
3092
+ return self.safe_position({
3093
+ 'info': position,
3094
+ 'id': None,
3095
+ 'symbol': self.safe_symbol(marketId, None, None, 'contract'),
3096
+ 'notional': None,
3097
+ 'marginMode': self.safe_string(position, 'mt'),
3098
+ 'liquidationPrice': None,
3099
+ 'entryPrice': self.safe_number(position, 'ep'),
3100
+ 'unrealizedPnl': self.safe_number(position, 'up'),
3101
+ 'percentage': None,
3102
+ 'contracts': self.parse_number(contractsAbs),
3103
+ 'contractSize': None,
3104
+ 'markPrice': None,
3105
+ 'side': positionSide,
3106
+ 'hedged': hedged,
3107
+ 'timestamp': None,
3108
+ 'datetime': None,
3109
+ 'maintenanceMargin': None,
3110
+ 'maintenanceMarginPercentage': None,
3111
+ 'collateral': None,
3112
+ 'initialMargin': None,
3113
+ 'initialMarginPercentage': None,
3114
+ 'leverage': None,
3115
+ 'marginRatio': None,
3116
+ })
3117
+
3118
+ async def fetch_my_trades_ws(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
3119
+ """
3120
+ :see: https://binance-docs.github.io/apidocs/websocket_api/en/#account-trade-history-user_data
3121
+ fetch all trades made by the user
3122
+ :param str symbol: unified market symbol
3123
+ :param int|None [since]: the earliest time in ms to fetch trades for
3124
+ :param int|None [limit]: the maximum number of trades structures to retrieve
3125
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3126
+ :param int [params.endTime]: the latest time in ms to fetch trades for
3127
+ :param int [params.fromId]: first trade Id to fetch
3128
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
3129
+ """
3130
+ await self.load_markets()
3131
+ if symbol is None:
3132
+ raise BadRequest(self.id + ' fetchMyTradesWs requires a symbol')
3133
+ market = self.market(symbol)
3134
+ type = self.get_market_type('fetchMyTradesWs', market, params)
3135
+ if type != 'spot' and type != 'future':
3136
+ raise BadRequest(self.id + ' fetchMyTradesWs does not support ' + type + ' markets')
3137
+ url = self.urls['api']['ws']['ws-api'][type]
3138
+ requestId = self.request_id(url)
3139
+ messageHash = str(requestId)
3140
+ returnRateLimits = False
3141
+ returnRateLimits, params = self.handle_option_and_params(params, 'fetchMyTradesWs', 'returnRateLimits', False)
3142
+ payload: dict = {
3143
+ 'symbol': self.market_id(symbol),
3144
+ 'returnRateLimits': returnRateLimits,
3145
+ }
3146
+ if since is not None:
3147
+ payload['startTime'] = since
3148
+ if limit is not None:
3149
+ payload['limit'] = limit
3150
+ fromId = self.safe_integer(params, 'fromId')
3151
+ if fromId is not None and since is not None:
3152
+ raise BadRequest(self.id + 'fetchMyTradesWs does not support fetching by both fromId and since parameters at the same time')
3153
+ message: dict = {
3154
+ 'id': messageHash,
3155
+ 'method': 'myTrades',
3156
+ 'params': self.sign_params(self.extend(payload, params)),
3157
+ }
3158
+ subscription: dict = {
3159
+ 'method': self.handle_trades_ws,
3160
+ }
3161
+ trades = await self.watch(url, messageHash, message, messageHash, subscription)
3162
+ return self.filter_by_symbol_since_limit(trades, symbol, since, limit)
3163
+
3164
+ async def fetch_trades_ws(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
3165
+ """
3166
+ :see: https://binance-docs.github.io/apidocs/websocket_api/en/#recent-trades
3167
+ fetch all trades made by the user
3168
+ :param str symbol: unified market symbol
3169
+ :param int [since]: the earliest time in ms to fetch trades for
3170
+ :param int [limit]: the maximum number of trades structures to retrieve, default=500, max=1000
3171
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3172
+ *
3173
+ * EXCHANGE SPECIFIC PARAMETERS
3174
+ :param int [params.fromId]: trade ID to begin at
3175
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
3176
+ """
3177
+ await self.load_markets()
3178
+ if symbol is None:
3179
+ raise BadRequest(self.id + ' fetchTradesWs() requires a symbol argument')
3180
+ market = self.market(symbol)
3181
+ type = self.get_market_type('fetchTradesWs', market, params)
3182
+ if type != 'spot' and type != 'future':
3183
+ raise BadRequest(self.id + ' fetchTradesWs does not support ' + type + ' markets')
3184
+ url = self.urls['api']['ws']['ws-api'][type]
3185
+ requestId = self.request_id(url)
3186
+ messageHash = str(requestId)
3187
+ returnRateLimits = False
3188
+ returnRateLimits, params = self.handle_option_and_params(params, 'fetchTradesWs', 'returnRateLimits', False)
3189
+ payload: dict = {
3190
+ 'symbol': self.market_id(symbol),
3191
+ 'returnRateLimits': returnRateLimits,
3192
+ }
3193
+ if limit is not None:
3194
+ payload['limit'] = limit
3195
+ message: dict = {
3196
+ 'id': messageHash,
3197
+ 'method': 'trades.historical',
3198
+ 'params': self.extend(payload, params),
3199
+ }
3200
+ subscription: dict = {
3201
+ 'method': self.handle_trades_ws,
3202
+ }
3203
+ trades = await self.watch(url, messageHash, message, messageHash, subscription)
3204
+ return self.filter_by_since_limit(trades, since, limit)
3205
+
3206
+ def handle_trades_ws(self, client: Client, message):
3207
+ #
3208
+ # fetchMyTradesWs
3209
+ #
3210
+ # {
3211
+ # "id": "f4ce6a53-a29d-4f70-823b-4ab59391d6e8",
3212
+ # "status": 200,
3213
+ # "result": [
3214
+ # {
3215
+ # "symbol": "BTCUSDT",
3216
+ # "id": 1650422481,
3217
+ # "orderId": 12569099453,
3218
+ # "orderListId": -1,
3219
+ # "price": "23416.10000000",
3220
+ # "qty": "0.00635000",
3221
+ # "quoteQty": "148.69223500",
3222
+ # "commission": "0.00000000",
3223
+ # "commissionAsset": "BNB",
3224
+ # "time": 1660801715793,
3225
+ # "isBuyer": False,
3226
+ # "isMaker": True,
3227
+ # "isBestMatch": True
3228
+ # },
3229
+ # ...
3230
+ # ],
3231
+ # }
3232
+ #
3233
+ # fetchTradesWs
3234
+ #
3235
+ # {
3236
+ # "id": "f4ce6a53-a29d-4f70-823b-4ab59391d6e8",
3237
+ # "status": 200,
3238
+ # "result": [
3239
+ # {
3240
+ # "id": 0,
3241
+ # "price": "0.00005000",
3242
+ # "qty": "40.00000000",
3243
+ # "quoteQty": "0.00200000",
3244
+ # "time": 1500004800376,
3245
+ # "isBuyerMaker": True,
3246
+ # "isBestMatch": True
3247
+ # }
3248
+ # ...
3249
+ # ],
3250
+ # }
3251
+ #
3252
+ messageHash = self.safe_string(message, 'id')
3253
+ result = self.safe_list(message, 'result', [])
3254
+ trades = self.parse_trades(result)
3255
+ client.resolve(trades, messageHash)
3256
+
3257
+ async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
3258
+ """
3259
+ watches information on multiple trades made by the user
3260
+ :param str symbol: unified market symbol of the market orders were made in
3261
+ :param int [since]: the earliest time in ms to fetch orders for
3262
+ :param int [limit]: the maximum number of order structures to retrieve
3263
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3264
+ :param boolean [params.portfolioMargin]: set to True if you would like to watch trades in a portfolio margin account
3265
+ :returns dict[]: a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure
3266
+ """
3267
+ await self.load_markets()
3268
+ type = None
3269
+ market = None
3270
+ if symbol is not None:
3271
+ market = self.market(symbol)
3272
+ symbol = market['symbol']
3273
+ type, params = self.handle_market_type_and_params('watchMyTrades', market, params)
3274
+ subType = None
3275
+ subType, params = self.handle_sub_type_and_params('watchMyTrades', market, params)
3276
+ if self.isLinear(type, subType):
3277
+ type = 'future'
3278
+ elif self.isInverse(type, subType):
3279
+ type = 'delivery'
3280
+ messageHash = 'myTrades'
3281
+ if symbol is not None:
3282
+ symbol = self.symbol(symbol)
3283
+ messageHash += ':' + symbol
3284
+ params = self.extend(params, {'type': market['type'], 'symbol': symbol})
3285
+ await self.authenticate(params)
3286
+ urlType = type # we don't change type because the listening key is different
3287
+ if type == 'margin':
3288
+ urlType = 'spot' # spot-margin shares the same stream spot
3289
+ isPortfolioMargin = None
3290
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'watchMyTrades', 'papi', 'portfolioMargin', False)
3291
+ if isPortfolioMargin:
3292
+ urlType = 'papi'
3293
+ url = self.urls['api']['ws'][urlType] + '/' + self.options[type]['listenKey']
3294
+ client = self.client(url)
3295
+ self.set_balance_cache(client, type, isPortfolioMargin)
3296
+ self.set_positions_cache(client, type, None, isPortfolioMargin)
3297
+ message = None
3298
+ trades = await self.watch(url, messageHash, message, type)
3299
+ if self.newUpdates:
3300
+ limit = trades.getLimit(symbol, limit)
3301
+ return self.filter_by_symbol_since_limit(trades, symbol, since, limit, True)
3302
+
3303
+ def handle_my_trade(self, client: Client, message):
3304
+ messageHash = 'myTrades'
3305
+ executionType = self.safe_string(message, 'x')
3306
+ if executionType == 'TRADE':
3307
+ trade = self.parse_ws_trade(message)
3308
+ orderId = self.safe_string(trade, 'order')
3309
+ tradeFee = self.safe_dict(trade, 'fee', {})
3310
+ tradeFee = self.extend({}, tradeFee)
3311
+ symbol = self.safe_string(trade, 'symbol')
3312
+ if orderId is not None and tradeFee is not None and symbol is not None:
3313
+ cachedOrders = self.orders
3314
+ if cachedOrders is not None:
3315
+ orders = self.safe_value(cachedOrders.hashmap, symbol, {})
3316
+ order = self.safe_value(orders, orderId)
3317
+ if order is not None:
3318
+ # accumulate order fees
3319
+ fees = self.safe_value(order, 'fees')
3320
+ fee = self.safe_value(order, 'fee')
3321
+ if not self.is_empty(fees):
3322
+ insertNewFeeCurrency = True
3323
+ for i in range(0, len(fees)):
3324
+ orderFee = fees[i]
3325
+ if orderFee['currency'] == tradeFee['currency']:
3326
+ feeCost = self.sum(tradeFee['cost'], orderFee['cost'])
3327
+ order['fees'][i]['cost'] = float(self.currency_to_precision(tradeFee['currency'], feeCost))
3328
+ insertNewFeeCurrency = False
3329
+ break
3330
+ if insertNewFeeCurrency:
3331
+ order['fees'].append(tradeFee)
3332
+ elif fee is not None:
3333
+ if fee['currency'] == tradeFee['currency']:
3334
+ feeCost = self.sum(fee['cost'], tradeFee['cost'])
3335
+ order['fee']['cost'] = float(self.currency_to_precision(tradeFee['currency'], feeCost))
3336
+ elif fee['currency'] is None:
3337
+ order['fee'] = tradeFee
3338
+ else:
3339
+ order['fees'] = [fee, tradeFee]
3340
+ order['fee'] = None
3341
+ else:
3342
+ order['fee'] = tradeFee
3343
+ # save self trade in the order
3344
+ orderTrades = self.safe_list(order, 'trades', [])
3345
+ orderTrades.append(trade)
3346
+ order['trades'] = orderTrades
3347
+ # don't append twice cause it breaks newUpdates mode
3348
+ # self order already exists in the cache
3349
+ if self.myTrades is None:
3350
+ limit = self.safe_integer(self.options, 'tradesLimit', 1000)
3351
+ self.myTrades = ArrayCacheBySymbolById(limit)
3352
+ myTrades = self.myTrades
3353
+ myTrades.append(trade)
3354
+ client.resolve(self.myTrades, messageHash)
3355
+ messageHashSymbol = messageHash + ':' + symbol
3356
+ client.resolve(self.myTrades, messageHashSymbol)
3357
+
3358
+ def handle_order(self, client: Client, message):
3359
+ parsed = self.parse_ws_order(message)
3360
+ symbol = self.safe_string(parsed, 'symbol')
3361
+ orderId = self.safe_string(parsed, 'id')
3362
+ if symbol is not None:
3363
+ if self.orders is None:
3364
+ limit = self.safe_integer(self.options, 'ordersLimit', 1000)
3365
+ self.orders = ArrayCacheBySymbolById(limit)
3366
+ cachedOrders = self.orders
3367
+ orders = self.safe_value(cachedOrders.hashmap, symbol, {})
3368
+ order = self.safe_value(orders, orderId)
3369
+ if order is not None:
3370
+ fee = self.safe_value(order, 'fee')
3371
+ if fee is not None:
3372
+ parsed['fee'] = fee
3373
+ fees = self.safe_value(order, 'fees')
3374
+ if fees is not None:
3375
+ parsed['fees'] = fees
3376
+ parsed['trades'] = self.safe_value(order, 'trades')
3377
+ timestamp = self.safe_integer(parsed, 'timestamp')
3378
+ if timestamp is None:
3379
+ parsed['timestamp'] = self.safe_integer(order, 'timestamp')
3380
+ parsed['datetime'] = self.safe_string(order, 'datetime')
3381
+ cachedOrders.append(parsed)
3382
+ messageHash = 'orders'
3383
+ symbolSpecificMessageHash = 'orders:' + symbol
3384
+ client.resolve(cachedOrders, messageHash)
3385
+ client.resolve(cachedOrders, symbolSpecificMessageHash)
3386
+
3387
+ def handle_acount_update(self, client, message):
3388
+ self.handle_balance(client, message)
3389
+ self.handle_positions(client, message)
3390
+
3391
+ def handle_ws_error(self, client: Client, message):
3392
+ #
3393
+ # {
3394
+ # "error": {
3395
+ # "code": 2,
3396
+ # "msg": "Invalid request: invalid stream"
3397
+ # },
3398
+ # "id": 1
3399
+ # }
3400
+ #
3401
+ id = self.safe_string(message, 'id')
3402
+ rejected = False
3403
+ error = self.safe_dict(message, 'error', {})
3404
+ code = self.safe_integer(error, 'code')
3405
+ msg = self.safe_string(error, 'msg')
3406
+ try:
3407
+ self.handle_errors(code, msg, client.url, None, None, self.json(error), error, None, None)
3408
+ except Exception as e:
3409
+ rejected = True
3410
+ # private endpoint uses id
3411
+ client.reject(e, id)
3412
+ # public endpoint stores messageHash in subscriptios
3413
+ subscriptionKeys = list(client.subscriptions.keys())
3414
+ for i in range(0, len(subscriptionKeys)):
3415
+ subscriptionHash = subscriptionKeys[i]
3416
+ subscriptionId = self.safe_string(client.subscriptions[subscriptionHash], 'id')
3417
+ if id == subscriptionId:
3418
+ client.reject(e, subscriptionHash)
3419
+ if not rejected:
3420
+ client.reject(message, id)
3421
+ # reset connection if 5xx error
3422
+ if self.safe_string(code, 0) == '5':
3423
+ client.reset(message)
3424
+
3425
+ def handle_message(self, client: Client, message):
3426
+ # handle WebSocketAPI
3427
+ status = self.safe_string(message, 'status')
3428
+ error = self.safe_value(message, 'error')
3429
+ if (error is not None) or (status is not None and status != '200'):
3430
+ self.handle_ws_error(client, message)
3431
+ return
3432
+ id = self.safe_string(message, 'id')
3433
+ subscriptions = self.safe_value(client.subscriptions, id)
3434
+ method = self.safe_value(subscriptions, 'method')
3435
+ if method is not None:
3436
+ method(client, message)
3437
+ return
3438
+ # handle other APIs
3439
+ methods: dict = {
3440
+ 'depthUpdate': self.handle_order_book,
3441
+ 'trade': self.handle_trade,
3442
+ 'aggTrade': self.handle_trade,
3443
+ 'kline': self.handle_ohlcv,
3444
+ 'markPrice_kline': self.handle_ohlcv,
3445
+ 'indexPrice_kline': self.handle_ohlcv,
3446
+ '1hTicker@arr': self.handle_tickers,
3447
+ '4hTicker@arr': self.handle_tickers,
3448
+ '1dTicker@arr': self.handle_tickers,
3449
+ '24hrTicker@arr': self.handle_tickers,
3450
+ '24hrMiniTicker@arr': self.handle_tickers,
3451
+ '1hTicker': self.handle_tickers,
3452
+ '4hTicker': self.handle_tickers,
3453
+ '1dTicker': self.handle_tickers,
3454
+ '24hrTicker': self.handle_tickers,
3455
+ '24hrMiniTicker': self.handle_tickers,
3456
+ 'bookTicker': self.handle_bids_asks, # there is no "bookTicker@arr" endpoint
3457
+ 'outboundAccountPosition': self.handle_balance,
3458
+ 'balanceUpdate': self.handle_balance,
3459
+ 'ACCOUNT_UPDATE': self.handle_acount_update,
3460
+ 'executionReport': self.handle_order_update,
3461
+ 'ORDER_TRADE_UPDATE': self.handle_order_update,
3462
+ 'forceOrder': self.handle_liquidation,
3463
+ }
3464
+ event = self.safe_string(message, 'e')
3465
+ if isinstance(message, list):
3466
+ data = message[0]
3467
+ event = self.safe_string(data, 'e') + '@arr'
3468
+ method = self.safe_value(methods, event)
3469
+ if method is None:
3470
+ requestId = self.safe_string(message, 'id')
3471
+ if requestId is not None:
3472
+ self.handle_subscription_status(client, message)
3473
+ return
3474
+ # special case for the real-time bookTicker, since it comes without an event identifier
3475
+ #
3476
+ # {
3477
+ # "u": 7488717758,
3478
+ # "s": "BTCUSDT",
3479
+ # "b": "28621.74000000",
3480
+ # "B": "1.43278800",
3481
+ # "a": "28621.75000000",
3482
+ # "A": "2.52500800"
3483
+ # }
3484
+ #
3485
+ if event is None and ('a' in message) and ('b' in message):
3486
+ self.handle_bids_asks(client, message)
3487
+ else:
3488
+ method(client, message)