ccxt 4.2.77__py2.py3-none-any.whl → 4.4.49__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 (546) hide show
  1. ccxt/__init__.py +36 -14
  2. ccxt/abstract/alpaca.py +4 -0
  3. ccxt/abstract/bigone.py +1 -1
  4. ccxt/abstract/binance.py +112 -48
  5. ccxt/abstract/binancecoinm.py +112 -48
  6. ccxt/abstract/binanceus.py +147 -83
  7. ccxt/abstract/binanceusdm.py +112 -48
  8. ccxt/abstract/bingx.py +133 -78
  9. ccxt/abstract/bitbank.py +5 -0
  10. ccxt/abstract/bitfinex.py +136 -65
  11. ccxt/abstract/bitfinex1.py +69 -0
  12. ccxt/abstract/bitflyer.py +1 -0
  13. ccxt/abstract/bitget.py +8 -1
  14. ccxt/abstract/bitmart.py +13 -1
  15. ccxt/abstract/bitopro.py +1 -0
  16. ccxt/abstract/bitpanda.py +0 -12
  17. ccxt/abstract/bitrue.py +3 -3
  18. ccxt/abstract/bitstamp.py +26 -3
  19. ccxt/abstract/blofin.py +24 -0
  20. ccxt/abstract/btcbox.py +1 -0
  21. ccxt/abstract/bybit.py +29 -14
  22. ccxt/abstract/cex.py +28 -29
  23. ccxt/abstract/coinbase.py +6 -0
  24. ccxt/abstract/coinbaseadvanced.py +94 -0
  25. ccxt/abstract/{coinbasepro.py → coinbaseexchange.py} +1 -0
  26. ccxt/abstract/coinbaseinternational.py +1 -1
  27. ccxt/abstract/coincatch.py +94 -0
  28. ccxt/abstract/coinex.py +233 -123
  29. ccxt/abstract/coinmetro.py +1 -0
  30. ccxt/abstract/cryptocom.py +14 -0
  31. ccxt/abstract/defx.py +69 -0
  32. ccxt/abstract/deribit.py +1 -0
  33. ccxt/abstract/digifinex.py +1 -0
  34. ccxt/abstract/ellipx.py +25 -0
  35. ccxt/abstract/gate.py +20 -0
  36. ccxt/abstract/gateio.py +20 -0
  37. ccxt/abstract/gemini.py +1 -0
  38. ccxt/abstract/hashkey.py +67 -0
  39. ccxt/abstract/hyperliquid.py +1 -1
  40. ccxt/abstract/independentreserve.py +6 -0
  41. ccxt/abstract/kraken.py +4 -3
  42. ccxt/abstract/krakenfutures.py +4 -0
  43. ccxt/abstract/kucoin.py +24 -0
  44. ccxt/abstract/kucoinfutures.py +34 -0
  45. ccxt/abstract/luno.py +2 -0
  46. ccxt/abstract/mexc.py +4 -0
  47. ccxt/abstract/myokx.py +340 -0
  48. ccxt/abstract/oceanex.py +5 -0
  49. ccxt/abstract/okx.py +30 -0
  50. ccxt/abstract/onetrading.py +0 -12
  51. ccxt/abstract/oxfun.py +34 -0
  52. ccxt/abstract/paradex.py +40 -0
  53. ccxt/abstract/phemex.py +1 -0
  54. ccxt/abstract/upbit.py +4 -0
  55. ccxt/abstract/vertex.py +19 -0
  56. ccxt/abstract/whitebit.py +31 -1
  57. ccxt/abstract/woo.py +6 -2
  58. ccxt/abstract/woofipro.py +119 -0
  59. ccxt/abstract/xt.py +153 -0
  60. ccxt/abstract/zonda.py +6 -0
  61. ccxt/ace.py +164 -60
  62. ccxt/alpaca.py +727 -63
  63. ccxt/ascendex.py +395 -249
  64. ccxt/async_support/__init__.py +36 -14
  65. ccxt/async_support/ace.py +164 -60
  66. ccxt/async_support/alpaca.py +727 -63
  67. ccxt/async_support/ascendex.py +396 -249
  68. ccxt/async_support/base/exchange.py +531 -155
  69. ccxt/async_support/base/ws/aiohttp_client.py +28 -5
  70. ccxt/async_support/base/ws/cache.py +3 -2
  71. ccxt/async_support/base/ws/client.py +26 -5
  72. ccxt/async_support/base/ws/fast_client.py +4 -3
  73. ccxt/async_support/base/ws/functions.py +1 -1
  74. ccxt/async_support/base/ws/future.py +40 -31
  75. ccxt/async_support/base/ws/order_book_side.py +3 -0
  76. ccxt/async_support/bequant.py +1 -1
  77. ccxt/async_support/bigone.py +329 -202
  78. ccxt/async_support/binance.py +3030 -1087
  79. ccxt/async_support/binancecoinm.py +2 -1
  80. ccxt/async_support/binanceus.py +12 -1
  81. ccxt/async_support/binanceusdm.py +3 -1
  82. ccxt/async_support/bingx.py +3205 -937
  83. ccxt/async_support/bit2c.py +119 -38
  84. ccxt/async_support/bitbank.py +215 -76
  85. ccxt/async_support/bitbns.py +124 -53
  86. ccxt/async_support/bitfinex.py +3236 -1078
  87. ccxt/async_support/bitfinex1.py +1711 -0
  88. ccxt/async_support/bitflyer.py +238 -49
  89. ccxt/async_support/bitget.py +1525 -573
  90. ccxt/async_support/bithumb.py +199 -65
  91. ccxt/async_support/bitmart.py +1320 -435
  92. ccxt/async_support/bitmex.py +308 -111
  93. ccxt/async_support/bitopro.py +256 -96
  94. ccxt/async_support/bitrue.py +365 -233
  95. ccxt/async_support/bitso.py +201 -89
  96. ccxt/async_support/bitstamp.py +438 -269
  97. ccxt/async_support/bitteam.py +179 -73
  98. ccxt/async_support/bitvavo.py +180 -70
  99. ccxt/async_support/bl3p.py +92 -25
  100. ccxt/async_support/blockchaincom.py +193 -79
  101. ccxt/async_support/blofin.py +392 -148
  102. ccxt/async_support/btcalpha.py +161 -55
  103. ccxt/async_support/btcbox.py +250 -34
  104. ccxt/async_support/btcmarkets.py +232 -85
  105. ccxt/async_support/btcturk.py +159 -60
  106. ccxt/async_support/bybit.py +2231 -1193
  107. ccxt/async_support/cex.py +1409 -1329
  108. ccxt/async_support/coinbase.py +1454 -287
  109. ccxt/async_support/coinbaseadvanced.py +17 -0
  110. ccxt/async_support/{coinbasepro.py → coinbaseexchange.py} +233 -99
  111. ccxt/async_support/coinbaseinternational.py +428 -88
  112. ccxt/async_support/coincatch.py +5152 -0
  113. ccxt/async_support/coincheck.py +121 -38
  114. ccxt/async_support/coinex.py +4020 -3339
  115. ccxt/async_support/coinlist.py +273 -116
  116. ccxt/async_support/coinmate.py +204 -97
  117. ccxt/async_support/coinmetro.py +203 -110
  118. ccxt/async_support/coinone.py +142 -68
  119. ccxt/async_support/coinsph.py +223 -97
  120. ccxt/async_support/coinspot.py +137 -62
  121. ccxt/async_support/cryptocom.py +515 -185
  122. ccxt/async_support/currencycom.py +203 -85
  123. ccxt/async_support/defx.py +2066 -0
  124. ccxt/async_support/delta.py +404 -109
  125. ccxt/async_support/deribit.py +639 -323
  126. ccxt/async_support/digifinex.py +465 -233
  127. ccxt/async_support/ellipx.py +1887 -0
  128. ccxt/async_support/exmo.py +317 -128
  129. ccxt/async_support/gate.py +1472 -463
  130. ccxt/async_support/gemini.py +206 -84
  131. ccxt/async_support/hashkey.py +4164 -0
  132. ccxt/async_support/hitbtc.py +433 -178
  133. ccxt/async_support/hollaex.py +207 -83
  134. ccxt/async_support/htx.py +1095 -563
  135. ccxt/async_support/huobijp.py +178 -56
  136. ccxt/async_support/hyperliquid.py +1678 -292
  137. ccxt/async_support/idex.py +219 -95
  138. ccxt/async_support/independentreserve.py +300 -31
  139. ccxt/async_support/indodax.py +226 -62
  140. ccxt/async_support/kraken.py +871 -354
  141. ccxt/async_support/krakenfutures.py +324 -100
  142. ccxt/async_support/kucoin.py +917 -357
  143. ccxt/async_support/kucoinfutures.py +1004 -149
  144. ccxt/async_support/kuna.py +198 -107
  145. ccxt/async_support/latoken.py +199 -79
  146. ccxt/async_support/lbank.py +360 -113
  147. ccxt/async_support/luno.py +185 -62
  148. ccxt/async_support/lykke.py +168 -55
  149. ccxt/async_support/mercado.py +101 -29
  150. ccxt/async_support/mexc.py +995 -429
  151. ccxt/async_support/myokx.py +53 -0
  152. ccxt/async_support/ndax.py +234 -82
  153. ccxt/async_support/novadax.py +195 -75
  154. ccxt/async_support/oceanex.py +244 -59
  155. ccxt/async_support/okcoin.py +301 -165
  156. ccxt/async_support/okx.py +1776 -454
  157. ccxt/async_support/onetrading.py +198 -414
  158. ccxt/async_support/oxfun.py +2898 -0
  159. ccxt/async_support/p2b.py +142 -52
  160. ccxt/async_support/paradex.py +2085 -0
  161. ccxt/async_support/paymium.py +56 -32
  162. ccxt/async_support/phemex.py +572 -196
  163. ccxt/async_support/poloniex.py +218 -95
  164. ccxt/async_support/poloniexfutures.py +260 -92
  165. ccxt/async_support/probit.py +143 -110
  166. ccxt/async_support/timex.py +123 -70
  167. ccxt/async_support/tokocrypto.py +129 -93
  168. ccxt/async_support/tradeogre.py +39 -25
  169. ccxt/async_support/upbit.py +322 -113
  170. ccxt/async_support/vertex.py +2983 -0
  171. ccxt/async_support/wavesexchange.py +227 -173
  172. ccxt/async_support/wazirx.py +145 -65
  173. ccxt/async_support/whitebit.py +533 -138
  174. ccxt/async_support/woo.py +1137 -296
  175. ccxt/async_support/woofipro.py +2716 -0
  176. ccxt/async_support/xt.py +4628 -0
  177. ccxt/async_support/yobit.py +160 -92
  178. ccxt/async_support/zaif.py +80 -33
  179. ccxt/async_support/zonda.py +140 -69
  180. ccxt/base/errors.py +51 -20
  181. ccxt/base/exchange.py +1722 -480
  182. ccxt/base/precise.py +10 -0
  183. ccxt/base/types.py +223 -4
  184. ccxt/bequant.py +1 -1
  185. ccxt/bigone.py +329 -202
  186. ccxt/binance.py +3030 -1087
  187. ccxt/binancecoinm.py +2 -1
  188. ccxt/binanceus.py +12 -1
  189. ccxt/binanceusdm.py +3 -1
  190. ccxt/bingx.py +3205 -937
  191. ccxt/bit2c.py +119 -38
  192. ccxt/bitbank.py +215 -76
  193. ccxt/bitbns.py +124 -53
  194. ccxt/bitfinex.py +3235 -1078
  195. ccxt/bitfinex1.py +1710 -0
  196. ccxt/bitflyer.py +238 -49
  197. ccxt/bitget.py +1525 -573
  198. ccxt/bithumb.py +198 -65
  199. ccxt/bitmart.py +1320 -435
  200. ccxt/bitmex.py +308 -111
  201. ccxt/bitopro.py +256 -96
  202. ccxt/bitrue.py +365 -233
  203. ccxt/bitso.py +201 -89
  204. ccxt/bitstamp.py +438 -269
  205. ccxt/bitteam.py +179 -73
  206. ccxt/bitvavo.py +180 -70
  207. ccxt/bl3p.py +92 -25
  208. ccxt/blockchaincom.py +193 -79
  209. ccxt/blofin.py +392 -148
  210. ccxt/btcalpha.py +161 -55
  211. ccxt/btcbox.py +250 -34
  212. ccxt/btcmarkets.py +232 -85
  213. ccxt/btcturk.py +159 -60
  214. ccxt/bybit.py +2231 -1193
  215. ccxt/cex.py +1408 -1329
  216. ccxt/coinbase.py +1454 -287
  217. ccxt/coinbaseadvanced.py +17 -0
  218. ccxt/{coinbasepro.py → coinbaseexchange.py} +233 -99
  219. ccxt/coinbaseinternational.py +428 -88
  220. ccxt/coincatch.py +5152 -0
  221. ccxt/coincheck.py +121 -38
  222. ccxt/coinex.py +4020 -3339
  223. ccxt/coinlist.py +273 -116
  224. ccxt/coinmate.py +204 -97
  225. ccxt/coinmetro.py +203 -110
  226. ccxt/coinone.py +142 -68
  227. ccxt/coinsph.py +223 -97
  228. ccxt/coinspot.py +137 -62
  229. ccxt/cryptocom.py +515 -185
  230. ccxt/currencycom.py +203 -85
  231. ccxt/defx.py +2065 -0
  232. ccxt/delta.py +404 -109
  233. ccxt/deribit.py +639 -323
  234. ccxt/digifinex.py +465 -233
  235. ccxt/ellipx.py +1887 -0
  236. ccxt/exmo.py +317 -128
  237. ccxt/gate.py +1472 -463
  238. ccxt/gemini.py +206 -84
  239. ccxt/hashkey.py +4164 -0
  240. ccxt/hitbtc.py +433 -178
  241. ccxt/hollaex.py +207 -83
  242. ccxt/htx.py +1095 -563
  243. ccxt/huobijp.py +178 -56
  244. ccxt/hyperliquid.py +1677 -292
  245. ccxt/idex.py +219 -95
  246. ccxt/independentreserve.py +299 -31
  247. ccxt/indodax.py +226 -62
  248. ccxt/kraken.py +871 -354
  249. ccxt/krakenfutures.py +324 -100
  250. ccxt/kucoin.py +917 -357
  251. ccxt/kucoinfutures.py +1004 -149
  252. ccxt/kuna.py +198 -107
  253. ccxt/latoken.py +199 -79
  254. ccxt/lbank.py +360 -113
  255. ccxt/luno.py +185 -62
  256. ccxt/lykke.py +168 -55
  257. ccxt/mercado.py +101 -29
  258. ccxt/mexc.py +994 -429
  259. ccxt/myokx.py +53 -0
  260. ccxt/ndax.py +234 -82
  261. ccxt/novadax.py +195 -75
  262. ccxt/oceanex.py +244 -59
  263. ccxt/okcoin.py +301 -165
  264. ccxt/okx.py +1776 -454
  265. ccxt/onetrading.py +198 -414
  266. ccxt/oxfun.py +2897 -0
  267. ccxt/p2b.py +142 -52
  268. ccxt/paradex.py +2085 -0
  269. ccxt/paymium.py +56 -32
  270. ccxt/phemex.py +572 -196
  271. ccxt/poloniex.py +218 -95
  272. ccxt/poloniexfutures.py +260 -92
  273. ccxt/pro/__init__.py +29 -5
  274. ccxt/pro/alpaca.py +32 -17
  275. ccxt/pro/ascendex.py +62 -14
  276. ccxt/pro/bequant.py +4 -0
  277. ccxt/pro/binance.py +1596 -329
  278. ccxt/pro/binancecoinm.py +1 -0
  279. ccxt/pro/binanceus.py +2 -9
  280. ccxt/pro/binanceusdm.py +2 -0
  281. ccxt/pro/bingx.py +527 -134
  282. ccxt/pro/bitcoincom.py +4 -1
  283. ccxt/pro/bitfinex.py +731 -266
  284. ccxt/pro/bitfinex1.py +635 -0
  285. ccxt/pro/bitget.py +726 -357
  286. ccxt/pro/bithumb.py +380 -0
  287. ccxt/pro/bitmart.py +143 -39
  288. ccxt/pro/bitmex.py +199 -40
  289. ccxt/pro/bitopro.py +25 -13
  290. ccxt/pro/bitrue.py +31 -32
  291. ccxt/pro/bitstamp.py +7 -6
  292. ccxt/pro/bitvavo.py +203 -81
  293. ccxt/pro/blockchaincom.py +30 -17
  294. ccxt/pro/blofin.py +692 -0
  295. ccxt/pro/bybit.py +791 -82
  296. ccxt/pro/cex.py +99 -51
  297. ccxt/pro/coinbase.py +220 -30
  298. ccxt/{async_support/hitbtc3.py → pro/coinbaseadvanced.py} +5 -5
  299. ccxt/pro/{coinbasepro.py → coinbaseexchange.py} +19 -19
  300. ccxt/pro/coinbaseinternational.py +193 -30
  301. ccxt/pro/coincatch.py +1464 -0
  302. ccxt/pro/coincheck.py +11 -6
  303. ccxt/pro/coinex.py +965 -665
  304. ccxt/pro/coinone.py +17 -10
  305. ccxt/pro/cryptocom.py +446 -66
  306. ccxt/pro/currencycom.py +11 -10
  307. ccxt/pro/defx.py +832 -0
  308. ccxt/pro/deribit.py +167 -31
  309. ccxt/pro/exmo.py +252 -20
  310. ccxt/pro/gate.py +729 -64
  311. ccxt/pro/gemini.py +44 -26
  312. ccxt/pro/hashkey.py +802 -0
  313. ccxt/pro/hitbtc.py +208 -103
  314. ccxt/pro/hollaex.py +25 -9
  315. ccxt/pro/htx.py +83 -39
  316. ccxt/pro/huobijp.py +17 -16
  317. ccxt/pro/hyperliquid.py +502 -31
  318. ccxt/pro/idex.py +28 -13
  319. ccxt/pro/independentreserve.py +21 -16
  320. ccxt/pro/kraken.py +298 -51
  321. ccxt/pro/krakenfutures.py +166 -75
  322. ccxt/pro/kucoin.py +395 -77
  323. ccxt/pro/kucoinfutures.py +400 -99
  324. ccxt/pro/lbank.py +52 -31
  325. ccxt/pro/luno.py +12 -10
  326. ccxt/pro/mexc.py +400 -50
  327. ccxt/pro/myokx.py +28 -0
  328. ccxt/pro/ndax.py +25 -12
  329. ccxt/pro/okcoin.py +28 -9
  330. ccxt/pro/okx.py +935 -124
  331. ccxt/pro/onetrading.py +41 -24
  332. ccxt/pro/oxfun.py +1054 -0
  333. ccxt/pro/p2b.py +100 -24
  334. ccxt/pro/paradex.py +352 -0
  335. ccxt/pro/phemex.py +92 -33
  336. ccxt/pro/poloniex.py +128 -49
  337. ccxt/pro/poloniexfutures.py +53 -32
  338. ccxt/pro/probit.py +92 -85
  339. ccxt/pro/upbit.py +401 -8
  340. ccxt/pro/vertex.py +943 -0
  341. ccxt/pro/wazirx.py +46 -28
  342. ccxt/pro/whitebit.py +65 -12
  343. ccxt/pro/woo.py +437 -65
  344. ccxt/pro/woofipro.py +1271 -0
  345. ccxt/pro/xt.py +1067 -0
  346. ccxt/probit.py +143 -110
  347. ccxt/static_dependencies/__init__.py +1 -1
  348. ccxt/static_dependencies/lark/__init__.py +38 -0
  349. ccxt/static_dependencies/lark/__pyinstaller/__init__.py +6 -0
  350. ccxt/static_dependencies/lark/__pyinstaller/hook-lark.py +14 -0
  351. ccxt/static_dependencies/lark/ast_utils.py +59 -0
  352. ccxt/static_dependencies/lark/common.py +86 -0
  353. ccxt/static_dependencies/lark/exceptions.py +292 -0
  354. ccxt/static_dependencies/lark/grammar.py +130 -0
  355. ccxt/static_dependencies/lark/grammars/__init__.py +0 -0
  356. ccxt/static_dependencies/lark/indenter.py +143 -0
  357. ccxt/static_dependencies/lark/lark.py +658 -0
  358. ccxt/static_dependencies/lark/lexer.py +678 -0
  359. ccxt/static_dependencies/lark/load_grammar.py +1428 -0
  360. ccxt/static_dependencies/lark/parse_tree_builder.py +391 -0
  361. ccxt/static_dependencies/lark/parser_frontends.py +257 -0
  362. ccxt/static_dependencies/lark/parsers/__init__.py +0 -0
  363. ccxt/static_dependencies/lark/parsers/cyk.py +340 -0
  364. ccxt/static_dependencies/lark/parsers/earley.py +314 -0
  365. ccxt/static_dependencies/lark/parsers/earley_common.py +42 -0
  366. ccxt/static_dependencies/lark/parsers/earley_forest.py +801 -0
  367. ccxt/static_dependencies/lark/parsers/grammar_analysis.py +203 -0
  368. ccxt/static_dependencies/lark/parsers/lalr_analysis.py +332 -0
  369. ccxt/static_dependencies/lark/parsers/lalr_interactive_parser.py +158 -0
  370. ccxt/static_dependencies/lark/parsers/lalr_parser.py +122 -0
  371. ccxt/static_dependencies/lark/parsers/lalr_parser_state.py +110 -0
  372. ccxt/static_dependencies/lark/parsers/xearley.py +165 -0
  373. ccxt/static_dependencies/lark/py.typed +0 -0
  374. ccxt/static_dependencies/lark/reconstruct.py +107 -0
  375. ccxt/static_dependencies/lark/tools/__init__.py +70 -0
  376. ccxt/static_dependencies/lark/tools/nearley.py +202 -0
  377. ccxt/static_dependencies/lark/tools/serialize.py +32 -0
  378. ccxt/static_dependencies/lark/tools/standalone.py +196 -0
  379. ccxt/static_dependencies/lark/tree.py +267 -0
  380. ccxt/static_dependencies/lark/tree_matcher.py +186 -0
  381. ccxt/static_dependencies/lark/tree_templates.py +180 -0
  382. ccxt/static_dependencies/lark/utils.py +343 -0
  383. ccxt/static_dependencies/lark/visitors.py +596 -0
  384. ccxt/static_dependencies/marshmallow/__init__.py +81 -0
  385. ccxt/static_dependencies/marshmallow/base.py +65 -0
  386. ccxt/static_dependencies/marshmallow/class_registry.py +94 -0
  387. ccxt/static_dependencies/marshmallow/decorators.py +231 -0
  388. ccxt/static_dependencies/marshmallow/error_store.py +60 -0
  389. ccxt/static_dependencies/marshmallow/exceptions.py +71 -0
  390. ccxt/static_dependencies/marshmallow/fields.py +2114 -0
  391. ccxt/static_dependencies/marshmallow/orderedset.py +89 -0
  392. ccxt/static_dependencies/marshmallow/py.typed +0 -0
  393. ccxt/static_dependencies/marshmallow/schema.py +1228 -0
  394. ccxt/static_dependencies/marshmallow/types.py +12 -0
  395. ccxt/static_dependencies/marshmallow/utils.py +378 -0
  396. ccxt/static_dependencies/marshmallow/validate.py +678 -0
  397. ccxt/static_dependencies/marshmallow/warnings.py +2 -0
  398. ccxt/static_dependencies/marshmallow_dataclass/__init__.py +1047 -0
  399. ccxt/static_dependencies/marshmallow_dataclass/collection_field.py +51 -0
  400. ccxt/static_dependencies/marshmallow_dataclass/lazy_class_attribute.py +45 -0
  401. ccxt/static_dependencies/marshmallow_dataclass/mypy.py +71 -0
  402. ccxt/static_dependencies/marshmallow_dataclass/py.typed +0 -0
  403. ccxt/static_dependencies/marshmallow_dataclass/typing.py +14 -0
  404. ccxt/static_dependencies/marshmallow_dataclass/union_field.py +82 -0
  405. ccxt/static_dependencies/marshmallow_oneofschema/__init__.py +1 -0
  406. ccxt/static_dependencies/marshmallow_oneofschema/one_of_schema.py +193 -0
  407. ccxt/static_dependencies/marshmallow_oneofschema/py.typed +0 -0
  408. ccxt/static_dependencies/starknet/__init__.py +0 -0
  409. ccxt/static_dependencies/starknet/cairo/__init__.py +0 -0
  410. ccxt/static_dependencies/starknet/cairo/data_types.py +123 -0
  411. ccxt/static_dependencies/starknet/cairo/deprecated_parse/__init__.py +0 -0
  412. ccxt/static_dependencies/starknet/cairo/deprecated_parse/cairo_types.py +77 -0
  413. ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser.py +46 -0
  414. ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser_transformer.py +138 -0
  415. ccxt/static_dependencies/starknet/cairo/felt.py +64 -0
  416. ccxt/static_dependencies/starknet/cairo/type_parser.py +121 -0
  417. ccxt/static_dependencies/starknet/cairo/v1/__init__.py +0 -0
  418. ccxt/static_dependencies/starknet/cairo/v1/type_parser.py +59 -0
  419. ccxt/static_dependencies/starknet/cairo/v2/__init__.py +0 -0
  420. ccxt/static_dependencies/starknet/cairo/v2/type_parser.py +77 -0
  421. ccxt/static_dependencies/starknet/ccxt_utils.py +7 -0
  422. ccxt/static_dependencies/starknet/common.py +15 -0
  423. ccxt/static_dependencies/starknet/constants.py +39 -0
  424. ccxt/static_dependencies/starknet/hash/__init__.py +0 -0
  425. ccxt/static_dependencies/starknet/hash/address.py +79 -0
  426. ccxt/static_dependencies/starknet/hash/compiled_class_hash_objects.py +111 -0
  427. ccxt/static_dependencies/starknet/hash/selector.py +16 -0
  428. ccxt/static_dependencies/starknet/hash/storage.py +12 -0
  429. ccxt/static_dependencies/starknet/hash/utils.py +78 -0
  430. ccxt/static_dependencies/starknet/models/__init__.py +0 -0
  431. ccxt/static_dependencies/starknet/models/typed_data.py +45 -0
  432. ccxt/static_dependencies/starknet/serialization/__init__.py +24 -0
  433. ccxt/static_dependencies/starknet/serialization/_calldata_reader.py +40 -0
  434. ccxt/static_dependencies/starknet/serialization/_context.py +142 -0
  435. ccxt/static_dependencies/starknet/serialization/data_serializers/__init__.py +10 -0
  436. ccxt/static_dependencies/starknet/serialization/data_serializers/_common.py +82 -0
  437. ccxt/static_dependencies/starknet/serialization/data_serializers/array_serializer.py +43 -0
  438. ccxt/static_dependencies/starknet/serialization/data_serializers/bool_serializer.py +37 -0
  439. ccxt/static_dependencies/starknet/serialization/data_serializers/byte_array_serializer.py +66 -0
  440. ccxt/static_dependencies/starknet/serialization/data_serializers/cairo_data_serializer.py +71 -0
  441. ccxt/static_dependencies/starknet/serialization/data_serializers/enum_serializer.py +71 -0
  442. ccxt/static_dependencies/starknet/serialization/data_serializers/felt_serializer.py +50 -0
  443. ccxt/static_dependencies/starknet/serialization/data_serializers/named_tuple_serializer.py +58 -0
  444. ccxt/static_dependencies/starknet/serialization/data_serializers/option_serializer.py +43 -0
  445. ccxt/static_dependencies/starknet/serialization/data_serializers/output_serializer.py +40 -0
  446. ccxt/static_dependencies/starknet/serialization/data_serializers/payload_serializer.py +72 -0
  447. ccxt/static_dependencies/starknet/serialization/data_serializers/struct_serializer.py +36 -0
  448. ccxt/static_dependencies/starknet/serialization/data_serializers/tuple_serializer.py +36 -0
  449. ccxt/static_dependencies/starknet/serialization/data_serializers/uint256_serializer.py +76 -0
  450. ccxt/static_dependencies/starknet/serialization/data_serializers/uint_serializer.py +100 -0
  451. ccxt/static_dependencies/starknet/serialization/data_serializers/unit_serializer.py +32 -0
  452. ccxt/static_dependencies/starknet/serialization/errors.py +10 -0
  453. ccxt/static_dependencies/starknet/serialization/factory.py +229 -0
  454. ccxt/static_dependencies/starknet/serialization/function_serialization_adapter.py +110 -0
  455. ccxt/static_dependencies/starknet/serialization/tuple_dataclass.py +59 -0
  456. ccxt/static_dependencies/starknet/utils/__init__.py +0 -0
  457. ccxt/static_dependencies/starknet/utils/constructor_args_translator.py +86 -0
  458. ccxt/static_dependencies/starknet/utils/iterable.py +13 -0
  459. ccxt/static_dependencies/starknet/utils/schema.py +13 -0
  460. ccxt/static_dependencies/starknet/utils/typed_data.py +182 -0
  461. ccxt/static_dependencies/starkware/__init__.py +0 -0
  462. ccxt/static_dependencies/starkware/crypto/__init__.py +0 -0
  463. ccxt/static_dependencies/starkware/crypto/fast_pedersen_hash.py +50 -0
  464. ccxt/static_dependencies/starkware/crypto/math_utils.py +78 -0
  465. ccxt/static_dependencies/starkware/crypto/signature.py +2344 -0
  466. ccxt/static_dependencies/starkware/crypto/utils.py +63 -0
  467. ccxt/static_dependencies/sympy/__init__.py +0 -0
  468. ccxt/static_dependencies/sympy/core/__init__.py +0 -0
  469. ccxt/static_dependencies/sympy/core/intfunc.py +35 -0
  470. ccxt/static_dependencies/sympy/external/__init__.py +0 -0
  471. ccxt/static_dependencies/sympy/external/gmpy.py +345 -0
  472. ccxt/static_dependencies/sympy/external/importtools.py +187 -0
  473. ccxt/static_dependencies/sympy/external/ntheory.py +637 -0
  474. ccxt/static_dependencies/sympy/external/pythonmpq.py +341 -0
  475. ccxt/static_dependencies/typing_inspect/__init__.py +0 -0
  476. ccxt/static_dependencies/typing_inspect/typing_inspect.py +851 -0
  477. ccxt/test/{test_async.py → tests_async.py} +456 -391
  478. ccxt/test/tests_helpers.py +285 -0
  479. ccxt/test/tests_init.py +39 -0
  480. ccxt/test/{test_sync.py → tests_sync.py} +456 -393
  481. ccxt/timex.py +123 -70
  482. ccxt/tokocrypto.py +129 -93
  483. ccxt/tradeogre.py +39 -25
  484. ccxt/upbit.py +322 -113
  485. ccxt/vertex.py +2983 -0
  486. ccxt/wavesexchange.py +227 -173
  487. ccxt/wazirx.py +145 -65
  488. ccxt/whitebit.py +533 -138
  489. ccxt/woo.py +1137 -296
  490. ccxt/woofipro.py +2716 -0
  491. ccxt/xt.py +4627 -0
  492. ccxt/yobit.py +159 -92
  493. ccxt/zaif.py +80 -33
  494. ccxt/zonda.py +140 -69
  495. ccxt-4.4.49.dist-info/LICENSE.txt +21 -0
  496. ccxt-4.4.49.dist-info/METADATA +646 -0
  497. ccxt-4.4.49.dist-info/RECORD +669 -0
  498. {ccxt-4.2.77.dist-info → ccxt-4.4.49.dist-info}/WHEEL +1 -1
  499. ccxt/abstract/bitbay.py +0 -47
  500. ccxt/abstract/bitfinex2.py +0 -139
  501. ccxt/abstract/hitbtc3.py +0 -115
  502. ccxt/async_support/bitbay.py +0 -17
  503. ccxt/async_support/bitfinex2.py +0 -3496
  504. ccxt/async_support/flowbtc.py +0 -34
  505. ccxt/bitbay.py +0 -17
  506. ccxt/bitfinex2.py +0 -3496
  507. ccxt/flowbtc.py +0 -34
  508. ccxt/hitbtc3.py +0 -16
  509. ccxt/pro/bitfinex2.py +0 -1081
  510. ccxt/test/base/__init__.py +0 -28
  511. ccxt/test/base/test_account.py +0 -26
  512. ccxt/test/base/test_balance.py +0 -56
  513. ccxt/test/base/test_borrow_interest.py +0 -35
  514. ccxt/test/base/test_borrow_rate.py +0 -32
  515. ccxt/test/base/test_calculate_fee.py +0 -51
  516. ccxt/test/base/test_crypto.py +0 -127
  517. ccxt/test/base/test_currency.py +0 -76
  518. ccxt/test/base/test_datetime.py +0 -103
  519. ccxt/test/base/test_decimal_to_precision.py +0 -392
  520. ccxt/test/base/test_deep_extend.py +0 -68
  521. ccxt/test/base/test_deposit_withdrawal.py +0 -50
  522. ccxt/test/base/test_exchange_datetime_functions.py +0 -76
  523. ccxt/test/base/test_funding_rate_history.py +0 -29
  524. ccxt/test/base/test_last_price.py +0 -32
  525. ccxt/test/base/test_ledger_entry.py +0 -45
  526. ccxt/test/base/test_ledger_item.py +0 -48
  527. ccxt/test/base/test_leverage_tier.py +0 -33
  528. ccxt/test/base/test_margin_mode.py +0 -24
  529. ccxt/test/base/test_margin_modification.py +0 -35
  530. ccxt/test/base/test_market.py +0 -190
  531. ccxt/test/base/test_number.py +0 -411
  532. ccxt/test/base/test_ohlcv.py +0 -32
  533. ccxt/test/base/test_open_interest.py +0 -32
  534. ccxt/test/base/test_order.py +0 -64
  535. ccxt/test/base/test_order_book.py +0 -63
  536. ccxt/test/base/test_position.py +0 -60
  537. ccxt/test/base/test_shared_methods.py +0 -345
  538. ccxt/test/base/test_status.py +0 -24
  539. ccxt/test/base/test_throttle.py +0 -126
  540. ccxt/test/base/test_ticker.py +0 -86
  541. ccxt/test/base/test_trade.py +0 -47
  542. ccxt/test/base/test_trading_fee.py +0 -26
  543. ccxt/test/base/test_transaction.py +0 -39
  544. ccxt-4.2.77.dist-info/METADATA +0 -626
  545. ccxt-4.2.77.dist-info/RECORD +0 -534
  546. {ccxt-4.2.77.dist-info → ccxt-4.4.49.dist-info}/top_level.txt +0 -0
ccxt/pro/binance.py CHANGED
@@ -6,25 +6,36 @@
6
6
  import ccxt.async_support
7
7
  from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide, ArrayCacheByTimestamp
8
8
  import hashlib
9
- from ccxt.base.types import Balances, Int, Num, Order, OrderBook, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, Trade
9
+ from ccxt.base.types import Balances, Int, Liquidation, Num, Order, OrderBook, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, Trade
10
10
  from ccxt.async_support.base.ws.client import Client
11
11
  from typing import List
12
- from ccxt.base.errors import ExchangeError
12
+ from typing import Any
13
13
  from ccxt.base.errors import ArgumentsRequired
14
14
  from ccxt.base.errors import BadRequest
15
+ from ccxt.base.errors import NotSupported
16
+ from ccxt.base.errors import ChecksumError
15
17
  from ccxt.base.precise import Precise
16
18
 
17
19
 
18
20
  class binance(ccxt.async_support.binance):
19
21
 
20
22
  def describe(self):
21
- return self.deep_extend(super(binance, self).describe(), {
23
+ superDescribe = super(binance, self).describe()
24
+ return self.deep_extend(superDescribe, self.describe_data())
25
+
26
+ def describe_data(self):
27
+ return {
22
28
  'has': {
23
29
  'ws': True,
24
30
  'watchBalance': True,
31
+ 'watchLiquidations': True,
32
+ 'watchLiquidationsForSymbols': True,
33
+ 'watchMyLiquidations': True,
34
+ 'watchMyLiquidationsForSymbols': True,
35
+ 'watchBidsAsks': True,
25
36
  'watchMyTrades': True,
26
37
  'watchOHLCV': True,
27
- 'watchOHLCVForSymbols': False,
38
+ 'watchOHLCVForSymbols': True,
28
39
  'watchOrderBook': True,
29
40
  'watchOrderBookForSymbols': True,
30
41
  'watchOrders': True,
@@ -32,6 +43,8 @@ class binance(ccxt.async_support.binance):
32
43
  'watchPositions': True,
33
44
  'watchTicker': True,
34
45
  'watchTickers': True,
46
+ 'watchMarkPrices': True,
47
+ 'watchMarkPrice': True,
35
48
  'watchTrades': True,
36
49
  'watchTradesForSymbols': True,
37
50
  'createOrderWs': True,
@@ -44,9 +57,14 @@ class binance(ccxt.async_support.binance):
44
57
  'fetchMarketsWs': False,
45
58
  'fetchMyTradesWs': True,
46
59
  'fetchOHLCVWs': True,
60
+ 'fetchOrderBookWs': True,
47
61
  'fetchOpenOrdersWs': True,
48
62
  'fetchOrderWs': True,
49
63
  'fetchOrdersWs': True,
64
+ 'fetchPositionWs': True,
65
+ 'fetchPositionForSymbolWs': True,
66
+ 'fetchPositionsWs': True,
67
+ 'fetchTickerWs': True,
50
68
  'fetchTradesWs': True,
51
69
  'fetchTradingFeesWs': False,
52
70
  'fetchWithdrawalsWs': False,
@@ -58,7 +76,10 @@ class binance(ccxt.async_support.binance):
58
76
  'margin': 'wss://testnet.binance.vision/ws',
59
77
  'future': 'wss://fstream.binancefuture.com/ws',
60
78
  'delivery': 'wss://dstream.binancefuture.com/ws',
61
- 'ws': 'wss://testnet.binance.vision/ws-api/v3',
79
+ 'ws-api': {
80
+ 'spot': 'wss://testnet.binance.vision/ws-api/v3',
81
+ 'future': 'wss://testnet.binancefuture.com/ws-fapi/v1',
82
+ },
62
83
  },
63
84
  },
64
85
  'api': {
@@ -67,10 +88,14 @@ class binance(ccxt.async_support.binance):
67
88
  'margin': 'wss://stream.binance.com:9443/ws',
68
89
  'future': 'wss://fstream.binance.com/ws',
69
90
  'delivery': 'wss://dstream.binance.com/ws',
70
- 'ws': 'wss://ws-api.binance.com:443/ws-api/v3',
91
+ 'ws-api': {
92
+ 'spot': 'wss://ws-api.binance.com:443/ws-api/v3',
93
+ 'future': 'wss://ws-fapi.binance.com/ws-fapi/v1',
94
+ },
71
95
  'papi': 'wss://fstream.binance.com/pm/ws',
72
96
  },
73
97
  },
98
+ 'doc': 'https://developers.binance.com/en',
74
99
  },
75
100
  'streaming': {
76
101
  'keepAlive': 180000,
@@ -89,35 +114,41 @@ class binance(ccxt.async_support.binance):
89
114
  'future': 200,
90
115
  'delivery': 200,
91
116
  },
92
- 'streamBySubscriptionsHash': {},
117
+ 'streamBySubscriptionsHash': self.create_safe_dictionary(),
93
118
  'streamIndex': -1,
94
119
  # get updates every 1000ms or 100ms
95
120
  # or every 0ms in real-time for futures
96
121
  'watchOrderBookRate': 100,
122
+ 'liquidationsLimit': 1000,
123
+ 'myLiquidationsLimit': 1000,
97
124
  'tradesLimit': 1000,
98
125
  'ordersLimit': 1000,
99
126
  'OHLCVLimit': 1000,
100
- 'requestId': {},
127
+ 'requestId': self.create_safe_dictionary(),
101
128
  'watchOrderBookLimit': 1000, # default limit
102
129
  'watchTrades': {
103
130
  'name': 'trade', # 'trade' or 'aggTrade'
104
131
  },
105
132
  'watchTicker': {
106
- 'name': 'ticker', # ticker = 1000ms L1+OHLCV, bookTicker = real-time L1
133
+ 'name': 'ticker', # ticker or miniTicker or ticker_<window_size>
107
134
  },
108
135
  'watchTickers': {
109
- 'name': 'ticker', # ticker or miniTicker or bookTicker
136
+ 'name': 'ticker', # ticker or miniTicker or ticker_<window_size>
110
137
  },
111
138
  'watchOHLCV': {
112
139
  'name': 'kline', # or indexPriceKline or markPriceKline(coin-m futures)
113
140
  },
114
141
  'watchOrderBook': {
115
142
  'maxRetries': 3,
143
+ 'checksum': True,
116
144
  },
117
145
  'watchBalance': {
118
146
  'fetchBalanceSnapshot': False, # or True
119
147
  'awaitBalanceSnapshot': True, # whether to wait for the balance snapshot before providing updates
120
148
  },
149
+ 'watchLiquidationsForSymbols': {
150
+ 'defaultType': 'swap',
151
+ },
121
152
  'watchPositions': {
122
153
  'fetchPositionsSnapshot': True, # or False
123
154
  'awaitPositionsSnapshot': True, # whether to wait for the positions snapshot before providing updates
@@ -127,18 +158,28 @@ class binance(ccxt.async_support.binance):
127
158
  'ws': {
128
159
  'cost': 5,
129
160
  },
161
+ 'tickerChannelsMap': {
162
+ '24hrTicker': 'ticker',
163
+ '24hrMiniTicker': 'miniTicker',
164
+ 'markPriceUpdate': 'markPrice',
165
+ # rolling window tickers
166
+ '1hTicker': 'ticker_1h',
167
+ '4hTicker': 'ticker_4h',
168
+ '1dTicker': 'ticker_1d',
169
+ 'bookTicker': 'bookTicker',
170
+ },
130
171
  },
131
- })
172
+ }
132
173
 
133
174
  def request_id(self, url):
134
- options = self.safe_value(self.options, 'requestId', {})
175
+ options = self.safe_dict(self.options, 'requestId', self.create_safe_dictionary())
135
176
  previousValue = self.safe_integer(options, url, 0)
136
177
  newValue = self.sum(previousValue, 1)
137
178
  self.options['requestId'][url] = newValue
138
179
  return newValue
139
180
 
140
- def stream(self, type, subscriptionHash, numSubscriptions=1):
141
- streamBySubscriptionsHash = self.safe_value(self.options, 'streamBySubscriptionsHash', {})
181
+ def stream(self, type: Str, subscriptionHash: Str, numSubscriptions=1):
182
+ streamBySubscriptionsHash = self.safe_dict(self.options, 'streamBySubscriptionsHash', self.create_safe_dictionary())
142
183
  stream = self.safe_string(streamBySubscriptionsHash, subscriptionHash)
143
184
  if stream is None:
144
185
  streamIndex = self.safe_integer(self.options, 'streamIndex', -1)
@@ -151,7 +192,7 @@ class binance(ccxt.async_support.binance):
151
192
  self.options['streamBySubscriptionsHash'][subscriptionHash] = stream
152
193
  subscriptionsByStreams = self.safe_value(self.options, 'numSubscriptionsByStream')
153
194
  if subscriptionsByStreams is None:
154
- self.options['numSubscriptionsByStream'] = {}
195
+ self.options['numSubscriptionsByStream'] = self.create_safe_dictionary()
155
196
  subscriptionsByStream = self.safe_integer(self.options['numSubscriptionsByStream'], stream, 0)
156
197
  newNumSubscriptions = subscriptionsByStream + numSubscriptions
157
198
  subscriptionLimitByStream = self.safe_integer(self.options['subscriptionLimitByStream'], type, 200)
@@ -160,8 +201,342 @@ class binance(ccxt.async_support.binance):
160
201
  self.options['numSubscriptionsByStream'][stream] = subscriptionsByStream + numSubscriptions
161
202
  return stream
162
203
 
204
+ async def watch_liquidations(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
205
+ """
206
+ watch the public liquidations of a trading pair
207
+
208
+ https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Liquidation-Order-Streams
209
+ https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/Liquidation-Order-Streams
210
+
211
+ :param str symbol: unified CCXT market symbol
212
+ :param int [since]: the earliest time in ms to fetch liquidations for
213
+ :param int [limit]: the maximum number of liquidation structures to retrieve
214
+ :param dict [params]: exchange specific parameters for the bitmex api endpoint
215
+ :returns dict: an array of `liquidation structures <https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure>`
216
+ """
217
+ return await self.watch_liquidations_for_symbols([symbol], since, limit, params)
218
+
219
+ async def watch_liquidations_for_symbols(self, symbols: List[str] = None, since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
220
+ """
221
+ watch the public liquidations of a trading pair
222
+
223
+ https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/All-Market-Liquidation-Order-Streams
224
+ https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/All-Market-Liquidation-Order-Streams
225
+
226
+ :param str[] symbols: list of unified market symbols
227
+ :param int [since]: the earliest time in ms to fetch liquidations for
228
+ :param int [limit]: the maximum number of liquidation structures to retrieve
229
+ :param dict [params]: exchange specific parameters for the bitmex api endpoint
230
+ :returns dict: an array of `liquidation structures <https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure>`
231
+ """
232
+ await self.load_markets()
233
+ subscriptionHashes = []
234
+ messageHashes = []
235
+ streamHash = 'liquidations'
236
+ symbols = self.market_symbols(symbols, None, True, True)
237
+ if self.is_empty(symbols):
238
+ subscriptionHashes.append('!' + 'forceOrder@arr')
239
+ messageHashes.append('liquidations')
240
+ else:
241
+ for i in range(0, len(symbols)):
242
+ market = self.market(symbols[i])
243
+ subscriptionHashes.append(market['lowercaseId'] + '@forceOrder')
244
+ messageHashes.append('liquidations::' + symbols[i])
245
+ streamHash += '::' + ','.join(symbols)
246
+ firstMarket = self.get_market_from_symbols(symbols)
247
+ type = None
248
+ type, params = self.handle_market_type_and_params('watchLiquidationsForSymbols', firstMarket, params)
249
+ if type == 'spot':
250
+ raise BadRequest(self.id + 'watchLiquidationsForSymbols is not supported for spot symbols')
251
+ subType = None
252
+ subType, params = self.handle_sub_type_and_params('watchLiquidationsForSymbols', firstMarket, params)
253
+ if self.isLinear(type, subType):
254
+ type = 'future'
255
+ elif self.isInverse(type, subType):
256
+ type = 'delivery'
257
+ numSubscriptions = len(subscriptionHashes)
258
+ url = self.urls['api']['ws'][type] + '/' + self.stream(type, streamHash, numSubscriptions)
259
+ requestId = self.request_id(url)
260
+ request = {
261
+ 'method': 'SUBSCRIBE',
262
+ 'params': subscriptionHashes,
263
+ 'id': requestId,
264
+ }
265
+ subscribe = {
266
+ 'id': requestId,
267
+ }
268
+ newLiquidations = await self.watch_multiple(url, messageHashes, self.extend(request, params), subscriptionHashes, subscribe)
269
+ if self.newUpdates:
270
+ return newLiquidations
271
+ return self.filter_by_symbols_since_limit(self.liquidations, symbols, since, limit, True)
272
+
273
+ def handle_liquidation(self, client: Client, message):
274
+ #
275
+ # future
276
+ # {
277
+ # "e":"forceOrder",
278
+ # "E":1698871323061,
279
+ # "o":{
280
+ # "s":"BTCUSDT",
281
+ # "S":"BUY",
282
+ # "o":"LIMIT",
283
+ # "f":"IOC",
284
+ # "q":"1.437",
285
+ # "p":"35100.81",
286
+ # "ap":"34959.70",
287
+ # "X":"FILLED",
288
+ # "l":"1.437",
289
+ # "z":"1.437",
290
+ # "T":1698871323059
291
+ # }
292
+ # }
293
+ # delivery
294
+ # {
295
+ # "e":"forceOrder", # Event Type
296
+ # "E": 1591154240950, # Event Time
297
+ # "o":{
298
+ # "s":"BTCUSD_200925", # Symbol
299
+ # "ps": "BTCUSD", # Pair
300
+ # "S":"SELL", # Side
301
+ # "o":"LIMIT", # Order Type
302
+ # "f":"IOC", # Time in Force
303
+ # "q":"1", # Original Quantity
304
+ # "p":"9425.5", # Price
305
+ # "ap":"9496.5", # Average Price
306
+ # "X":"FILLED", # Order Status
307
+ # "l":"1", # Order Last Filled Quantity
308
+ # "z":"1", # Order Filled Accumulated Quantity
309
+ # "T": 1591154240949, # Order Trade Time
310
+ # }
311
+ # }
312
+ #
313
+ rawLiquidation = self.safe_value(message, 'o', {})
314
+ marketId = self.safe_string(rawLiquidation, 's')
315
+ market = self.safe_market(marketId, None, '', 'contract')
316
+ symbol = market['symbol']
317
+ liquidation = self.parse_ws_liquidation(rawLiquidation, market)
318
+ liquidations = self.safe_value(self.liquidations, symbol)
319
+ if liquidations is None:
320
+ limit = self.safe_integer(self.options, 'liquidationsLimit', 1000)
321
+ liquidations = ArrayCache(limit)
322
+ liquidations.append(liquidation)
323
+ self.liquidations[symbol] = liquidations
324
+ client.resolve([liquidation], 'liquidations')
325
+ client.resolve([liquidation], 'liquidations::' + symbol)
326
+
327
+ def parse_ws_liquidation(self, liquidation, market=None):
328
+ #
329
+ # future
330
+ # {
331
+ # "s":"BTCUSDT",
332
+ # "S":"BUY",
333
+ # "o":"LIMIT",
334
+ # "f":"IOC",
335
+ # "q":"1.437",
336
+ # "p":"35100.81",
337
+ # "ap":"34959.70",
338
+ # "X":"FILLED",
339
+ # "l":"1.437",
340
+ # "z":"1.437",
341
+ # "T":1698871323059
342
+ # }
343
+ # delivery
344
+ # {
345
+ # "s":"BTCUSD_200925", # Symbol
346
+ # "ps": "BTCUSD", # Pair
347
+ # "S":"SELL", # Side
348
+ # "o":"LIMIT", # Order Type
349
+ # "f":"IOC", # Time in Force
350
+ # "q":"1", # Original Quantity
351
+ # "p":"9425.5", # Price
352
+ # "ap":"9496.5", # Average Price
353
+ # "X":"FILLED", # Order Status
354
+ # "l":"1", # Order Last Filled Quantity
355
+ # "z":"1", # Order Filled Accumulated Quantity
356
+ # "T": 1591154240949, # Order Trade Time
357
+ # }
358
+ # myLiquidation
359
+ # {
360
+ # "s":"BTCUSDT", # Symbol
361
+ # "c":"TEST", # Client Order Id
362
+ # # special client order id:
363
+ # # starts with "autoclose-": liquidation order
364
+ # # "adl_autoclose": ADL auto close order
365
+ # # "settlement_autoclose-": settlement order for delisting or delivery
366
+ # "S":"SELL", # Side
367
+ # "o":"TRAILING_STOP_MARKET", # Order Type
368
+ # "f":"GTC", # Time in Force
369
+ # "q":"0.001", # Original Quantity
370
+ # "p":"0", # Original Price
371
+ # "ap":"0", # Average Price
372
+ # "sp":"7103.04", # Stop Price. Please ignore with TRAILING_STOP_MARKET order
373
+ # "x":"NEW", # Execution Type
374
+ # "X":"NEW", # Order Status
375
+ # "i":8886774, # Order Id
376
+ # "l":"0", # Order Last Filled Quantity
377
+ # "z":"0", # Order Filled Accumulated Quantity
378
+ # "L":"0", # Last Filled Price
379
+ # "N":"USDT", # Commission Asset, will not push if no commission
380
+ # "n":"0", # Commission, will not push if no commission
381
+ # "T":1568879465650, # Order Trade Time
382
+ # "t":0, # Trade Id
383
+ # "b":"0", # Bids Notional
384
+ # "a":"9.91", # Ask Notional
385
+ # "m":false, # Is self trade the maker side?
386
+ # "R":false, # Is self reduce only
387
+ # "wt":"CONTRACT_PRICE", # Stop Price Working Type
388
+ # "ot":"TRAILING_STOP_MARKET",// Original Order Type
389
+ # "ps":"LONG", # Position Side
390
+ # "cp":false, # If Close-All, pushed with conditional order
391
+ # "AP":"7476.89", # Activation Price, only puhed with TRAILING_STOP_MARKET order
392
+ # "cr":"5.0", # Callback Rate, only puhed with TRAILING_STOP_MARKET order
393
+ # "pP": False, # If price protection is turned on
394
+ # "si": 0, # ignore
395
+ # "ss": 0, # ignore
396
+ # "rp":"0", # Realized Profit of the trade
397
+ # "V":"EXPIRE_TAKER", # STP mode
398
+ # "pm":"OPPONENT", # Price match mode
399
+ # "gtd":0 # TIF GTD order auto cancel time
400
+ # }
401
+ #
402
+ marketId = self.safe_string(liquidation, 's')
403
+ market = self.safe_market(marketId, market)
404
+ timestamp = self.safe_integer(liquidation, 'T')
405
+ return self.safe_liquidation({
406
+ 'info': liquidation,
407
+ 'symbol': self.safe_symbol(marketId, market),
408
+ 'contracts': self.safe_number(liquidation, 'l'),
409
+ 'contractSize': self.safe_number(market, 'contractSize'),
410
+ 'price': self.safe_number(liquidation, 'ap'),
411
+ 'baseValue': None,
412
+ 'quoteValue': None,
413
+ 'timestamp': timestamp,
414
+ 'datetime': self.iso8601(timestamp),
415
+ })
416
+
417
+ async def watch_my_liquidations(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
418
+ """
419
+ watch the private liquidations of a trading pair
420
+
421
+ https://developers.binance.com/docs/derivatives/usds-margined-futures/user-data-streams/Event-Order-Update
422
+ https://developers.binance.com/docs/derivatives/coin-margined-futures/user-data-streams/Event-Order-Update
423
+
424
+ :param str symbol: unified CCXT market symbol
425
+ :param int [since]: the earliest time in ms to fetch liquidations for
426
+ :param int [limit]: the maximum number of liquidation structures to retrieve
427
+ :param dict [params]: exchange specific parameters for the bitmex api endpoint
428
+ :returns dict: an array of `liquidation structures <https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure>`
429
+ """
430
+ return self.watch_my_liquidations_for_symbols([symbol], since, limit, params)
431
+
432
+ async def watch_my_liquidations_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
433
+ """
434
+ watch the private liquidations of a trading pair
435
+
436
+ https://developers.binance.com/docs/derivatives/usds-margined-futures/user-data-streams/Event-Order-Update
437
+ https://developers.binance.com/docs/derivatives/coin-margined-futures/user-data-streams/Event-Order-Update
438
+
439
+ :param str[] symbols: list of unified market symbols
440
+ :param int [since]: the earliest time in ms to fetch liquidations for
441
+ :param int [limit]: the maximum number of liquidation structures to retrieve
442
+ :param dict [params]: exchange specific parameters for the bitmex api endpoint
443
+ :returns dict: an array of `liquidation structures <https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure>`
444
+ """
445
+ await self.load_markets()
446
+ symbols = self.market_symbols(symbols, None, True, True, True)
447
+ market = self.get_market_from_symbols(symbols)
448
+ messageHashes = ['myLiquidations']
449
+ if not self.is_empty(symbols):
450
+ for i in range(0, len(symbols)):
451
+ symbol = symbols[i]
452
+ messageHashes.append('myLiquidations::' + symbol)
453
+ type = None
454
+ type, params = self.handle_market_type_and_params('watchMyLiquidationsForSymbols', market, params)
455
+ subType = None
456
+ subType, params = self.handle_sub_type_and_params('watchMyLiquidationsForSymbols', market, params)
457
+ if self.isLinear(type, subType):
458
+ type = 'future'
459
+ elif self.isInverse(type, subType):
460
+ type = 'delivery'
461
+ await self.authenticate(params)
462
+ url = self.urls['api']['ws'][type] + '/' + self.options[type]['listenKey']
463
+ message = None
464
+ newLiquidations = await self.watch_multiple(url, messageHashes, message, [type])
465
+ if self.newUpdates:
466
+ return newLiquidations
467
+ return self.filter_by_symbols_since_limit(self.liquidations, symbols, since, limit)
468
+
469
+ def handle_my_liquidation(self, client: Client, message):
470
+ #
471
+ # {
472
+ # "s":"BTCUSDT", # Symbol
473
+ # "c":"TEST", # Client Order Id
474
+ # # special client order id:
475
+ # # starts with "autoclose-": liquidation order
476
+ # # "adl_autoclose": ADL auto close order
477
+ # # "settlement_autoclose-": settlement order for delisting or delivery
478
+ # "S":"SELL", # Side
479
+ # "o":"TRAILING_STOP_MARKET", # Order Type
480
+ # "f":"GTC", # Time in Force
481
+ # "q":"0.001", # Original Quantity
482
+ # "p":"0", # Original Price
483
+ # "ap":"0", # Average Price
484
+ # "sp":"7103.04", # Stop Price. Please ignore with TRAILING_STOP_MARKET order
485
+ # "x":"NEW", # Execution Type
486
+ # "X":"NEW", # Order Status
487
+ # "i":8886774, # Order Id
488
+ # "l":"0", # Order Last Filled Quantity
489
+ # "z":"0", # Order Filled Accumulated Quantity
490
+ # "L":"0", # Last Filled Price
491
+ # "N":"USDT", # Commission Asset, will not push if no commission
492
+ # "n":"0", # Commission, will not push if no commission
493
+ # "T":1568879465650, # Order Trade Time
494
+ # "t":0, # Trade Id
495
+ # "b":"0", # Bids Notional
496
+ # "a":"9.91", # Ask Notional
497
+ # "m":false, # Is self trade the maker side?
498
+ # "R":false, # Is self reduce only
499
+ # "wt":"CONTRACT_PRICE", # Stop Price Working Type
500
+ # "ot":"TRAILING_STOP_MARKET",// Original Order Type
501
+ # "ps":"LONG", # Position Side
502
+ # "cp":false, # If Close-All, pushed with conditional order
503
+ # "AP":"7476.89", # Activation Price, only puhed with TRAILING_STOP_MARKET order
504
+ # "cr":"5.0", # Callback Rate, only puhed with TRAILING_STOP_MARKET order
505
+ # "pP": False, # If price protection is turned on
506
+ # "si": 0, # ignore
507
+ # "ss": 0, # ignore
508
+ # "rp":"0", # Realized Profit of the trade
509
+ # "V":"EXPIRE_TAKER", # STP mode
510
+ # "pm":"OPPONENT", # Price match mode
511
+ # "gtd":0 # TIF GTD order auto cancel time
512
+ # }
513
+ #
514
+ orderType = self.safe_string(message, 'o')
515
+ if orderType != 'LIQUIDATION':
516
+ return
517
+ marketId = self.safe_string(message, 's')
518
+ market = self.safe_market(marketId)
519
+ symbol = self.safe_symbol(marketId)
520
+ liquidation = self.parse_ws_liquidation(message, market)
521
+ myLiquidations = self.safe_value(self.myLiquidations, symbol)
522
+ if myLiquidations is None:
523
+ limit = self.safe_integer(self.options, 'myLiquidationsLimit', 1000)
524
+ myLiquidations = ArrayCache(limit)
525
+ myLiquidations.append(liquidation)
526
+ self.myLiquidations[symbol] = myLiquidations
527
+ client.resolve([liquidation], 'myLiquidations')
528
+ client.resolve([liquidation], 'myLiquidations::' + symbol)
529
+
163
530
  async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
164
531
  """
532
+
533
+ https://binance-docs.github.io/apidocs/spot/en/#partial-book-depth-streams
534
+ https://binance-docs.github.io/apidocs/spot/en/#diff-depth-stream
535
+ https://binance-docs.github.io/apidocs/futures/en/#partial-book-depth-streams
536
+ https://binance-docs.github.io/apidocs/futures/en/#diff-book-depth-streams
537
+ https://binance-docs.github.io/apidocs/delivery/en/#partial-book-depth-streams
538
+ https://binance-docs.github.io/apidocs/delivery/en/#diff-book-depth-streams
539
+
165
540
  watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
166
541
  :param str symbol: unified symbol of the market to fetch the order book for
167
542
  :param int [limit]: the maximum amount of order book entries to return
@@ -209,6 +584,14 @@ class binance(ccxt.async_support.binance):
209
584
 
210
585
  async def watch_order_book_for_symbols(self, symbols: List[str], limit: Int = None, params={}) -> OrderBook:
211
586
  """
587
+
588
+ https://binance-docs.github.io/apidocs/spot/en/#partial-book-depth-streams
589
+ https://binance-docs.github.io/apidocs/spot/en/#diff-depth-stream
590
+ https://binance-docs.github.io/apidocs/futures/en/#partial-book-depth-streams
591
+ https://binance-docs.github.io/apidocs/futures/en/#diff-book-depth-streams
592
+ https://binance-docs.github.io/apidocs/delivery/en/#partial-book-depth-streams
593
+ https://binance-docs.github.io/apidocs/delivery/en/#diff-book-depth-streams
594
+
212
595
  watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
213
596
  :param str[] symbols: unified array of symbols
214
597
  :param int [limit]: the maximum amount of order book entries to return
@@ -234,19 +617,19 @@ class binance(ccxt.async_support.binance):
234
617
  for i in range(0, len(symbols)):
235
618
  symbol = symbols[i]
236
619
  market = self.market(symbol)
237
- messageHash = market['lowercaseId'] + '@' + name
238
- messageHashes.append(messageHash)
239
- symbolHash = messageHash + '@' + watchOrderBookRate + 'ms'
620
+ messageHashes.append('orderbook::' + symbol)
621
+ subscriptionHash = market['lowercaseId'] + '@' + name
622
+ symbolHash = subscriptionHash + '@' + watchOrderBookRate + 'ms'
240
623
  subParams.append(symbolHash)
241
624
  messageHashesLength = len(messageHashes)
242
625
  url = self.urls['api']['ws'][type] + '/' + self.stream(type, streamHash, messageHashesLength)
243
626
  requestId = self.request_id(url)
244
- request = {
627
+ request: dict = {
245
628
  'method': 'SUBSCRIBE',
246
629
  'params': subParams,
247
630
  'id': requestId,
248
631
  }
249
- subscription = {
632
+ subscription: dict = {
250
633
  'id': str(requestId),
251
634
  'name': name,
252
635
  'symbols': symbols,
@@ -255,15 +638,156 @@ class binance(ccxt.async_support.binance):
255
638
  'type': type,
256
639
  'params': params,
257
640
  }
258
- message = self.extend(request, params)
259
- orderbook = await self.watch_multiple(url, messageHashes, message, messageHashes, subscription)
641
+ orderbook = await self.watch_multiple(url, messageHashes, self.extend(request, params), messageHashes, subscription)
260
642
  return orderbook.limit()
261
643
 
644
+ async def un_watch_order_book_for_symbols(self, symbols: List[str], params={}) -> Any:
645
+ """
646
+
647
+ https://binance-docs.github.io/apidocs/spot/en/#partial-book-depth-streams
648
+ https://binance-docs.github.io/apidocs/spot/en/#diff-depth-stream
649
+ https://binance-docs.github.io/apidocs/futures/en/#partial-book-depth-streams
650
+ https://binance-docs.github.io/apidocs/futures/en/#diff-book-depth-streams
651
+ https://binance-docs.github.io/apidocs/delivery/en/#partial-book-depth-streams
652
+ https://binance-docs.github.io/apidocs/delivery/en/#diff-book-depth-streams
653
+
654
+ unWatches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
655
+ :param str[] symbols: unified array of symbols
656
+ :param dict [params]: extra parameters specific to the exchange API endpoint
657
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
658
+ """
659
+ await self.load_markets()
660
+ symbols = self.market_symbols(symbols, None, False, True, True)
661
+ firstMarket = self.market(symbols[0])
662
+ type = firstMarket['type']
663
+ if firstMarket['contract']:
664
+ type = 'future' if firstMarket['linear'] else 'delivery'
665
+ name = 'depth'
666
+ streamHash = 'multipleOrderbook'
667
+ if symbols is not None:
668
+ streamHash += '::' + ','.join(symbols)
669
+ watchOrderBookRate = self.safe_string(self.options, 'watchOrderBookRate', '100')
670
+ subParams = []
671
+ subMessageHashes = []
672
+ messageHashes = []
673
+ for i in range(0, len(symbols)):
674
+ symbol = symbols[i]
675
+ market = self.market(symbol)
676
+ subMessageHashes.append('orderbook::' + symbol)
677
+ messageHashes.append('unsubscribe:orderbook:' + symbol)
678
+ subscriptionHash = market['lowercaseId'] + '@' + name
679
+ symbolHash = subscriptionHash + '@' + watchOrderBookRate + 'ms'
680
+ subParams.append(symbolHash)
681
+ messageHashesLength = len(subMessageHashes)
682
+ url = self.urls['api']['ws'][type] + '/' + self.stream(type, streamHash, messageHashesLength)
683
+ requestId = self.request_id(url)
684
+ request: dict = {
685
+ 'method': 'UNSUBSCRIBE',
686
+ 'params': subParams,
687
+ 'id': requestId,
688
+ }
689
+ subscription: dict = {
690
+ 'unsubscribe': True,
691
+ 'id': str(requestId),
692
+ 'symbols': symbols,
693
+ 'subMessageHashes': subMessageHashes,
694
+ 'messageHashes': messageHashes,
695
+ 'topic': 'orderbook',
696
+ }
697
+ return await self.watch_multiple(url, messageHashes, self.extend(request, params), messageHashes, subscription)
698
+
699
+ async def un_watch_order_book(self, symbol: str, params={}) -> Any:
700
+ """
701
+
702
+ https://binance-docs.github.io/apidocs/spot/en/#partial-book-depth-streams
703
+ https://binance-docs.github.io/apidocs/spot/en/#diff-depth-stream
704
+ https://binance-docs.github.io/apidocs/futures/en/#partial-book-depth-streams
705
+ https://binance-docs.github.io/apidocs/futures/en/#diff-book-depth-streams
706
+ https://binance-docs.github.io/apidocs/delivery/en/#partial-book-depth-streams
707
+ https://binance-docs.github.io/apidocs/delivery/en/#diff-book-depth-streams
708
+
709
+ unWatches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
710
+ :param str symbol: unified array of symbols
711
+ :param dict [params]: extra parameters specific to the exchange API endpoint
712
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
713
+ """
714
+ return await self.un_watch_order_book_for_symbols([symbol], params)
715
+
716
+ async def fetch_order_book_ws(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
717
+ """
718
+ fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
719
+
720
+ https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api#order-book
721
+ https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/websocket-api/Order-Book
722
+
723
+ :param str symbol: unified symbol of the market to fetch the order book for
724
+ :param int [limit]: the maximum amount of order book entries to return
725
+ :param dict [params]: extra parameters specific to the exchange API endpoint
726
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
727
+ """
728
+ await self.load_markets()
729
+ market = self.market(symbol)
730
+ payload: dict = {
731
+ 'symbol': market['id'],
732
+ }
733
+ if limit is not None:
734
+ payload['limit'] = limit
735
+ marketType = self.get_market_type('fetchOrderBookWs', market, params)
736
+ if marketType != 'future':
737
+ raise BadRequest(self.id + ' fetchOrderBookWs only supports swap markets')
738
+ url = self.urls['api']['ws']['ws-api'][marketType]
739
+ requestId = self.request_id(url)
740
+ messageHash = str(requestId)
741
+ returnRateLimits = False
742
+ returnRateLimits, params = self.handle_option_and_params(params, 'createOrderWs', 'returnRateLimits', False)
743
+ payload['returnRateLimits'] = returnRateLimits
744
+ params = self.omit(params, 'test')
745
+ message: dict = {
746
+ 'id': messageHash,
747
+ 'method': 'depth',
748
+ 'params': self.sign_params(self.extend(payload, params)),
749
+ }
750
+ subscription: dict = {
751
+ 'method': self.handle_fetch_order_book,
752
+ }
753
+ orderbook = await self.watch(url, messageHash, message, messageHash, subscription)
754
+ orderbook['symbol'] = market['symbol']
755
+ return orderbook
756
+
757
+ def handle_fetch_order_book(self, client: Client, message):
758
+ #
759
+ # {
760
+ # "id":"51e2affb-0aba-4821-ba75-f2625006eb43",
761
+ # "status":200,
762
+ # "result":{
763
+ # "lastUpdateId":1027024,
764
+ # "E":1589436922972,
765
+ # "T":1589436922959,
766
+ # "bids":[
767
+ # [
768
+ # "4.00000000",
769
+ # "431.00000000"
770
+ # ]
771
+ # ],
772
+ # "asks":[
773
+ # [
774
+ # "4.00000200",
775
+ # "12.00000000"
776
+ # ]
777
+ # ]
778
+ # }
779
+ # }
780
+ #
781
+ messageHash = self.safe_string(message, 'id')
782
+ result = self.safe_dict(message, 'result')
783
+ timestamp = self.safe_integer(result, 'T')
784
+ orderbook = self.parse_order_book(result, None, timestamp)
785
+ orderbook['nonce'] = self.safe_integer_2(result, 'lastUpdateId', 'u')
786
+ client.resolve(orderbook, messageHash)
787
+
262
788
  async def fetch_order_book_snapshot(self, client, message, subscription):
263
- name = self.safe_string(subscription, 'name')
264
789
  symbol = self.safe_string(subscription, 'symbol')
265
- market = self.market(symbol)
266
- messageHash = market['lowercaseId'] + '@' + name
790
+ messageHash = 'orderbook::' + symbol
267
791
  try:
268
792
  defaultLimit = self.safe_integer(self.options, 'watchOrderBookLimit', 1000)
269
793
  type = self.safe_value(subscription, 'type')
@@ -273,10 +797,10 @@ class binance(ccxt.async_support.binance):
273
797
  # todo: self is a synch blocking call - make it async
274
798
  # default 100, max 1000, valid limits 5, 10, 20, 50, 100, 500, 1000
275
799
  snapshot = await self.fetch_rest_order_book_safe(symbol, limit, params)
276
- orderbook = self.safe_value(self.orderbooks, symbol)
277
- if orderbook is None:
800
+ if self.safe_value(self.orderbooks, symbol) is None:
278
801
  # if the orderbook is dropped before the snapshot is received
279
802
  return
803
+ orderbook = self.orderbooks[symbol]
280
804
  orderbook.reset(snapshot)
281
805
  # unroll the accumulated deltas
282
806
  messages = orderbook.cache
@@ -350,10 +874,8 @@ class binance(ccxt.async_support.binance):
350
874
  marketId = self.safe_string(message, 's')
351
875
  market = self.safe_market(marketId, None, None, marketType)
352
876
  symbol = market['symbol']
353
- name = 'depth'
354
- messageHash = market['lowercaseId'] + '@' + name
355
- orderbook = self.safe_value(self.orderbooks, symbol)
356
- if orderbook is None:
877
+ messageHash = 'orderbook::' + symbol
878
+ if not (symbol in self.orderbooks):
357
879
  #
358
880
  # https://github.com/ccxt/ccxt/issues/6672
359
881
  #
@@ -363,6 +885,7 @@ class binance(ccxt.async_support.binance):
363
885
  # therefore it is safe to drop these premature messages.
364
886
  #
365
887
  return
888
+ orderbook = self.orderbooks[symbol]
366
889
  nonce = self.safe_integer(orderbook, 'nonce')
367
890
  if nonce is None:
368
891
  # 2. Buffer the events you receive from the stream.
@@ -389,8 +912,10 @@ class binance(ccxt.async_support.binance):
389
912
  if nonce < orderbook['nonce']:
390
913
  client.resolve(orderbook, messageHash)
391
914
  else:
392
- # todo: client.reject from handleOrderBookMessage properly
393
- raise ExchangeError(self.id + ' handleOrderBook received an out-of-order nonce')
915
+ checksum = self.handle_option('watchOrderBook', 'checksum', True)
916
+ if checksum:
917
+ # todo: client.reject from handleOrderBookMessage properly
918
+ raise ChecksumError(self.id + ' ' + self.orderbook_checksum_message(symbol))
394
919
  else:
395
920
  # future
396
921
  # 4. Drop any event where u is < lastUpdateId in the snapshot
@@ -402,8 +927,10 @@ class binance(ccxt.async_support.binance):
402
927
  if nonce <= orderbook['nonce']:
403
928
  client.resolve(orderbook, messageHash)
404
929
  else:
405
- # todo: client.reject from handleOrderBookMessage properly
406
- raise ExchangeError(self.id + ' handleOrderBook received an out-of-order nonce')
930
+ checksum = self.handle_option('watchOrderBook', 'checksum', True)
931
+ if checksum:
932
+ # todo: client.reject from handleOrderBookMessage properly
933
+ raise ChecksumError(self.id + ' ' + self.orderbook_checksum_message(symbol))
407
934
  except Exception as e:
408
935
  del self.orderbooks[symbol]
409
936
  del client.subscriptions[messageHash]
@@ -438,15 +965,34 @@ class binance(ccxt.async_support.binance):
438
965
  method = self.safe_value(subscription, 'method')
439
966
  if method is not None:
440
967
  method(client, message, subscription)
968
+ isUnSubMessage = self.safe_bool(subscription, 'unsubscribe', False)
969
+ if isUnSubMessage:
970
+ self.handle_un_subscription(client, subscription)
441
971
  return message
442
972
 
973
+ def handle_un_subscription(self, client: Client, subscription: dict):
974
+ messageHashes = self.safe_list(subscription, 'messageHashes', [])
975
+ subMessageHashes = self.safe_list(subscription, 'subMessageHashes', [])
976
+ for j in range(0, len(messageHashes)):
977
+ unsubHash = messageHashes[j]
978
+ subHash = subMessageHashes[j]
979
+ self.clean_unsubscription(client, subHash, unsubHash)
980
+ self.clean_cache(subscription)
981
+
443
982
  async def watch_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Trade]:
444
983
  """
445
984
  get the list of most recent trades for a list of symbols
985
+
986
+ https://binance-docs.github.io/apidocs/spot/en/#aggregate-trade-streams
987
+ https://binance-docs.github.io/apidocs/spot/en/#trade-streams
988
+ https://binance-docs.github.io/apidocs/futures/en/#aggregate-trade-streams
989
+ https://binance-docs.github.io/apidocs/delivery/en/#aggregate-trade-streams
990
+
446
991
  :param str[] symbols: unified symbol of the market to fetch trades for
447
992
  :param int [since]: timestamp in ms of the earliest trade to fetch
448
993
  :param int [limit]: the maximum amount of trades to fetch
449
994
  :param dict [params]: extra parameters specific to the exchange API endpoint
995
+ :param str [params.name]: the name of the method to call, 'trade' or 'aggTrade', default is 'trade'
450
996
  :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
451
997
  """
452
998
  await self.load_markets()
@@ -457,46 +1003,132 @@ class binance(ccxt.async_support.binance):
457
1003
  if symbolsLength > 200:
458
1004
  raise BadRequest(self.id + ' watchTradesForSymbols() accepts 200 symbols at most. To watch more symbols call watchTradesForSymbols() multiple times')
459
1005
  streamHash += '::' + ','.join(symbols)
460
- options = self.safe_value(self.options, 'watchTradesForSymbols', {})
461
- name = self.safe_string(options, 'name', 'trade')
1006
+ name = None
1007
+ name, params = self.handle_option_and_params(params, 'watchTradesForSymbols', 'name', 'trade')
1008
+ params = self.omit(params, 'callerMethodName')
462
1009
  firstMarket = self.market(symbols[0])
463
1010
  type = firstMarket['type']
464
1011
  if firstMarket['contract']:
465
1012
  type = 'future' if firstMarket['linear'] else 'delivery'
1013
+ messageHashes = []
466
1014
  subParams = []
467
1015
  for i in range(0, len(symbols)):
468
1016
  symbol = symbols[i]
469
1017
  market = self.market(symbol)
470
- currentMessageHash = market['lowercaseId'] + '@' + name
471
- subParams.append(currentMessageHash)
1018
+ messageHashes.append('trade::' + symbol)
1019
+ rawHash = market['lowercaseId'] + '@' + name
1020
+ subParams.append(rawHash)
472
1021
  query = self.omit(params, 'type')
473
1022
  subParamsLength = len(subParams)
474
1023
  url = self.urls['api']['ws'][type] + '/' + self.stream(type, streamHash, subParamsLength)
475
1024
  requestId = self.request_id(url)
476
- request = {
1025
+ request: dict = {
477
1026
  'method': 'SUBSCRIBE',
478
1027
  'params': subParams,
479
1028
  'id': requestId,
480
1029
  }
481
- subscribe = {
1030
+ subscribe: dict = {
482
1031
  'id': requestId,
483
1032
  }
484
- trades = await self.watch_multiple(url, subParams, self.extend(request, query), subParams, subscribe)
1033
+ trades = await self.watch_multiple(url, messageHashes, self.extend(request, query), messageHashes, subscribe)
485
1034
  if self.newUpdates:
486
1035
  first = self.safe_value(trades, 0)
487
1036
  tradeSymbol = self.safe_string(first, 'symbol')
488
1037
  limit = trades.getLimit(tradeSymbol, limit)
489
1038
  return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
490
1039
 
1040
+ async def un_watch_trades_for_symbols(self, symbols: List[str], params={}) -> Any:
1041
+ """
1042
+ unsubscribes from the trades channel
1043
+
1044
+ https://binance-docs.github.io/apidocs/spot/en/#aggregate-trade-streams
1045
+ https://binance-docs.github.io/apidocs/spot/en/#trade-streams
1046
+ https://binance-docs.github.io/apidocs/futures/en/#aggregate-trade-streams
1047
+ https://binance-docs.github.io/apidocs/delivery/en/#aggregate-trade-streams
1048
+
1049
+ :param str[] symbols: unified symbol of the market to fetch trades for
1050
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1051
+ :param str [params.name]: the name of the method to call, 'trade' or 'aggTrade', default is 'trade'
1052
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
1053
+ """
1054
+ await self.load_markets()
1055
+ symbols = self.market_symbols(symbols, None, False, True, True)
1056
+ streamHash = 'multipleTrades'
1057
+ if symbols is not None:
1058
+ symbolsLength = len(symbols)
1059
+ if symbolsLength > 200:
1060
+ raise BadRequest(self.id + ' watchTradesForSymbols() accepts 200 symbols at most. To watch more symbols call watchTradesForSymbols() multiple times')
1061
+ streamHash += '::' + ','.join(symbols)
1062
+ name = None
1063
+ name, params = self.handle_option_and_params(params, 'watchTradesForSymbols', 'name', 'trade')
1064
+ params = self.omit(params, 'callerMethodName')
1065
+ firstMarket = self.market(symbols[0])
1066
+ type = firstMarket['type']
1067
+ if firstMarket['contract']:
1068
+ type = 'future' if firstMarket['linear'] else 'delivery'
1069
+ subMessageHashes = []
1070
+ subParams = []
1071
+ messageHashes = []
1072
+ for i in range(0, len(symbols)):
1073
+ symbol = symbols[i]
1074
+ market = self.market(symbol)
1075
+ subMessageHashes.append('trade::' + symbol)
1076
+ messageHashes.append('unsubscribe:trade:' + symbol)
1077
+ rawHash = market['lowercaseId'] + '@' + name
1078
+ subParams.append(rawHash)
1079
+ query = self.omit(params, 'type')
1080
+ subParamsLength = len(subParams)
1081
+ url = self.urls['api']['ws'][type] + '/' + self.stream(type, streamHash, subParamsLength)
1082
+ requestId = self.request_id(url)
1083
+ request: dict = {
1084
+ 'method': 'UNSUBSCRIBE',
1085
+ 'params': subParams,
1086
+ 'id': requestId,
1087
+ }
1088
+ subscription: dict = {
1089
+ 'unsubscribe': True,
1090
+ 'id': str(requestId),
1091
+ 'subMessageHashes': subMessageHashes,
1092
+ 'messageHashes': messageHashes,
1093
+ 'symbols': symbols,
1094
+ 'topic': 'trades',
1095
+ }
1096
+ return await self.watch_multiple(url, messageHashes, self.extend(request, query), messageHashes, subscription)
1097
+
1098
+ async def un_watch_trades(self, symbol: str, params={}) -> Any:
1099
+ """
1100
+ unsubscribes from the trades channel
1101
+
1102
+ https://binance-docs.github.io/apidocs/spot/en/#aggregate-trade-streams
1103
+ https://binance-docs.github.io/apidocs/spot/en/#trade-streams
1104
+ https://binance-docs.github.io/apidocs/futures/en/#aggregate-trade-streams
1105
+ https://binance-docs.github.io/apidocs/delivery/en/#aggregate-trade-streams
1106
+
1107
+ :param str symbol: unified symbol of the market to fetch trades for
1108
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1109
+ :param str [params.name]: the name of the method to call, 'trade' or 'aggTrade', default is 'trade'
1110
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
1111
+ """
1112
+ await self.load_markets()
1113
+ return await self.un_watch_trades_for_symbols([symbol], params)
1114
+
491
1115
  async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
492
1116
  """
493
1117
  get the list of most recent trades for a particular symbol
1118
+
1119
+ https://binance-docs.github.io/apidocs/spot/en/#aggregate-trade-streams
1120
+ https://binance-docs.github.io/apidocs/spot/en/#trade-streams
1121
+ https://binance-docs.github.io/apidocs/futures/en/#aggregate-trade-streams
1122
+ https://binance-docs.github.io/apidocs/delivery/en/#aggregate-trade-streams
1123
+
494
1124
  :param str symbol: unified symbol of the market to fetch trades for
495
1125
  :param int [since]: timestamp in ms of the earliest trade to fetch
496
1126
  :param int [limit]: the maximum amount of trades to fetch
497
1127
  :param dict [params]: extra parameters specific to the exchange API endpoint
1128
+ :param str [params.name]: the name of the method to call, 'trade' or 'aggTrade', default is 'trade'
498
1129
  :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
499
1130
  """
1131
+ params['callerMethodName'] = 'watchTrades'
500
1132
  return await self.watch_trades_for_symbols([symbol], since, limit, params)
501
1133
 
502
1134
  def parse_ws_trade(self, trade, market=None) -> Trade:
@@ -662,9 +1294,7 @@ class binance(ccxt.async_support.binance):
662
1294
  marketId = self.safe_string(message, 's')
663
1295
  market = self.safe_market(marketId, None, None, marketType)
664
1296
  symbol = market['symbol']
665
- lowerCaseId = self.safe_string_lower(message, 's')
666
- event = self.safe_string(message, 'e')
667
- messageHash = lowerCaseId + '@' + event
1297
+ messageHash = 'trade::' + symbol
668
1298
  trade = self.parse_ws_trade(message, market)
669
1299
  tradesArray = self.safe_value(self.trades, symbol)
670
1300
  if tradesArray is None:
@@ -677,44 +1307,171 @@ class binance(ccxt.async_support.binance):
677
1307
  async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
678
1308
  """
679
1309
  watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
1310
+
1311
+ https://binance-docs.github.io/apidocs/spot/en/#kline-candlestick-data
1312
+ https://binance-docs.github.io/apidocs/futures/en/#kline-candlestick-data
1313
+ https://binance-docs.github.io/apidocs/delivery/en/#kline-candlestick-data
1314
+
680
1315
  :param str symbol: unified symbol of the market to fetch OHLCV data for
681
1316
  :param str timeframe: the length of time each candle represents
682
1317
  :param int [since]: timestamp in ms of the earliest candle to fetch
683
1318
  :param int [limit]: the maximum amount of candles to fetch
684
1319
  :param dict [params]: extra parameters specific to the exchange API endpoint
1320
+ :param dict [params.timezone]: if provided, kline intervals are interpreted in that timezone instead of UTC, example '+08:00'
685
1321
  :returns int[][]: A list of candles ordered, open, high, low, close, volume
686
1322
  """
687
1323
  await self.load_markets()
688
1324
  market = self.market(symbol)
689
- marketId = market['lowercaseId']
690
- interval = self.safe_string(self.timeframes, timeframe, timeframe)
691
- options = self.safe_value(self.options, 'watchOHLCV', {})
692
- nameOption = self.safe_string(options, 'name', 'kline')
693
- name = self.safe_string(params, 'name', nameOption)
694
- if name == 'indexPriceKline':
695
- marketId = marketId.replace('_perp', '')
696
- # weird behavior for index price kline we can't use the perp suffix
697
- params = self.omit(params, 'name')
698
- messageHash = marketId + '@' + name + '_' + interval
699
- type = market['type']
700
- if market['contract']:
701
- type = 'future' if market['linear'] else 'delivery'
702
- url = self.urls['api']['ws'][type] + '/' + self.stream(type, messageHash)
1325
+ symbol = market['symbol']
1326
+ params['callerMethodName'] = 'watchOHLCV'
1327
+ result = await self.watch_ohlcv_for_symbols([[symbol, timeframe]], since, limit, params)
1328
+ return result[symbol][timeframe]
1329
+
1330
+ async def watch_ohlcv_for_symbols(self, symbolsAndTimeframes: List[List[str]], since: Int = None, limit: Int = None, params={}):
1331
+ """
1332
+ watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
1333
+
1334
+ https://binance-docs.github.io/apidocs/spot/en/#kline-candlestick-data
1335
+ https://binance-docs.github.io/apidocs/futures/en/#kline-candlestick-data
1336
+ https://binance-docs.github.io/apidocs/delivery/en/#kline-candlestick-data
1337
+
1338
+ :param str[][] symbolsAndTimeframes: array of arrays containing unified symbols and timeframes to fetch OHLCV data for, example [['BTC/USDT', '1m'], ['LTC/USDT', '5m']]
1339
+ :param int [since]: timestamp in ms of the earliest candle to fetch
1340
+ :param int [limit]: the maximum amount of candles to fetch
1341
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1342
+ :param dict [params.timezone]: if provided, kline intervals are interpreted in that timezone instead of UTC, example '+08:00'
1343
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
1344
+ """
1345
+ await self.load_markets()
1346
+ klineType = None
1347
+ klineType, params = self.handle_param_string_2(params, 'channel', 'name', 'kline')
1348
+ symbols = self.get_list_from_object_values(symbolsAndTimeframes, 0)
1349
+ marketSymbols = self.market_symbols(symbols, None, False, False, True)
1350
+ firstMarket = self.market(marketSymbols[0])
1351
+ type = firstMarket['type']
1352
+ if firstMarket['contract']:
1353
+ type = 'future' if firstMarket['linear'] else 'delivery'
1354
+ isSpot = (type == 'spot')
1355
+ timezone = None
1356
+ timezone, params = self.handle_param_string(params, 'timezone', None)
1357
+ isUtc8 = (timezone is not None) and ((timezone == '+08:00') or Precise.string_eq(timezone, '8'))
1358
+ rawHashes = []
1359
+ messageHashes = []
1360
+ for i in range(0, len(symbolsAndTimeframes)):
1361
+ symAndTf = symbolsAndTimeframes[i]
1362
+ symbolString = symAndTf[0]
1363
+ timeframeString = symAndTf[1]
1364
+ interval = self.safe_string(self.timeframes, timeframeString, timeframeString)
1365
+ market = self.market(symbolString)
1366
+ marketId = market['lowercaseId']
1367
+ if klineType == 'indexPriceKline':
1368
+ # weird behavior for index price kline we can't use the perp suffix
1369
+ marketId = marketId.replace('_perp', '')
1370
+ shouldUseUTC8 = (isUtc8 and isSpot)
1371
+ suffix = '@+08:00'
1372
+ utcSuffix = suffix if shouldUseUTC8 else ''
1373
+ rawHashes.append(marketId + '@' + klineType + '_' + interval + utcSuffix)
1374
+ messageHashes.append('ohlcv::' + market['symbol'] + '::' + timeframeString)
1375
+ url = self.urls['api']['ws'][type] + '/' + self.stream(type, 'multipleOHLCV')
703
1376
  requestId = self.request_id(url)
704
1377
  request = {
705
1378
  'method': 'SUBSCRIBE',
706
- 'params': [
707
- messageHash,
708
- ],
1379
+ 'params': rawHashes,
709
1380
  'id': requestId,
710
1381
  }
711
1382
  subscribe = {
712
1383
  'id': requestId,
713
1384
  }
714
- ohlcv = await self.watch(url, messageHash, self.extend(request, params), messageHash, subscribe)
1385
+ params = self.omit(params, 'callerMethodName')
1386
+ symbol, timeframe, candles = await self.watch_multiple(url, messageHashes, self.extend(request, params), messageHashes, subscribe)
715
1387
  if self.newUpdates:
716
- limit = ohlcv.getLimit(symbol, limit)
717
- return self.filter_by_since_limit(ohlcv, since, limit, 0, True)
1388
+ limit = candles.getLimit(symbol, limit)
1389
+ filtered = self.filter_by_since_limit(candles, since, limit, 0, True)
1390
+ return self.create_ohlcv_object(symbol, timeframe, filtered)
1391
+
1392
+ async def un_watch_ohlcv_for_symbols(self, symbolsAndTimeframes: List[List[str]], params={}) -> Any:
1393
+ """
1394
+ unWatches historical candlestick data containing the open, high, low, and close price, and the volume of a market
1395
+
1396
+ https://binance-docs.github.io/apidocs/spot/en/#kline-candlestick-data
1397
+ https://binance-docs.github.io/apidocs/futures/en/#kline-candlestick-data
1398
+ https://binance-docs.github.io/apidocs/delivery/en/#kline-candlestick-data
1399
+
1400
+ :param str[][] symbolsAndTimeframes: array of arrays containing unified symbols and timeframes to fetch OHLCV data for, example [['BTC/USDT', '1m'], ['LTC/USDT', '5m']]
1401
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1402
+ :param dict [params.timezone]: if provided, kline intervals are interpreted in that timezone instead of UTC, example '+08:00'
1403
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
1404
+ """
1405
+ await self.load_markets()
1406
+ klineType = None
1407
+ klineType, params = self.handle_param_string_2(params, 'channel', 'name', 'kline')
1408
+ symbols = self.get_list_from_object_values(symbolsAndTimeframes, 0)
1409
+ marketSymbols = self.market_symbols(symbols, None, False, False, True)
1410
+ firstMarket = self.market(marketSymbols[0])
1411
+ type = firstMarket['type']
1412
+ if firstMarket['contract']:
1413
+ type = 'future' if firstMarket['linear'] else 'delivery'
1414
+ isSpot = (type == 'spot')
1415
+ timezone = None
1416
+ timezone, params = self.handle_param_string(params, 'timezone', None)
1417
+ isUtc8 = (timezone is not None) and ((timezone == '+08:00') or Precise.string_eq(timezone, '8'))
1418
+ rawHashes = []
1419
+ subMessageHashes = []
1420
+ messageHashes = []
1421
+ for i in range(0, len(symbolsAndTimeframes)):
1422
+ symAndTf = symbolsAndTimeframes[i]
1423
+ symbolString = symAndTf[0]
1424
+ timeframeString = symAndTf[1]
1425
+ interval = self.safe_string(self.timeframes, timeframeString, timeframeString)
1426
+ market = self.market(symbolString)
1427
+ marketId = market['lowercaseId']
1428
+ if klineType == 'indexPriceKline':
1429
+ # weird behavior for index price kline we can't use the perp suffix
1430
+ marketId = marketId.replace('_perp', '')
1431
+ shouldUseUTC8 = (isUtc8 and isSpot)
1432
+ suffix = '@+08:00'
1433
+ utcSuffix = suffix if shouldUseUTC8 else ''
1434
+ rawHashes.append(marketId + '@' + klineType + '_' + interval + utcSuffix)
1435
+ subMessageHashes.append('ohlcv::' + market['symbol'] + '::' + timeframeString)
1436
+ messageHashes.append('unsubscribe::ohlcv::' + market['symbol'] + '::' + timeframeString)
1437
+ url = self.urls['api']['ws'][type] + '/' + self.stream(type, 'multipleOHLCV')
1438
+ requestId = self.request_id(url)
1439
+ request = {
1440
+ 'method': 'UNSUBSCRIBE',
1441
+ 'params': rawHashes,
1442
+ 'id': requestId,
1443
+ }
1444
+ subscribe = {
1445
+ 'unsubscribe': True,
1446
+ 'id': str(requestId),
1447
+ 'symbols': symbols,
1448
+ 'symbolsAndTimeframes': symbolsAndTimeframes,
1449
+ 'subMessageHashes': subMessageHashes,
1450
+ 'messageHashes': messageHashes,
1451
+ 'topic': 'ohlcv',
1452
+ }
1453
+ params = self.omit(params, 'callerMethodName')
1454
+ return await self.watch_multiple(url, messageHashes, self.extend(request, params), messageHashes, subscribe)
1455
+
1456
+ async def un_watch_ohlcv(self, symbol: str, timeframe='1m', params={}) -> Any:
1457
+ """
1458
+ unWatches historical candlestick data containing the open, high, low, and close price, and the volume of a market
1459
+
1460
+ https://binance-docs.github.io/apidocs/spot/en/#kline-candlestick-data
1461
+ https://binance-docs.github.io/apidocs/futures/en/#kline-candlestick-data
1462
+ https://binance-docs.github.io/apidocs/delivery/en/#kline-candlestick-data
1463
+
1464
+ :param str symbol: unified symbol of the market to fetch OHLCV data for
1465
+ :param str timeframe: the length of time each candle represents
1466
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1467
+ :param dict [params.timezone]: if provided, kline intervals are interpreted in that timezone instead of UTC, example '+08:00'
1468
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
1469
+ """
1470
+ await self.load_markets()
1471
+ market = self.market(symbol)
1472
+ symbol = market['symbol']
1473
+ params['callerMethodName'] = 'watchOHLCV'
1474
+ return await self.un_watch_ohlcv_for_symbols([[symbol, timeframe]], params)
718
1475
 
719
1476
  def handle_ohlcv(self, client: Client, message):
720
1477
  #
@@ -744,7 +1501,7 @@ class binance(ccxt.async_support.binance):
744
1501
  # }
745
1502
  #
746
1503
  event = self.safe_string(message, 'e')
747
- eventMap = {
1504
+ eventMap: dict = {
748
1505
  'indexPrice_kline': 'indexPriceKline',
749
1506
  'markPrice_kline': 'markPriceKline',
750
1507
  }
@@ -754,11 +1511,9 @@ class binance(ccxt.async_support.binance):
754
1511
  if event == 'indexPriceKline':
755
1512
  # indexPriceKline doesn't have the _PERP suffix
756
1513
  marketId = self.safe_string(message, 'ps')
757
- lowercaseMarketId = marketId.lower()
758
1514
  interval = self.safe_string(kline, 'i')
759
1515
  # use a reverse lookup in a static map instead
760
- timeframe = self.find_timeframe(interval)
761
- messageHash = lowercaseMarketId + '@' + event + '_' + interval
1516
+ unifiedTimeframe = self.find_timeframe(interval)
762
1517
  parsed = [
763
1518
  self.safe_integer(kline, 't'),
764
1519
  self.safe_float(kline, 'o'),
@@ -770,38 +1525,82 @@ class binance(ccxt.async_support.binance):
770
1525
  isSpot = ((client.url.find('/stream') > -1) or (client.url.find('/testnet.binance') > -1))
771
1526
  marketType = 'spot' if (isSpot) else 'contract'
772
1527
  symbol = self.safe_symbol(marketId, None, None, marketType)
1528
+ messageHash = 'ohlcv::' + symbol + '::' + unifiedTimeframe
773
1529
  self.ohlcvs[symbol] = self.safe_value(self.ohlcvs, symbol, {})
774
- stored = self.safe_value(self.ohlcvs[symbol], timeframe)
1530
+ stored = self.safe_value(self.ohlcvs[symbol], unifiedTimeframe)
775
1531
  if stored is None:
776
1532
  limit = self.safe_integer(self.options, 'OHLCVLimit', 1000)
777
1533
  stored = ArrayCacheByTimestamp(limit)
778
- self.ohlcvs[symbol][timeframe] = stored
1534
+ self.ohlcvs[symbol][unifiedTimeframe] = stored
779
1535
  stored.append(parsed)
780
- client.resolve(stored, messageHash)
1536
+ resolveData = [symbol, unifiedTimeframe, stored]
1537
+ client.resolve(resolveData, messageHash)
1538
+
1539
+ async def fetch_ticker_ws(self, symbol: str, params={}) -> Ticker:
1540
+ """
1541
+ fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
1542
+ :param str symbol: unified symbol of the market to fetch the ticker for
1543
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1544
+ :param str [params.method]: method to use can be ticker.price or ticker.book
1545
+ :param boolean [params.returnRateLimits]: return the rate limits for the exchange
1546
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
1547
+ """
1548
+ await self.load_markets()
1549
+ market = self.market(symbol)
1550
+ payload: dict = {
1551
+ 'symbol': market['id'],
1552
+ }
1553
+ type = self.get_market_type('fetchTickerWs', market, params)
1554
+ if type != 'future':
1555
+ raise BadRequest(self.id + ' fetchTickerWs only supports swap markets')
1556
+ url = self.urls['api']['ws']['ws-api'][type]
1557
+ requestId = self.request_id(url)
1558
+ messageHash = str(requestId)
1559
+ subscription: dict = {
1560
+ 'method': self.handle_ticker_ws,
1561
+ }
1562
+ returnRateLimits = False
1563
+ returnRateLimits, params = self.handle_option_and_params(params, 'fetchTickerWs', 'returnRateLimits', False)
1564
+ payload['returnRateLimits'] = returnRateLimits
1565
+ params = self.omit(params, 'test')
1566
+ method = None
1567
+ method, params = self.handle_option_and_params(params, 'fetchTickerWs', 'method', 'ticker.book')
1568
+ message: dict = {
1569
+ 'id': messageHash,
1570
+ 'method': method,
1571
+ 'params': self.sign_params(self.extend(payload, params)),
1572
+ }
1573
+ ticker = await self.watch(url, messageHash, message, messageHash, subscription)
1574
+ return ticker
781
1575
 
782
1576
  async def fetch_ohlcv_ws(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
783
1577
  """
784
- :see: https://binance-docs.github.io/apidocs/websocket_api/en/#klines
785
1578
  query historical candlestick data containing the open, high, low, and close price, and the volume of a market
1579
+
1580
+ https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api#klines
1581
+
786
1582
  :param str symbol: unified symbol of the market to query OHLCV data for
787
1583
  :param str timeframe: the length of time each candle represents
788
1584
  :param int since: timestamp in ms of the earliest candle to fetch
789
1585
  :param int limit: the maximum amount of candles to fetch
790
1586
  :param dict params: extra parameters specific to the exchange API endpoint
791
1587
  :param int params['until']: timestamp in ms of the earliest candle to fetch
792
- *
793
- * EXCHANGE SPECIFIC PARAMETERS
1588
+
1589
+ EXCHANGE SPECIFIC PARAMETERS
794
1590
  :param str params['timeZone']: default=0(UTC)
795
1591
  :returns int[][]: A list of candles ordered, open, high, low, close, volume
796
1592
  """
797
1593
  await self.load_markets()
798
- self.check_is_spot('fetchOHLCVWs', symbol, params)
799
- url = self.urls['api']['ws']['ws']
1594
+ market = self.market(symbol)
1595
+ marketType = self.get_market_type('fetchOHLCVWs', market, params)
1596
+ if marketType != 'spot' and marketType != 'future':
1597
+ raise BadRequest(self.id + ' fetchOHLCVWs only supports spot or swap markets')
1598
+ url = self.urls['api']['ws']['ws-api'][marketType]
800
1599
  requestId = self.request_id(url)
801
1600
  messageHash = str(requestId)
802
1601
  returnRateLimits = False
803
1602
  returnRateLimits, params = self.handle_option_and_params(params, 'fetchOHLCVWs', 'returnRateLimits', False)
804
- payload = {
1603
+ payload: dict = {
805
1604
  'symbol': self.market_id(symbol),
806
1605
  'returnRateLimits': returnRateLimits,
807
1606
  'interval': self.timeframes[timeframe],
@@ -814,12 +1613,12 @@ class binance(ccxt.async_support.binance):
814
1613
  payload['limit'] = limit
815
1614
  if until is not None:
816
1615
  payload['endTime'] = until
817
- message = {
1616
+ message: dict = {
818
1617
  'id': messageHash,
819
1618
  'method': 'klines',
820
1619
  'params': self.extend(payload, params),
821
1620
  }
822
- subscription = {
1621
+ subscription: dict = {
823
1622
  'method': self.handle_fetch_ohlcv,
824
1623
  }
825
1624
  return await self.watch(url, messageHash, message, messageHash, subscription)
@@ -864,92 +1663,281 @@ class binance(ccxt.async_support.binance):
864
1663
 
865
1664
  async def watch_ticker(self, symbol: str, params={}) -> Ticker:
866
1665
  """
1666
+
1667
+ https://binance-docs.github.io/apidocs/spot/en/#individual-symbol-mini-ticker-stream
1668
+ https://binance-docs.github.io/apidocs/spot/en/#individual-symbol-ticker-streams
1669
+ https://binance-docs.github.io/apidocs/futures/en/#all-market-mini-tickers-stream
1670
+ https://binance-docs.github.io/apidocs/futures/en/#individual-symbol-ticker-streams
1671
+ https://binance-docs.github.io/apidocs/delivery/en/#all-market-mini-tickers-stream
1672
+ https://binance-docs.github.io/apidocs/delivery/en/#individual-symbol-ticker-streams
1673
+
867
1674
  watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
868
1675
  :param str symbol: unified symbol of the market to fetch the ticker for
869
1676
  :param dict [params]: extra parameters specific to the exchange API endpoint
870
- :param str [params.name]: stream to use can be ticker or bookTicker
1677
+ :param str [params.name]: stream to use can be ticker or miniTicker
871
1678
  :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
872
1679
  """
873
1680
  await self.load_markets()
874
- market = self.market(symbol)
875
- marketId = market['lowercaseId']
876
- type = market['type']
877
- if market['contract']:
878
- type = 'future' if market['linear'] else 'delivery'
879
- options = self.safe_value(self.options, 'watchTicker', {})
880
- name = self.safe_string(options, 'name', 'ticker')
881
- name = self.safe_string(params, 'name', name)
882
- params = self.omit(params, 'name')
883
- messageHash = marketId + '@' + name
884
- url = self.urls['api']['ws'][type] + '/' + self.stream(type, messageHash)
1681
+ symbol = self.symbol(symbol)
1682
+ tickers = await self.watch_tickers([symbol], self.extend(params, {'callerMethodName': 'watchTicker'}))
1683
+ return tickers[symbol]
1684
+
1685
+ async def watch_mark_price(self, symbol: str, params={}) -> Ticker:
1686
+ """
1687
+
1688
+ https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Mark-Price-Stream
1689
+
1690
+ watches a mark price for a specific market
1691
+ :param str symbol: unified symbol of the market to fetch the ticker for
1692
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1693
+ :param boolean [params.use1sFreq]: *default is True* if set to True, the mark price will be updated every second, otherwise every 3 seconds
1694
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
1695
+ """
1696
+ await self.load_markets()
1697
+ symbol = self.symbol(symbol)
1698
+ tickers = await self.watch_mark_prices([symbol], self.extend(params, {'callerMethodName': 'watchMarkPrice'}))
1699
+ return tickers[symbol]
1700
+
1701
+ async def watch_mark_prices(self, symbols: Strings = None, params={}) -> Tickers:
1702
+ """
1703
+
1704
+ https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Mark-Price-Stream-for-All-market
1705
+
1706
+ watches the mark price for all markets
1707
+ :param str[] symbols: unified symbol of the market to fetch the ticker for
1708
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1709
+ :param boolean [params.use1sFreq]: *default is True* if set to True, the mark price will be updated every second, otherwise every 3 seconds
1710
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
1711
+ """
1712
+ channelName = None
1713
+ # for now watchmarkPrice uses the same messageHash
1714
+ # so it's impossible to watch both at the same time
1715
+ # refactor self to use different messageHashes
1716
+ channelName, params = self.handle_option_and_params(params, 'watchMarkPrices', 'name', 'markPrice')
1717
+ newTickers = await self.watch_multi_ticker_helper('watchMarkPrices', channelName, symbols, params)
1718
+ if self.newUpdates:
1719
+ return newTickers
1720
+ return self.filter_by_array(self.tickers, 'symbol', symbols)
1721
+
1722
+ async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
1723
+ """
1724
+
1725
+ https://binance-docs.github.io/apidocs/spot/en/#individual-symbol-mini-ticker-stream
1726
+ https://binance-docs.github.io/apidocs/spot/en/#individual-symbol-ticker-streams
1727
+ https://binance-docs.github.io/apidocs/futures/en/#all-market-mini-tickers-stream
1728
+ https://binance-docs.github.io/apidocs/futures/en/#individual-symbol-ticker-streams
1729
+ https://binance-docs.github.io/apidocs/delivery/en/#all-market-mini-tickers-stream
1730
+ https://binance-docs.github.io/apidocs/delivery/en/#individual-symbol-ticker-streams
1731
+
1732
+ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
1733
+ :param str[] symbols: unified symbol of the market to fetch the ticker for
1734
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1735
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
1736
+ """
1737
+ channelName = None
1738
+ channelName, params = self.handle_option_and_params(params, 'watchTickers', 'name', 'ticker')
1739
+ if channelName == 'bookTicker':
1740
+ raise BadRequest(self.id + ' deprecation notice - to subscribe for bids-asks, use watch_bids_asks() method instead')
1741
+ newTickers = await self.watch_multi_ticker_helper('watchTickers', channelName, symbols, params)
1742
+ if self.newUpdates:
1743
+ return newTickers
1744
+ return self.filter_by_array(self.tickers, 'symbol', symbols)
1745
+
1746
+ async def un_watch_tickers(self, symbols: Strings = None, params={}) -> Any:
1747
+ """
1748
+
1749
+ https://binance-docs.github.io/apidocs/spot/en/#individual-symbol-mini-ticker-stream
1750
+ https://binance-docs.github.io/apidocs/spot/en/#individual-symbol-ticker-streams
1751
+ https://binance-docs.github.io/apidocs/futures/en/#all-market-mini-tickers-stream
1752
+ https://binance-docs.github.io/apidocs/futures/en/#individual-symbol-ticker-streams
1753
+ https://binance-docs.github.io/apidocs/delivery/en/#all-market-mini-tickers-stream
1754
+ https://binance-docs.github.io/apidocs/delivery/en/#individual-symbol-ticker-streams
1755
+
1756
+ unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
1757
+ :param str[] symbols: unified symbol of the market to fetch the ticker for
1758
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1759
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
1760
+ """
1761
+ channelName = None
1762
+ channelName, params = self.handle_option_and_params(params, 'watchTickers', 'name', 'ticker')
1763
+ if channelName == 'bookTicker':
1764
+ raise BadRequest(self.id + ' deprecation notice - to subscribe for bids-asks, use watch_bids_asks() method instead')
1765
+ await self.load_markets()
1766
+ methodName = 'watchTickers'
1767
+ symbols = self.market_symbols(symbols, None, True, False, True)
1768
+ firstMarket = None
1769
+ marketType = None
1770
+ symbolsDefined = (symbols is not None)
1771
+ if symbolsDefined:
1772
+ firstMarket = self.market(symbols[0])
1773
+ marketType, params = self.handle_market_type_and_params(methodName, firstMarket, params)
1774
+ subType = None
1775
+ subType, params = self.handle_sub_type_and_params(methodName, firstMarket, params)
1776
+ rawMarketType = None
1777
+ if self.isLinear(marketType, subType):
1778
+ rawMarketType = 'future'
1779
+ elif self.isInverse(marketType, subType):
1780
+ rawMarketType = 'delivery'
1781
+ elif marketType == 'spot':
1782
+ rawMarketType = marketType
1783
+ else:
1784
+ raise NotSupported(self.id + ' ' + methodName + '() does not support options markets')
1785
+ isBidAsk = (channelName == 'bookTicker')
1786
+ subscriptionArgs = []
1787
+ subMessageHashes = []
1788
+ messageHashes = []
1789
+ if symbolsDefined:
1790
+ for i in range(0, len(symbols)):
1791
+ symbol = symbols[i]
1792
+ market = self.market(symbol)
1793
+ subscriptionArgs.append(market['lowercaseId'] + '@' + channelName)
1794
+ subMessageHashes.append(self.get_message_hash(channelName, market['symbol'], isBidAsk))
1795
+ messageHashes.append('unsubscribe:ticker:' + symbol)
1796
+ else:
1797
+ if isBidAsk:
1798
+ if marketType == 'spot':
1799
+ raise ArgumentsRequired(self.id + ' ' + methodName + '() requires symbols for self channel for spot markets')
1800
+ subscriptionArgs.append('!' + channelName)
1801
+ else:
1802
+ subscriptionArgs.append('!' + channelName + '@arr')
1803
+ subMessageHashes.append(self.get_message_hash(channelName, None, isBidAsk))
1804
+ messageHashes.append('unsubscribe:ticker')
1805
+ streamHash = channelName
1806
+ if symbolsDefined:
1807
+ streamHash = channelName + '::' + ','.join(symbols)
1808
+ url = self.urls['api']['ws'][rawMarketType] + '/' + self.stream(rawMarketType, streamHash)
885
1809
  requestId = self.request_id(url)
886
- request = {
887
- 'method': 'SUBSCRIBE',
888
- 'params': [
889
- messageHash,
890
- ],
1810
+ request: dict = {
1811
+ 'method': 'UNSUBSCRIBE',
1812
+ 'params': subscriptionArgs,
891
1813
  'id': requestId,
892
1814
  }
893
- subscribe = {
894
- 'id': requestId,
1815
+ subscription: dict = {
1816
+ 'unsubscribe': True,
1817
+ 'id': str(requestId),
1818
+ 'subMessageHashes': subMessageHashes,
1819
+ 'messageHashes': subMessageHashes,
1820
+ 'symbols': symbols,
1821
+ 'topic': 'ticker',
895
1822
  }
896
- return await self.watch(url, messageHash, self.extend(request, params), messageHash, subscribe)
1823
+ return await self.watch_multiple(url, subMessageHashes, self.extend(request, params), subMessageHashes, subscription)
897
1824
 
898
- async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
1825
+ async def un_watch_ticker(self, symbol: str, params={}) -> Any:
899
1826
  """
900
- watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
1827
+
1828
+ https://binance-docs.github.io/apidocs/spot/en/#individual-symbol-mini-ticker-stream
1829
+ https://binance-docs.github.io/apidocs/spot/en/#individual-symbol-ticker-streams
1830
+ https://binance-docs.github.io/apidocs/futures/en/#all-market-mini-tickers-stream
1831
+ https://binance-docs.github.io/apidocs/futures/en/#individual-symbol-ticker-streams
1832
+ https://binance-docs.github.io/apidocs/delivery/en/#all-market-mini-tickers-stream
1833
+ https://binance-docs.github.io/apidocs/delivery/en/#individual-symbol-ticker-streams
1834
+
1835
+ unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
1836
+ :param str symbol: unified symbol of the market to fetch the ticker for
1837
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1838
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
1839
+ """
1840
+ return await self.un_watch_tickers([symbol], params)
1841
+
1842
+ async def watch_bids_asks(self, symbols: Strings = None, params={}) -> Tickers:
1843
+ """
1844
+ watches best bid & ask for symbols
1845
+
1846
+ https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api#symbol-order-book-ticker
1847
+ https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/All-Book-Tickers-Stream
1848
+ https://developers.binance.com/docs/derivatives/coin-margined-futures/websocket-market-streams/All-Book-Tickers-Stream
1849
+
901
1850
  :param str[] symbols: unified symbol of the market to fetch the ticker for
902
1851
  :param dict [params]: extra parameters specific to the exchange API endpoint
903
1852
  :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
904
1853
  """
905
1854
  await self.load_markets()
906
- symbols = self.market_symbols(symbols, None, True, True, True)
907
- marketIds = self.market_ids(symbols)
908
- market = None
909
- type = None
910
- if symbols is not None:
911
- market = self.market(symbols[0])
912
- type, params = self.handle_market_type_and_params('watchTickers', market, params)
1855
+ symbols = self.market_symbols(symbols, None, True, False, True)
1856
+ result = await self.watch_multi_ticker_helper('watchBidsAsks', 'bookTicker', symbols, params)
1857
+ if self.newUpdates:
1858
+ return result
1859
+ return self.filter_by_array(self.bidsasks, 'symbol', symbols)
1860
+
1861
+ async def watch_multi_ticker_helper(self, methodName, channelName: str, symbols: Strings = None, params={}):
1862
+ await self.load_markets()
1863
+ symbols = self.market_symbols(symbols, None, True, False, True)
1864
+ isBidAsk = (channelName == 'bookTicker')
1865
+ isMarkPrice = (channelName == 'markPrice')
1866
+ use1sFreq = self.safe_bool(params, 'use1sFreq', True)
1867
+ firstMarket = None
1868
+ marketType = None
1869
+ symbolsDefined = (symbols is not None)
1870
+ if symbolsDefined:
1871
+ firstMarket = self.market(symbols[0])
1872
+ defaultMarket = 'swap' if (isMarkPrice) else None
1873
+ marketType, params = self.handle_market_type_and_params(methodName, firstMarket, params, defaultMarket)
913
1874
  subType = None
914
- subType, params = self.handle_sub_type_and_params('watchTickers', market, params)
915
- if self.isLinear(type, subType):
916
- type = 'future'
917
- elif self.isInverse(type, subType):
918
- type = 'delivery'
919
- options = self.safe_value(self.options, 'watchTickers', {})
920
- name = self.safe_string(options, 'name', 'ticker')
921
- name = self.safe_string(params, 'name', name)
922
- params = self.omit(params, 'name')
923
- wsParams = []
924
- messageHash = 'tickers'
925
- if symbols is not None:
926
- messageHash = 'tickers::' + ','.join(symbols)
927
- if name == 'bookTicker':
928
- if marketIds is None:
929
- raise ArgumentsRequired(self.id + ' watchTickers() requires symbols for bookTicker')
930
- # simulate watchTickers with subscribe multiple individual bookTicker topic
931
- for i in range(0, len(marketIds)):
932
- wsParams.append(marketIds[i].lower() + '@bookTicker')
1875
+ subType, params = self.handle_sub_type_and_params(methodName, firstMarket, params)
1876
+ rawMarketType = None
1877
+ if self.isLinear(marketType, subType):
1878
+ rawMarketType = 'future'
1879
+ elif self.isInverse(marketType, subType):
1880
+ rawMarketType = 'delivery'
1881
+ elif marketType == 'spot':
1882
+ rawMarketType = marketType
1883
+ else:
1884
+ raise NotSupported(self.id + ' ' + methodName + '() does not support options markets')
1885
+ subscriptionArgs = []
1886
+ messageHashes = []
1887
+ suffix = ''
1888
+ if isMarkPrice:
1889
+ suffix = '@1s' if (use1sFreq) else ''
1890
+ if symbolsDefined:
1891
+ for i in range(0, len(symbols)):
1892
+ symbol = symbols[i]
1893
+ market = self.market(symbol)
1894
+ subscriptionArgs.append(market['lowercaseId'] + '@' + channelName + suffix)
1895
+ messageHashes.append(self.get_message_hash(channelName, market['symbol'], isBidAsk))
933
1896
  else:
934
- wsParams = [
935
- '!' + name + '@arr',
936
- ]
937
- url = self.urls['api']['ws'][type] + '/' + self.stream(type, messageHash)
1897
+ if isBidAsk:
1898
+ if marketType == 'spot':
1899
+ raise ArgumentsRequired(self.id + ' ' + methodName + '() requires symbols for self channel for spot markets')
1900
+ subscriptionArgs.append('!' + channelName)
1901
+ elif isMarkPrice:
1902
+ subscriptionArgs.append('!' + channelName + '@arr' + suffix)
1903
+ else:
1904
+ subscriptionArgs.append('!' + channelName + '@arr')
1905
+ messageHashes.append(self.get_message_hash(channelName, None, isBidAsk))
1906
+ streamHash = channelName
1907
+ if symbolsDefined:
1908
+ streamHash = channelName + '::' + ','.join(symbols)
1909
+ url = self.urls['api']['ws'][rawMarketType] + '/' + self.stream(rawMarketType, streamHash)
938
1910
  requestId = self.request_id(url)
939
- request = {
1911
+ request: dict = {
940
1912
  'method': 'SUBSCRIBE',
941
- 'params': wsParams,
1913
+ 'params': subscriptionArgs,
942
1914
  'id': requestId,
943
1915
  }
944
- subscribe = {
1916
+ subscribe: dict = {
945
1917
  'id': requestId,
946
1918
  }
947
- newTickers = await self.watch(url, messageHash, self.extend(request, params), messageHash, subscribe)
948
- if self.newUpdates:
949
- return newTickers
950
- return self.filter_by_array(self.tickers, 'symbol', symbols)
1919
+ result = await self.watch_multiple(url, messageHashes, self.deep_extend(request, params), subscriptionArgs, subscribe)
1920
+ # for efficiency, we have two type of returned structure here - if symbols array was provided, then individual
1921
+ # ticker dict comes in, otherwise all-tickers dict comes in
1922
+ if not symbolsDefined:
1923
+ return result
1924
+ else:
1925
+ newDict: dict = {}
1926
+ newDict[result['symbol']] = result
1927
+ return newDict
951
1928
 
952
1929
  def parse_ws_ticker(self, message, marketType):
1930
+ # markPrice
1931
+ # {
1932
+ # "e": "markPriceUpdate", # Event type
1933
+ # "E": 1562305380000, # Event time
1934
+ # "s": "BTCUSDT", # Symbol
1935
+ # "p": "11794.15000000", # Mark price
1936
+ # "i": "11784.62659091", # Index price
1937
+ # "P": "11784.25641265", # Estimated Settle Price, only useful in the last hour before the settlement starts
1938
+ # "r": "0.00038167", # Funding rate
1939
+ # "T": 1562306400000 # Next funding time
1940
+ # }
953
1941
  #
954
1942
  # ticker
955
1943
  # {
@@ -990,31 +1978,57 @@ class binance(ccxt.async_support.binance):
990
1978
  # "v": "2109995.32000000",
991
1979
  # "q": "2019254.05788000"
992
1980
  # }
993
- #
1981
+ # fetchTickerWs
1982
+ # {
1983
+ # "symbol":"BTCUSDT",
1984
+ # "price":"72606.70",
1985
+ # "time":1712526204284
1986
+ # }
1987
+ # fetchTickerWs - ticker.book
1988
+ # {
1989
+ # "lastUpdateId":1027024,
1990
+ # "symbol":"BTCUSDT",
1991
+ # "bidPrice":"4.00000000",
1992
+ # "bidQty":"431.00000000",
1993
+ # "askPrice":"4.00000200",
1994
+ # "askQty":"9.00000000",
1995
+ # "time":1589437530011,
1996
+ # }
1997
+ #
1998
+ marketId = self.safe_string_2(message, 's', 'symbol')
1999
+ symbol = self.safe_symbol(marketId, None, None, marketType)
994
2000
  event = self.safe_string(message, 'e', 'bookTicker')
995
2001
  if event == '24hrTicker':
996
2002
  event = 'ticker'
2003
+ if event == 'markPriceUpdate':
2004
+ # handle self separately because some fields clash with the ticker fields
2005
+ return self.safe_ticker({
2006
+ 'symbol': symbol,
2007
+ 'timestamp': self.safe_integer(message, 'E'),
2008
+ 'datetime': self.iso8601(self.safe_integer(message, 'E')),
2009
+ 'info': message,
2010
+ 'markPrice': self.safe_string(message, 'p'),
2011
+ 'indexPrice': self.safe_string(message, 'i'),
2012
+ })
997
2013
  timestamp = None
998
2014
  if event == 'bookTicker':
999
2015
  # take the event timestamp, if available, for spot tickers it is not
1000
- timestamp = self.safe_integer(message, 'E')
2016
+ timestamp = self.safe_integer_2(message, 'E', 'time')
1001
2017
  else:
1002
2018
  # take the timestamp of the closing price for candlestick streams
1003
- timestamp = self.safe_integer_2(message, 'C', 'E')
1004
- marketId = self.safe_string(message, 's')
1005
- symbol = self.safe_symbol(marketId, None, None, marketType)
2019
+ timestamp = self.safe_integer_n(message, ['C', 'E', 'time'])
1006
2020
  market = self.safe_market(marketId, None, None, marketType)
1007
- last = self.safe_string(message, 'c')
2021
+ last = self.safe_string_2(message, 'c', 'price')
1008
2022
  return self.safe_ticker({
1009
2023
  'symbol': symbol,
1010
2024
  'timestamp': timestamp,
1011
2025
  'datetime': self.iso8601(timestamp),
1012
2026
  'high': self.safe_string(message, 'h'),
1013
2027
  'low': self.safe_string(message, 'l'),
1014
- 'bid': self.safe_string(message, 'b'),
1015
- 'bidVolume': self.safe_string(message, 'B'),
1016
- 'ask': self.safe_string(message, 'a'),
1017
- 'askVolume': self.safe_string(message, 'A'),
2028
+ 'bid': self.safe_string_2(message, 'b', 'bidPrice'),
2029
+ 'bidVolume': self.safe_string_2(message, 'B', 'bidQty'),
2030
+ 'ask': self.safe_string_2(message, 'a', 'askPrice'),
2031
+ 'askVolume': self.safe_string_2(message, 'A', 'askQty'),
1018
2032
  'vwap': self.safe_string(message, 'w'),
1019
2033
  'open': self.safe_string(message, 'o'),
1020
2034
  'close': last,
@@ -1028,11 +2042,56 @@ class binance(ccxt.async_support.binance):
1028
2042
  'info': message,
1029
2043
  }, market)
1030
2044
 
1031
- def handle_ticker(self, client: Client, message):
2045
+ def handle_ticker_ws(self, client: Client, message):
2046
+ #
2047
+ # ticker.price
2048
+ # {
2049
+ # "id":"1",
2050
+ # "status":200,
2051
+ # "result":{
2052
+ # "symbol":"BTCUSDT",
2053
+ # "price":"73178.50",
2054
+ # "time":1712527052374
2055
+ # }
2056
+ # }
2057
+ # ticker.book
2058
+ # {
2059
+ # "id":"9d32157c-a556-4d27-9866-66760a174b57",
2060
+ # "status":200,
2061
+ # "result":{
2062
+ # "lastUpdateId":1027024,
2063
+ # "symbol":"BTCUSDT",
2064
+ # "bidPrice":"4.00000000",
2065
+ # "bidQty":"431.00000000",
2066
+ # "askPrice":"4.00000200",
2067
+ # "askQty":"9.00000000",
2068
+ # "time":1589437530011 # Transaction time
2069
+ # }
2070
+ # }
2071
+ #
2072
+ messageHash = self.safe_string(message, 'id')
2073
+ result = self.safe_value(message, 'result', {})
2074
+ ticker = self.parse_ws_ticker(result, 'future')
2075
+ client.resolve(ticker, messageHash)
2076
+
2077
+ def handle_bids_asks(self, client: Client, message):
2078
+ #
2079
+ # arrives one symbol dict or array of symbol dicts
2080
+ #
2081
+ # {
2082
+ # "u": 7488717758,
2083
+ # "s": "BTCUSDT",
2084
+ # "b": "28621.74000000",
2085
+ # "B": "1.43278800",
2086
+ # "a": "28621.75000000",
2087
+ # "A": "2.52500800"
2088
+ # }
2089
+ #
2090
+ self.handle_tickers_and_bids_asks(client, message, 'bidasks')
2091
+
2092
+ def handle_tickers(self, client: Client, message):
1032
2093
  #
1033
- # 24hr rolling window ticker statistics for a single symbol
1034
- # These are NOT the statistics of the UTC day, but a 24hr rolling window for the previous 24hrs
1035
- # Update Speed 1000ms
2094
+ # arrives one symbol dict or array of symbol dicts
1036
2095
  #
1037
2096
  # {
1038
2097
  # "e": "24hrTicker", # event type
@@ -1060,58 +2119,50 @@ class binance(ccxt.async_support.binance):
1060
2119
  # "n": 163222, # total number of trades
1061
2120
  # }
1062
2121
  #
1063
- event = self.safe_string(message, 'e', 'bookTicker')
1064
- if event == '24hrTicker':
1065
- event = 'ticker'
1066
- elif event == '24hrMiniTicker':
1067
- event = 'miniTicker'
1068
- wsMarketId = self.safe_string_lower(message, 's')
1069
- messageHash = wsMarketId + '@' + event
1070
- isSpot = ((client.url.find('/stream') > -1) or (client.url.find('/testnet.binance') > -1))
1071
- marketType = 'spot' if (isSpot) else 'contract'
1072
- result = self.parse_ws_ticker(message, marketType)
1073
- symbol = result['symbol']
1074
- self.tickers[symbol] = result
1075
- client.resolve(result, messageHash)
1076
- if event == 'bookTicker':
1077
- # watch bookTickers
1078
- client.resolve(result, '!' + 'bookTicker@arr')
1079
- messageHashes = self.find_message_hashes(client, 'tickers::')
1080
- for i in range(0, len(messageHashes)):
1081
- currentMessageHash = messageHashes[i]
1082
- parts = currentMessageHash.split('::')
1083
- symbolsString = parts[1]
1084
- symbols = symbolsString.split(',')
1085
- if self.in_array(symbol, symbols):
1086
- client.resolve(result, currentMessageHash)
2122
+ self.handle_tickers_and_bids_asks(client, message, 'tickers')
1087
2123
 
1088
- def handle_tickers(self, client: Client, message):
2124
+ def handle_tickers_and_bids_asks(self, client: Client, message, methodType):
1089
2125
  isSpot = ((client.url.find('/stream') > -1) or (client.url.find('/testnet.binance') > -1))
1090
2126
  marketType = 'spot' if (isSpot) else 'contract'
2127
+ isBidAsk = (methodType == 'bidasks')
2128
+ channelName = None
2129
+ resolvedMessageHashes = []
1091
2130
  rawTickers = []
1092
- newTickers = {}
2131
+ newTickers: dict = {}
1093
2132
  if isinstance(message, list):
1094
2133
  rawTickers = message
1095
2134
  else:
1096
2135
  rawTickers.append(message)
1097
2136
  for i in range(0, len(rawTickers)):
1098
2137
  ticker = rawTickers[i]
1099
- result = self.parse_ws_ticker(ticker, marketType)
1100
- symbol = result['symbol']
1101
- self.tickers[symbol] = result
1102
- newTickers[symbol] = result
1103
- messageHashes = self.find_message_hashes(client, 'tickers::')
1104
- for i in range(0, len(messageHashes)):
1105
- messageHash = messageHashes[i]
1106
- parts = messageHash.split('::')
1107
- symbolsString = parts[1]
1108
- symbols = symbolsString.split(',')
1109
- tickers = self.filter_by_array(newTickers, 'symbol', symbols)
1110
- tickersSymbols = list(tickers.keys())
1111
- numTickers = len(tickersSymbols)
1112
- if numTickers > 0:
1113
- client.resolve(tickers, messageHash)
1114
- client.resolve(newTickers, 'tickers')
2138
+ event = self.safe_string(ticker, 'e')
2139
+ if isBidAsk:
2140
+ event = 'bookTicker' # in `handleMessage`, bookTicker doesn't have identifier, so manually set here
2141
+ channelName = self.safe_string(self.options['tickerChannelsMap'], event, event)
2142
+ if channelName is None:
2143
+ continue
2144
+ parsedTicker = self.parse_ws_ticker(ticker, marketType)
2145
+ symbol = parsedTicker['symbol']
2146
+ newTickers[symbol] = parsedTicker
2147
+ if isBidAsk:
2148
+ self.bidsasks[symbol] = parsedTicker
2149
+ else:
2150
+ self.tickers[symbol] = parsedTicker
2151
+ messageHash = self.get_message_hash(channelName, symbol, isBidAsk)
2152
+ resolvedMessageHashes.append(messageHash)
2153
+ client.resolve(parsedTicker, messageHash)
2154
+ # resolve batch endpoint
2155
+ length = len(resolvedMessageHashes)
2156
+ if length > 0:
2157
+ batchMessageHash = self.get_message_hash(channelName, None, isBidAsk)
2158
+ client.resolve(newTickers, batchMessageHash)
2159
+
2160
+ def get_message_hash(self, channelName: str, symbol: Str, isBidAsk: bool):
2161
+ prefix = 'bidask' if isBidAsk else 'ticker'
2162
+ if symbol is not None:
2163
+ return prefix + ':' + channelName + '@' + symbol
2164
+ else:
2165
+ return prefix + 's' + ':' + channelName
1115
2166
 
1116
2167
  def sign_params(self, params={}):
1117
2168
  self.check_required_credentials()
@@ -1201,7 +2252,7 @@ class binance(ccxt.async_support.binance):
1201
2252
  if listenKey is None:
1202
2253
  # A network error happened: we can't renew a listen key that does not exist.
1203
2254
  return
1204
- request = {}
2255
+ request: dict = {}
1205
2256
  symbol = self.safe_string(params, 'symbol')
1206
2257
  params = self.omit(params, ['type', 'symbol'])
1207
2258
  time = self.milliseconds()
@@ -1264,7 +2315,7 @@ class binance(ccxt.async_support.binance):
1264
2315
  self.balance[type] = {}
1265
2316
 
1266
2317
  async def load_balance_snapshot(self, client, messageHash, type, isPortfolioMargin):
1267
- params = {
2318
+ params: dict = {
1268
2319
  'type': type,
1269
2320
  }
1270
2321
  if isPortfolioMargin:
@@ -1279,34 +2330,59 @@ class binance(ccxt.async_support.binance):
1279
2330
  async def fetch_balance_ws(self, params={}) -> Balances:
1280
2331
  """
1281
2332
  fetch balance and get the amount of funds available for trading or funds locked in orders
1282
- :see: https://binance-docs.github.io/apidocs/websocket_api/en/#account-information-user_data
2333
+
2334
+ https://developers.binance.com/docs/derivatives/usds-margined-futures/account/websocket-api/Futures-Account-Balance
2335
+ https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api#account-information-user_data
2336
+
1283
2337
  :param dict [params]: extra parameters specific to the exchange API endpoint
1284
2338
  :param str|None [params.type]: 'future', 'delivery', 'savings', 'funding', or 'spot'
1285
2339
  :param str|None [params.marginMode]: 'cross' or 'isolated', for margin trading, uses self.options.defaultMarginMode if not passed, defaults to None/None/None
1286
2340
  :param str[]|None [params.symbols]: unified market symbols, only used in isolated margin mode
2341
+ :param str|None [params.method]: method to use. Can be account.balance, account.status, v2/account.balance or v2/account.status
1287
2342
  :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
1288
2343
  """
1289
2344
  await self.load_markets()
1290
- url = self.urls['api']['ws']['ws']
2345
+ type = self.get_market_type('fetchBalanceWs', None, params)
2346
+ if type != 'spot' and type != 'future':
2347
+ raise BadRequest(self.id + ' fetchBalanceWs only supports spot or swap markets')
2348
+ url = self.urls['api']['ws']['ws-api'][type]
1291
2349
  requestId = self.request_id(url)
1292
2350
  messageHash = str(requestId)
1293
2351
  returnRateLimits = False
1294
- returnRateLimits, params = self.handle_option_and_params(params, 'createOrderWs', 'returnRateLimits', False)
1295
- payload = {
2352
+ returnRateLimits, params = self.handle_option_and_params(params, 'fetchBalanceWs', 'returnRateLimits', False)
2353
+ payload: dict = {
1296
2354
  'returnRateLimits': returnRateLimits,
1297
2355
  }
1298
- message = {
2356
+ method = None
2357
+ method, params = self.handle_option_and_params(params, 'fetchBalanceWs', 'method', 'account.status')
2358
+ message: dict = {
1299
2359
  'id': messageHash,
1300
- 'method': 'account.status',
2360
+ 'method': method,
1301
2361
  'params': self.sign_params(self.extend(payload, params)),
1302
2362
  }
1303
- subscription = {
1304
- 'method': self.handle_balance_ws,
2363
+ subscription: dict = {
2364
+ 'method': self.handle_account_status_ws if (method == 'account.status') else self.handle_balance_ws,
1305
2365
  }
1306
2366
  return await self.watch(url, messageHash, message, messageHash, subscription)
1307
2367
 
1308
2368
  def handle_balance_ws(self, client: Client, message):
1309
2369
  #
2370
+ #
2371
+ messageHash = self.safe_string(message, 'id')
2372
+ rawBalance = None
2373
+ if isinstance(message['result'], list):
2374
+ # account.balance
2375
+ rawBalance = self.safe_list(message, 'result', [])
2376
+ else:
2377
+ # account.status
2378
+ result = self.safe_dict(message, 'result', {})
2379
+ rawBalance = self.safe_list(result, 'assets', [])
2380
+ parsedBalances = self.parseBalanceCustom(rawBalance)
2381
+ client.resolve(parsedBalances, messageHash)
2382
+
2383
+ def handle_account_status_ws(self, client: Client, message):
2384
+ #
2385
+ # spot
1310
2386
  # {
1311
2387
  # "id": "605a6d20-6588-4cb9-afa0-b0ab087507ba",
1312
2388
  # "status": 200,
@@ -1349,12 +2425,104 @@ class binance(ccxt.async_support.binance):
1349
2425
  # ]
1350
2426
  # }
1351
2427
  # }
2428
+ # swap
1352
2429
  #
1353
2430
  messageHash = self.safe_string(message, 'id')
1354
- result = self.safe_value(message, 'result', {})
1355
- parsedBalances = self.parse_balance(result)
2431
+ result = self.safe_dict(message, 'result', {})
2432
+ parsedBalances = self.parseBalanceCustom(result)
1356
2433
  client.resolve(parsedBalances, messageHash)
1357
2434
 
2435
+ async def fetch_position_ws(self, symbol: str, params={}) -> List[Position]:
2436
+ """
2437
+
2438
+ https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/websocket-api/Position-Information
2439
+
2440
+ fetch data on an open position
2441
+ :param str symbol: unified market symbol of the market the position is held in
2442
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2443
+ :returns dict: a `position structure <https://docs.ccxt.com/#/?id=position-structure>`
2444
+ """
2445
+ return await self.fetch_positions_ws([symbol], params)
2446
+
2447
+ async def fetch_positions_ws(self, symbols: Strings = None, params={}) -> List[Position]:
2448
+ """
2449
+ fetch all open positions
2450
+
2451
+ https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/websocket-api/Position-Information
2452
+
2453
+ :param str[] [symbols]: list of unified market symbols
2454
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2455
+ :param boolean [params.returnRateLimits]: set to True to return rate limit informations, defaults to False.
2456
+ :param str|None [params.method]: method to use. Can be account.position or v2/account.position
2457
+ :returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
2458
+ """
2459
+ await self.load_markets()
2460
+ symbols = self.market_symbols(symbols, 'swap', True, True, True)
2461
+ url = self.urls['api']['ws']['ws-api']['future']
2462
+ requestId = self.request_id(url)
2463
+ messageHash = str(requestId)
2464
+ payload: dict = {}
2465
+ if symbols is not None:
2466
+ symbolsLength = len(symbols)
2467
+ if symbolsLength == 1:
2468
+ payload['symbol'] = self.market_id(symbols[0])
2469
+ returnRateLimits = False
2470
+ returnRateLimits, params = self.handle_option_and_params(params, 'fetchPositionsWs', 'returnRateLimits', False)
2471
+ payload['returnRateLimits'] = returnRateLimits
2472
+ method = None
2473
+ method, params = self.handle_option_and_params(params, 'fetchPositionsWs', 'method', 'account.position')
2474
+ message: dict = {
2475
+ 'id': messageHash,
2476
+ 'method': method,
2477
+ 'params': self.sign_params(self.extend(payload, params)),
2478
+ }
2479
+ subscription: dict = {
2480
+ 'method': self.handle_positions_ws,
2481
+ }
2482
+ result = await self.watch(url, messageHash, message, messageHash, subscription)
2483
+ return self.filter_by_array_positions(result, 'symbol', symbols, False)
2484
+
2485
+ def handle_positions_ws(self, client: Client, message):
2486
+ #
2487
+ # {
2488
+ # id: '1',
2489
+ # status: 200,
2490
+ # result: [
2491
+ # {
2492
+ # symbol: 'BTCUSDT',
2493
+ # positionAmt: '-0.014',
2494
+ # entryPrice: '42901.1',
2495
+ # breakEvenPrice: '30138.83333142',
2496
+ # markPrice: '71055.98470333',
2497
+ # unRealizedProfit: '-394.16838584',
2498
+ # liquidationPrice: '137032.02272908',
2499
+ # leverage: '123',
2500
+ # maxNotionalValue: '50000',
2501
+ # marginType: 'cross',
2502
+ # isolatedMargin: '0.00000000',
2503
+ # isAutoAddMargin: 'false',
2504
+ # positionSide: 'BOTH',
2505
+ # notional: '-994.78378584',
2506
+ # isolatedWallet: '0',
2507
+ # updateTime: 1708906343111,
2508
+ # isolated: False,
2509
+ # adlQuantile: 2
2510
+ # },
2511
+ # ...
2512
+ # ]
2513
+ # }
2514
+ #
2515
+ #
2516
+ messageHash = self.safe_string(message, 'id')
2517
+ result = self.safe_list(message, 'result', [])
2518
+ positions = []
2519
+ for i in range(0, len(result)):
2520
+ parsed = self.parse_position_risk(result[i])
2521
+ entryPrice = self.safe_string(parsed, 'entryPrice')
2522
+ if (entryPrice != '0') and (entryPrice != '0.0') and (entryPrice != '0.00000000'):
2523
+ positions.append(parsed)
2524
+ client.resolve(positions, messageHash)
2525
+
1358
2526
  async def watch_balance(self, params={}) -> Balances:
1359
2527
  """
1360
2528
  watch balance and get the amount of funds available for trading or funds locked in orders
@@ -1370,18 +2538,18 @@ class binance(ccxt.async_support.binance):
1370
2538
  subType, params = self.handle_sub_type_and_params('watchBalance', None, params)
1371
2539
  isPortfolioMargin = None
1372
2540
  isPortfolioMargin, params = self.handle_option_and_params_2(params, 'watchBalance', 'papi', 'portfolioMargin', False)
1373
- urlType = type
1374
- if isPortfolioMargin:
1375
- urlType = 'papi'
1376
2541
  if self.isLinear(type, subType):
1377
2542
  type = 'future'
1378
2543
  elif self.isInverse(type, subType):
1379
2544
  type = 'delivery'
2545
+ urlType = type
2546
+ if isPortfolioMargin:
2547
+ urlType = 'papi'
1380
2548
  url = self.urls['api']['ws'][urlType] + '/' + self.options[type]['listenKey']
1381
2549
  client = self.client(url)
1382
2550
  self.set_balance_cache(client, type, isPortfolioMargin)
1383
2551
  self.set_positions_cache(client, type, None, isPortfolioMargin)
1384
- options = self.safe_value(self.options, 'watchBalance')
2552
+ options = self.safe_dict(self.options, 'watchBalance')
1385
2553
  fetchBalanceSnapshot = self.safe_bool(options, 'fetchBalanceSnapshot', False)
1386
2554
  awaitBalanceSnapshot = self.safe_bool(options, 'awaitBalanceSnapshot', True)
1387
2555
  if fetchBalanceSnapshot and awaitBalanceSnapshot:
@@ -1448,7 +2616,7 @@ class binance(ccxt.async_support.binance):
1448
2616
  # }
1449
2617
  # }
1450
2618
  #
1451
- wallet = self.safe_value(self.options, 'wallet', 'wb') # cw for cross wallet
2619
+ wallet = self.safe_string(self.options, 'wallet', 'wb') # cw for cross wallet
1452
2620
  # each account is connected to a different endpoint
1453
2621
  # and has exactly one subscriptionhash which is the account type
1454
2622
  subscriptions = list(client.subscriptions.keys())
@@ -1472,8 +2640,8 @@ class binance(ccxt.async_support.binance):
1472
2640
  account['free'] = delta
1473
2641
  self.balance[accountType][code] = account
1474
2642
  else:
1475
- message = self.safe_value(message, 'a', message)
1476
- B = self.safe_value(message, 'B')
2643
+ message = self.safe_dict(message, 'a', message)
2644
+ B = self.safe_list(message, 'B')
1477
2645
  for i in range(0, len(B)):
1478
2646
  entry = B[i]
1479
2647
  currencyId = self.safe_string(entry, 'a')
@@ -1489,42 +2657,43 @@ class binance(ccxt.async_support.binance):
1489
2657
  self.balance[accountType] = self.safe_balance(self.balance[accountType])
1490
2658
  client.resolve(self.balance[accountType], messageHash)
1491
2659
 
1492
- def check_is_spot(self, method: str, symbol: str, params={}):
1493
- """
1494
- * @ignore
1495
- checks if symbols is a spot market if not throws an error
1496
- :param str method: name of the method to be checked
1497
- :param str symbol: symbol or marketId of the market to be checked
1498
- """
1499
- if symbol is None:
1500
- type = self.safe_string(params, 'type', 'spot')
1501
- defaultType = self.safe_string(self.options, 'defaultType', type)
1502
- if defaultType == 'spot':
1503
- return
1504
- raise BadRequest(self.id + ' ' + method + ' only supports spot markets')
1505
- market = self.market(symbol)
1506
- if not market['spot']:
1507
- raise BadRequest(self.id + ' ' + method + ' only supports spot markets')
2660
+ def get_market_type(self, method, market, params={}):
2661
+ type = None
2662
+ type, params = self.handle_market_type_and_params(method, market, params)
2663
+ subType = None
2664
+ subType, params = self.handle_sub_type_and_params(method, market, params)
2665
+ if self.isLinear(type, subType):
2666
+ type = 'future'
2667
+ elif self.isInverse(type, subType):
2668
+ type = 'delivery'
2669
+ return type
1508
2670
 
1509
2671
  async def create_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}) -> Order:
1510
2672
  """
1511
- :see: https://binance-docs.github.io/apidocs/websocket_api/en/#place-new-order-trade
1512
2673
  create a trade order
2674
+
2675
+ https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api#place-new-order-trade
2676
+ https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/websocket-api/New-Order
2677
+
1513
2678
  :param str symbol: unified symbol of the market to create an order in
1514
2679
  :param str type: 'market' or 'limit'
1515
2680
  :param str side: 'buy' or 'sell'
1516
2681
  :param float amount: how much of currency you want to trade in units of base currency
1517
- :param float|None [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
2682
+ :param float|None [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
1518
2683
  :param dict [params]: extra parameters specific to the exchange API endpoint
1519
2684
  :param boolean params['test']: test order, default False
2685
+ :param boolean params['returnRateLimits']: set to True to return rate limit information, default False
1520
2686
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1521
2687
  """
1522
2688
  await self.load_markets()
1523
- self.check_is_spot('createOrderWs', symbol, params)
1524
- url = self.urls['api']['ws']['ws']
2689
+ market = self.market(symbol)
2690
+ marketType = self.get_market_type('createOrderWs', market, params)
2691
+ if marketType != 'spot' and marketType != 'future':
2692
+ raise BadRequest(self.id + ' createOrderWs only supports spot or swap markets')
2693
+ url = self.urls['api']['ws']['ws-api'][marketType]
1525
2694
  requestId = self.request_id(url)
1526
2695
  messageHash = str(requestId)
1527
- sor = self.safe_value_2(params, 'sor', 'SOR', False)
2696
+ sor = self.safe_bool_2(params, 'sor', 'SOR', False)
1528
2697
  params = self.omit(params, 'sor', 'SOR')
1529
2698
  payload = self.create_order_request(symbol, type, side, amount, price, params)
1530
2699
  returnRateLimits = False
@@ -1532,7 +2701,7 @@ class binance(ccxt.async_support.binance):
1532
2701
  payload['returnRateLimits'] = returnRateLimits
1533
2702
  test = self.safe_bool(params, 'test', False)
1534
2703
  params = self.omit(params, 'test')
1535
- message = {
2704
+ message: dict = {
1536
2705
  'id': messageHash,
1537
2706
  'method': 'order.place',
1538
2707
  'params': self.sign_params(self.extend(payload, params)),
@@ -1542,7 +2711,7 @@ class binance(ccxt.async_support.binance):
1542
2711
  message['method'] = 'sor.order.test'
1543
2712
  else:
1544
2713
  message['method'] = 'order.test'
1545
- subscription = {
2714
+ subscription: dict = {
1546
2715
  'method': self.handle_order_ws,
1547
2716
  }
1548
2717
  return await self.watch(url, messageHash, message, messageHash, subscription)
@@ -1596,7 +2765,7 @@ class binance(ccxt.async_support.binance):
1596
2765
  # }
1597
2766
  #
1598
2767
  messageHash = self.safe_string(message, 'id')
1599
- result = self.safe_value(message, 'result', {})
2768
+ result = self.safe_dict(message, 'result', {})
1600
2769
  order = self.parse_order(result)
1601
2770
  client.resolve(order, messageHash)
1602
2771
 
@@ -1639,44 +2808,55 @@ class binance(ccxt.async_support.binance):
1639
2808
  # }
1640
2809
  #
1641
2810
  messageHash = self.safe_string(message, 'id')
1642
- result = self.safe_value(message, 'result', [])
2811
+ result = self.safe_list(message, 'result', [])
1643
2812
  orders = self.parse_orders(result)
1644
2813
  client.resolve(orders, messageHash)
1645
2814
 
1646
- async def edit_order_ws(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}) -> Order:
2815
+ async def edit_order_ws(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}) -> Order:
1647
2816
  """
1648
2817
  edit a trade order
1649
- :see: https://binance-docs.github.io/apidocs/websocket_api/en/#cancel-and-replace-order-trade
2818
+
2819
+ https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api#cancel-and-replace-order-trade
2820
+ https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/websocket-api/Modify-Order
2821
+
1650
2822
  :param str id: order id
1651
2823
  :param str symbol: unified symbol of the market to create an order in
1652
2824
  :param str type: 'market' or 'limit'
1653
2825
  :param str side: 'buy' or 'sell'
1654
2826
  :param float amount: how much of the currency you want to trade in units of the base currency
1655
- :param float|None [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
2827
+ :param float|None [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
1656
2828
  :param dict [params]: extra parameters specific to the exchange API endpoint
1657
2829
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1658
2830
  """
1659
2831
  await self.load_markets()
1660
- self.check_is_spot('editOrderWs', symbol, params)
1661
- url = self.urls['api']['ws']['ws']
2832
+ market = self.market(symbol)
2833
+ marketType = self.get_market_type('editOrderWs', market, params)
2834
+ if marketType != 'spot' and marketType != 'future':
2835
+ raise BadRequest(self.id + ' editOrderWs only supports spot or swap markets')
2836
+ url = self.urls['api']['ws']['ws-api'][marketType]
1662
2837
  requestId = self.request_id(url)
1663
2838
  messageHash = str(requestId)
1664
- payload = self.editSpotOrderRequest(id, symbol, type, side, amount, price, params)
2839
+ payload = None
2840
+ if marketType == 'spot':
2841
+ payload = self.editSpotOrderRequest(id, symbol, type, side, amount, price, params)
2842
+ elif marketType == 'future':
2843
+ payload = self.editContractOrderRequest(id, symbol, type, side, amount, price, params)
1665
2844
  returnRateLimits = False
1666
2845
  returnRateLimits, params = self.handle_option_and_params(params, 'editOrderWs', 'returnRateLimits', False)
1667
2846
  payload['returnRateLimits'] = returnRateLimits
1668
- message = {
2847
+ message: dict = {
1669
2848
  'id': messageHash,
1670
- 'method': 'order.cancelReplace',
2849
+ 'method': 'order.modify' if (marketType == 'future') else 'order.cancelReplace',
1671
2850
  'params': self.sign_params(self.extend(payload, params)),
1672
2851
  }
1673
- subscription = {
2852
+ subscription: dict = {
1674
2853
  'method': self.handle_edit_order_ws,
1675
2854
  }
1676
2855
  return await self.watch(url, messageHash, message, messageHash, subscription)
1677
2856
 
1678
2857
  def handle_edit_order_ws(self, client: Client, message):
1679
2858
  #
2859
+ # spot
1680
2860
  # {
1681
2861
  # "id": 1,
1682
2862
  # "status": 200,
@@ -1741,19 +2921,57 @@ class binance(ccxt.async_support.binance):
1741
2921
  # }
1742
2922
  # ]
1743
2923
  # }
2924
+ # swap
2925
+ # {
2926
+ # "id":"1",
2927
+ # "status":200,
2928
+ # "result":{
2929
+ # "orderId":667061487,
2930
+ # "symbol":"LTCUSDT",
2931
+ # "status":"NEW",
2932
+ # "clientOrderId":"x-xcKtGhcu91a74c818749ee42c0f70",
2933
+ # "price":"82.00",
2934
+ # "avgPrice":"0.00",
2935
+ # "origQty":"1.000",
2936
+ # "executedQty":"0.000",
2937
+ # "cumQty":"0.000",
2938
+ # "cumQuote":"0.00000",
2939
+ # "timeInForce":"GTC",
2940
+ # "type":"LIMIT",
2941
+ # "reduceOnly":false,
2942
+ # "closePosition":false,
2943
+ # "side":"BUY",
2944
+ # "positionSide":"BOTH",
2945
+ # "stopPrice":"0.00",
2946
+ # "workingType":"CONTRACT_PRICE",
2947
+ # "priceProtect":false,
2948
+ # "origType":"LIMIT",
2949
+ # "priceMatch":"NONE",
2950
+ # "selfTradePreventionMode":"NONE",
2951
+ # "goodTillDate":0,
2952
+ # "updateTime":1712918927511
2953
+ # }
2954
+ # }
1744
2955
  #
1745
2956
  messageHash = self.safe_string(message, 'id')
1746
- result = self.safe_value(message, 'result', {})
1747
- rawOrder = self.safe_value(result, 'newOrderResponse', {})
1748
- order = self.parse_order(rawOrder)
2957
+ result = self.safe_dict(message, 'result', {})
2958
+ newSpotOrder = self.safe_dict(result, 'newOrderResponse')
2959
+ order = None
2960
+ if newSpotOrder is not None:
2961
+ order = self.parse_order(newSpotOrder)
2962
+ else:
2963
+ order = self.parse_order(result)
1749
2964
  client.resolve(order, messageHash)
1750
2965
 
1751
2966
  async def cancel_order_ws(self, id: str, symbol: Str = None, params={}) -> Order:
1752
2967
  """
1753
- :see: https://binance-docs.github.io/apidocs/websocket_api/en/#cancel-order-trade
1754
2968
  cancel multiple orders
2969
+
2970
+ https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api#cancel-order-trade
2971
+ https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/websocket-api/Cancel-Order
2972
+
1755
2973
  :param str id: order id
1756
- :param str symbol: unified market symbol, default is None
2974
+ :param str [symbol]: unified market symbol, default is None
1757
2975
  :param dict [params]: extra parameters specific to the exchange API endpoint
1758
2976
  :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.
1759
2977
  :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
@@ -1761,100 +2979,116 @@ class binance(ccxt.async_support.binance):
1761
2979
  await self.load_markets()
1762
2980
  if symbol is None:
1763
2981
  raise BadRequest(self.id + ' cancelOrderWs requires a symbol')
1764
- self.check_is_spot('cancelOrderWs', symbol, params)
1765
- url = self.urls['api']['ws']['ws']
2982
+ market = self.market(symbol)
2983
+ type = self.get_market_type('cancelOrderWs', market, params)
2984
+ url = self.urls['api']['ws']['ws-api'][type]
1766
2985
  requestId = self.request_id(url)
1767
2986
  messageHash = str(requestId)
1768
2987
  returnRateLimits = False
1769
2988
  returnRateLimits, params = self.handle_option_and_params(params, 'cancelOrderWs', 'returnRateLimits', False)
1770
- payload = {
2989
+ payload: dict = {
1771
2990
  'symbol': self.market_id(symbol),
1772
2991
  'returnRateLimits': returnRateLimits,
1773
2992
  }
1774
- clientOrderId = self.safe_value_2(params, 'origClientOrderId', 'clientOrderId')
2993
+ clientOrderId = self.safe_string_2(params, 'origClientOrderId', 'clientOrderId')
1775
2994
  if clientOrderId is not None:
1776
2995
  payload['origClientOrderId'] = clientOrderId
1777
2996
  else:
1778
2997
  payload['orderId'] = self.parse_to_int(id)
1779
2998
  params = self.omit(params, ['origClientOrderId', 'clientOrderId'])
1780
- message = {
2999
+ message: dict = {
1781
3000
  'id': messageHash,
1782
3001
  'method': 'order.cancel',
1783
3002
  'params': self.sign_params(self.extend(payload, params)),
1784
3003
  }
1785
- subscription = {
3004
+ subscription: dict = {
1786
3005
  'method': self.handle_order_ws,
1787
3006
  }
1788
3007
  return await self.watch(url, messageHash, message, messageHash, subscription)
1789
3008
 
1790
3009
  async def cancel_all_orders_ws(self, symbol: Str = None, params={}):
1791
3010
  """
1792
- :see: https://binance-docs.github.io/apidocs/websocket_api/en/#current-open-orders-user_data
1793
3011
  cancel all open orders in a market
1794
- :param str symbol: unified market symbol of the market to cancel orders in
3012
+
3013
+ https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api#cancel-open-orders-trade
3014
+
3015
+ :param str [symbol]: unified market symbol of the market to cancel orders in
1795
3016
  :param dict [params]: extra parameters specific to the exchange API endpoint
1796
3017
  :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1797
3018
  """
1798
3019
  await self.load_markets()
1799
- url = self.urls['api']['ws']['ws']
3020
+ market = self.market(symbol)
3021
+ type = self.get_market_type('cancelAllOrdersWs', market, params)
3022
+ if type != 'spot' and type != 'future':
3023
+ raise BadRequest(self.id + ' cancelAllOrdersWs only supports spot or swap markets')
3024
+ url = self.urls['api']['ws']['ws-api'][type]
1800
3025
  requestId = self.request_id(url)
1801
3026
  messageHash = str(requestId)
1802
3027
  returnRateLimits = False
1803
3028
  returnRateLimits, params = self.handle_option_and_params(params, 'cancelAllOrdersWs', 'returnRateLimits', False)
1804
- payload = {
3029
+ payload: dict = {
1805
3030
  'symbol': self.market_id(symbol),
1806
3031
  'returnRateLimits': returnRateLimits,
1807
3032
  }
1808
- message = {
3033
+ message: dict = {
1809
3034
  'id': messageHash,
1810
3035
  'method': 'order.cancel',
1811
3036
  'params': self.sign_params(self.extend(payload, params)),
1812
3037
  }
1813
- subscription = {
3038
+ subscription: dict = {
1814
3039
  'method': self.handle_orders_ws,
1815
3040
  }
1816
3041
  return await self.watch(url, messageHash, message, messageHash, subscription)
1817
3042
 
1818
3043
  async def fetch_order_ws(self, id: str, symbol: Str = None, params={}) -> Order:
1819
3044
  """
1820
- :see: https://binance-docs.github.io/apidocs/websocket_api/en/#query-order-user_data
1821
3045
  fetches information on an order made by the user
1822
- :param str symbol: unified symbol of the market the order was made in
1823
- :param dict params: extra parameters specific to the exchange API endpoint
3046
+
3047
+ https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api#query-order-user_data
3048
+ https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/websocket-api/Query-Order
3049
+
3050
+ :param str id: order id
3051
+ :param str [symbol]: unified symbol of the market the order was made in
3052
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1824
3053
  :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1825
3054
  """
1826
3055
  await self.load_markets()
1827
3056
  if symbol is None:
1828
3057
  raise BadRequest(self.id + ' cancelOrderWs requires a symbol')
1829
- self.check_is_spot('fetchOrderWs', symbol, params)
1830
- url = self.urls['api']['ws']['ws']
3058
+ market = self.market(symbol)
3059
+ type = self.get_market_type('fetchOrderWs', market, params)
3060
+ if type != 'spot' and type != 'future':
3061
+ raise BadRequest(self.id + ' fetchOrderWs only supports spot or swap markets')
3062
+ url = self.urls['api']['ws']['ws-api'][type]
1831
3063
  requestId = self.request_id(url)
1832
3064
  messageHash = str(requestId)
1833
3065
  returnRateLimits = False
1834
3066
  returnRateLimits, params = self.handle_option_and_params(params, 'fetchOrderWs', 'returnRateLimits', False)
1835
- payload = {
3067
+ payload: dict = {
1836
3068
  'symbol': self.market_id(symbol),
1837
3069
  'returnRateLimits': returnRateLimits,
1838
3070
  }
1839
- clientOrderId = self.safe_value_2(params, 'origClientOrderId', 'clientOrderId')
3071
+ clientOrderId = self.safe_string_2(params, 'origClientOrderId', 'clientOrderId')
1840
3072
  if clientOrderId is not None:
1841
3073
  payload['origClientOrderId'] = clientOrderId
1842
3074
  else:
1843
3075
  payload['orderId'] = self.parse_to_int(id)
1844
- message = {
3076
+ message: dict = {
1845
3077
  'id': messageHash,
1846
3078
  'method': 'order.status',
1847
3079
  'params': self.sign_params(self.extend(payload, params)),
1848
3080
  }
1849
- subscription = {
3081
+ subscription: dict = {
1850
3082
  'method': self.handle_order_ws,
1851
3083
  }
1852
3084
  return await self.watch(url, messageHash, message, messageHash, subscription)
1853
3085
 
1854
3086
  async def fetch_orders_ws(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1855
3087
  """
1856
- :see: https://binance-docs.github.io/apidocs/websocket_api/en/#account-order-history-user_data
1857
3088
  fetches information on multiple orders made by the user
3089
+
3090
+ https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api#query-order-list-user_data
3091
+
1858
3092
  :param str symbol: unified market symbol of the market orders were made in
1859
3093
  :param int|None [since]: the earliest time in ms to fetch orders for
1860
3094
  :param int|None [limit]: the maximum number of order structures to retrieve
@@ -1868,22 +3102,25 @@ class binance(ccxt.async_support.binance):
1868
3102
  await self.load_markets()
1869
3103
  if symbol is None:
1870
3104
  raise BadRequest(self.id + ' fetchOrdersWs requires a symbol')
1871
- self.check_is_spot('fetchOrdersWs', symbol, params)
1872
- url = self.urls['api']['ws']['ws']
3105
+ market = self.market(symbol)
3106
+ type = self.get_market_type('fetchOrdersWs', market, params)
3107
+ if type != 'spot':
3108
+ raise BadRequest(self.id + ' fetchOrdersWs only supports spot markets')
3109
+ url = self.urls['api']['ws']['ws-api'][type]
1873
3110
  requestId = self.request_id(url)
1874
3111
  messageHash = str(requestId)
1875
3112
  returnRateLimits = False
1876
3113
  returnRateLimits, params = self.handle_option_and_params(params, 'fetchOrderWs', 'returnRateLimits', False)
1877
- payload = {
3114
+ payload: dict = {
1878
3115
  'symbol': self.market_id(symbol),
1879
3116
  'returnRateLimits': returnRateLimits,
1880
3117
  }
1881
- message = {
3118
+ message: dict = {
1882
3119
  'id': messageHash,
1883
3120
  'method': 'allOrders',
1884
3121
  'params': self.sign_params(self.extend(payload, params)),
1885
3122
  }
1886
- subscription = {
3123
+ subscription: dict = {
1887
3124
  'method': self.handle_orders_ws,
1888
3125
  }
1889
3126
  orders = await self.watch(url, messageHash, message, messageHash, subscription)
@@ -1891,8 +3128,10 @@ class binance(ccxt.async_support.binance):
1891
3128
 
1892
3129
  async def fetch_closed_orders_ws(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1893
3130
  """
1894
- :see: https://binance-docs.github.io/apidocs/websocket_api/en/#account-order-history-user_data
1895
3131
  fetch closed orders
3132
+
3133
+ https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api#query-order-list-user_data
3134
+
1896
3135
  :param str symbol: unified market symbol
1897
3136
  :param int [since]: the earliest time in ms to fetch open orders for
1898
3137
  :param int [limit]: the maximum number of open orders structures to retrieve
@@ -1909,8 +3148,10 @@ class binance(ccxt.async_support.binance):
1909
3148
 
1910
3149
  async def fetch_open_orders_ws(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1911
3150
  """
1912
- :see: https://binance-docs.github.io/apidocs/websocket_api/en/#current-open-orders-user_data
1913
3151
  fetch all unfilled currently open orders
3152
+
3153
+ https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api#current-open-orders-user_data
3154
+
1914
3155
  :param str symbol: unified market symbol
1915
3156
  :param int|None [since]: the earliest time in ms to fetch open orders for
1916
3157
  :param int|None [limit]: the maximum number of open orders structures to retrieve
@@ -1918,23 +3159,26 @@ class binance(ccxt.async_support.binance):
1918
3159
  :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1919
3160
  """
1920
3161
  await self.load_markets()
1921
- self.check_is_spot('fetchOpenOrdersWs', symbol)
1922
- url = self.urls['api']['ws']['ws']
3162
+ market = self.market(symbol)
3163
+ type = self.get_market_type('fetchOpenOrdersWs', market, params)
3164
+ if type != 'spot' and type != 'future':
3165
+ raise BadRequest(self.id + ' fetchOpenOrdersWs only supports spot or swap markets')
3166
+ url = self.urls['api']['ws']['ws-api'][type]
1923
3167
  requestId = self.request_id(url)
1924
3168
  messageHash = str(requestId)
1925
3169
  returnRateLimits = False
1926
3170
  returnRateLimits, params = self.handle_option_and_params(params, 'fetchOrderWs', 'returnRateLimits', False)
1927
- payload = {
3171
+ payload: dict = {
1928
3172
  'returnRateLimits': returnRateLimits,
1929
3173
  }
1930
3174
  if symbol is not None:
1931
3175
  payload['symbol'] = self.market_id(symbol)
1932
- message = {
3176
+ message: dict = {
1933
3177
  'id': messageHash,
1934
3178
  'method': 'openOrders.status',
1935
3179
  'params': self.sign_params(self.extend(payload, params)),
1936
3180
  }
1937
- subscription = {
3181
+ subscription: dict = {
1938
3182
  'method': self.handle_orders_ws,
1939
3183
  }
1940
3184
  orders = await self.watch(url, messageHash, message, messageHash, subscription)
@@ -1943,9 +3187,11 @@ class binance(ccxt.async_support.binance):
1943
3187
  async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1944
3188
  """
1945
3189
  watches information on multiple orders made by the user
1946
- :see: https://binance-docs.github.io/apidocs/spot/en/#payload-order-update
1947
- :see: https://binance-docs.github.io/apidocs/pm/en/#event-futures-order-update
1948
- :see: https://binance-docs.github.io/apidocs/pm/en/#event-margin-order-update
3190
+
3191
+ https://developers.binance.com/docs/binance-spot-api-docs/user-data-stream#order-update
3192
+ https://developers.binance.com/docs/margin_trading/trade-data-stream/Event-Order-Update
3193
+ https://developers.binance.com/docs/derivatives/usds-margined-futures/user-data-streams/Event-Order-Update
3194
+
1949
3195
  :param str symbol: unified market symbol of the market the orders were made in
1950
3196
  :param int [since]: the earliest time in ms to fetch orders for
1951
3197
  :param int [limit]: the maximum number of order structures to retrieve
@@ -2120,7 +3366,7 @@ class binance(ccxt.async_support.binance):
2120
3366
  'type': type,
2121
3367
  'timeInForce': timeInForce,
2122
3368
  'postOnly': None,
2123
- 'reduceOnly': self.safe_value(order, 'R'),
3369
+ 'reduceOnly': self.safe_bool(order, 'R'),
2124
3370
  'side': side,
2125
3371
  'price': price,
2126
3372
  'stopPrice': stopPrice,
@@ -2219,14 +3465,17 @@ class binance(ccxt.async_support.binance):
2219
3465
  #
2220
3466
  e = self.safe_string(message, 'e')
2221
3467
  if e == 'ORDER_TRADE_UPDATE':
2222
- message = self.safe_value(message, 'o', message)
3468
+ message = self.safe_dict(message, 'o', message)
2223
3469
  self.handle_my_trade(client, message)
2224
3470
  self.handle_order(client, message)
3471
+ self.handle_my_liquidation(client, message)
2225
3472
 
2226
3473
  async def watch_positions(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]:
2227
3474
  """
2228
3475
  watch all open positions
2229
3476
  :param str[]|None symbols: list of unified market symbols
3477
+ :param number [since]: since timestamp
3478
+ :param number [limit]: limit
2230
3479
  :param dict params: extra parameters specific to the exchange API endpoint
2231
3480
  :param boolean [params.portfolioMargin]: set to True if you would like to watch positions in a portfolio margin account
2232
3481
  :returns dict[]: a list of `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
@@ -2238,11 +3487,6 @@ class binance(ccxt.async_support.binance):
2238
3487
  if not self.is_empty(symbols):
2239
3488
  market = self.get_market_from_symbols(symbols)
2240
3489
  messageHash = '::' + ','.join(symbols)
2241
- marketTypeObject = {}
2242
- if market is not None:
2243
- marketTypeObject['type'] = market['type']
2244
- marketTypeObject['subType'] = market['subType']
2245
- await self.authenticate(self.extend(marketTypeObject, params))
2246
3490
  type = None
2247
3491
  type, params = self.handle_market_type_and_params('watchPositions', market, params)
2248
3492
  if type == 'spot' or type == 'margin':
@@ -2253,6 +3497,10 @@ class binance(ccxt.async_support.binance):
2253
3497
  type = 'future'
2254
3498
  elif self.isInverse(type, subType):
2255
3499
  type = 'delivery'
3500
+ marketTypeObject: dict = {}
3501
+ marketTypeObject['type'] = type
3502
+ marketTypeObject['subType'] = subType
3503
+ await self.authenticate(self.extend(marketTypeObject, params))
2256
3504
  messageHash = type + ':positions' + messageHash
2257
3505
  isPortfolioMargin = None
2258
3506
  isPortfolioMargin, params = self.handle_option_and_params_2(params, 'watchPositions', 'papi', 'portfolioMargin', False)
@@ -2264,7 +3512,7 @@ class binance(ccxt.async_support.binance):
2264
3512
  self.set_balance_cache(client, type, isPortfolioMargin)
2265
3513
  self.set_positions_cache(client, type, symbols, isPortfolioMargin)
2266
3514
  fetchPositionsSnapshot = self.handle_option('watchPositions', 'fetchPositionsSnapshot', True)
2267
- awaitPositionsSnapshot = self.safe_bool('watchPositions', 'awaitPositionsSnapshot', True)
3515
+ awaitPositionsSnapshot = self.handle_option('watchPositions', 'awaitPositionsSnapshot', True)
2268
3516
  cache = self.safe_value(self.positions, type)
2269
3517
  if fetchPositionsSnapshot and awaitPositionsSnapshot and cache is None:
2270
3518
  snapshot = await client.future(type + ':fetchPositionsSnapshot')
@@ -2291,7 +3539,7 @@ class binance(ccxt.async_support.binance):
2291
3539
  self.positions[type] = ArrayCacheBySymbolBySide()
2292
3540
 
2293
3541
  async def load_positions_snapshot(self, client, messageHash, type, isPortfolioMargin):
2294
- params = {
3542
+ params: dict = {
2295
3543
  'type': type,
2296
3544
  }
2297
3545
  if isPortfolioMargin:
@@ -2346,8 +3594,8 @@ class binance(ccxt.async_support.binance):
2346
3594
  if not (accountType in self.positions):
2347
3595
  self.positions[accountType] = ArrayCacheBySymbolBySide()
2348
3596
  cache = self.positions[accountType]
2349
- data = self.safe_value(message, 'a', {})
2350
- rawPositions = self.safe_value(data, 'P', [])
3597
+ data = self.safe_dict(message, 'a', {})
3598
+ rawPositions = self.safe_list(data, 'P', [])
2351
3599
  newPositions = []
2352
3600
  for i in range(0, len(rawPositions)):
2353
3601
  rawPosition = rawPositions[i]
@@ -2421,8 +3669,10 @@ class binance(ccxt.async_support.binance):
2421
3669
 
2422
3670
  async def fetch_my_trades_ws(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
2423
3671
  """
2424
- :see: https://binance-docs.github.io/apidocs/websocket_api/en/#account-trade-history-user_data
2425
3672
  fetch all trades made by the user
3673
+
3674
+ https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api#account-trade-history-user_data
3675
+
2426
3676
  :param str symbol: unified market symbol
2427
3677
  :param int|None [since]: the earliest time in ms to fetch trades for
2428
3678
  :param int|None [limit]: the maximum number of trades structures to retrieve
@@ -2434,13 +3684,16 @@ class binance(ccxt.async_support.binance):
2434
3684
  await self.load_markets()
2435
3685
  if symbol is None:
2436
3686
  raise BadRequest(self.id + ' fetchMyTradesWs requires a symbol')
2437
- self.check_is_spot('fetchMyTradesWs', symbol, params)
2438
- url = self.urls['api']['ws']['ws']
3687
+ market = self.market(symbol)
3688
+ type = self.get_market_type('fetchMyTradesWs', market, params)
3689
+ if type != 'spot' and type != 'future':
3690
+ raise BadRequest(self.id + ' fetchMyTradesWs does not support ' + type + ' markets')
3691
+ url = self.urls['api']['ws']['ws-api'][type]
2439
3692
  requestId = self.request_id(url)
2440
3693
  messageHash = str(requestId)
2441
3694
  returnRateLimits = False
2442
3695
  returnRateLimits, params = self.handle_option_and_params(params, 'fetchMyTradesWs', 'returnRateLimits', False)
2443
- payload = {
3696
+ payload: dict = {
2444
3697
  'symbol': self.market_id(symbol),
2445
3698
  'returnRateLimits': returnRateLimits,
2446
3699
  }
@@ -2451,12 +3704,12 @@ class binance(ccxt.async_support.binance):
2451
3704
  fromId = self.safe_integer(params, 'fromId')
2452
3705
  if fromId is not None and since is not None:
2453
3706
  raise BadRequest(self.id + 'fetchMyTradesWs does not support fetching by both fromId and since parameters at the same time')
2454
- message = {
3707
+ message: dict = {
2455
3708
  'id': messageHash,
2456
3709
  'method': 'myTrades',
2457
3710
  'params': self.sign_params(self.extend(payload, params)),
2458
3711
  }
2459
- subscription = {
3712
+ subscription: dict = {
2460
3713
  'method': self.handle_trades_ws,
2461
3714
  }
2462
3715
  trades = await self.watch(url, messageHash, message, messageHash, subscription)
@@ -2464,38 +3717,43 @@ class binance(ccxt.async_support.binance):
2464
3717
 
2465
3718
  async def fetch_trades_ws(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
2466
3719
  """
2467
- :see: https://binance-docs.github.io/apidocs/websocket_api/en/#recent-trades
2468
3720
  fetch all trades made by the user
3721
+
3722
+ https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api#recent-trades
3723
+
2469
3724
  :param str symbol: unified market symbol
2470
3725
  :param int [since]: the earliest time in ms to fetch trades for
2471
3726
  :param int [limit]: the maximum number of trades structures to retrieve, default=500, max=1000
2472
3727
  :param dict [params]: extra parameters specific to the exchange API endpoint
2473
- *
2474
- * EXCHANGE SPECIFIC PARAMETERS
3728
+
3729
+ EXCHANGE SPECIFIC PARAMETERS
2475
3730
  :param int [params.fromId]: trade ID to begin at
2476
3731
  :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
2477
3732
  """
2478
3733
  await self.load_markets()
2479
3734
  if symbol is None:
2480
3735
  raise BadRequest(self.id + ' fetchTradesWs() requires a symbol argument')
2481
- self.check_is_spot('fetchTradesWs', symbol, params)
2482
- url = self.urls['api']['ws']['ws']
3736
+ market = self.market(symbol)
3737
+ type = self.get_market_type('fetchTradesWs', market, params)
3738
+ if type != 'spot' and type != 'future':
3739
+ raise BadRequest(self.id + ' fetchTradesWs does not support ' + type + ' markets')
3740
+ url = self.urls['api']['ws']['ws-api'][type]
2483
3741
  requestId = self.request_id(url)
2484
3742
  messageHash = str(requestId)
2485
3743
  returnRateLimits = False
2486
3744
  returnRateLimits, params = self.handle_option_and_params(params, 'fetchTradesWs', 'returnRateLimits', False)
2487
- payload = {
3745
+ payload: dict = {
2488
3746
  'symbol': self.market_id(symbol),
2489
3747
  'returnRateLimits': returnRateLimits,
2490
3748
  }
2491
3749
  if limit is not None:
2492
3750
  payload['limit'] = limit
2493
- message = {
3751
+ message: dict = {
2494
3752
  'id': messageHash,
2495
3753
  'method': 'trades.historical',
2496
3754
  'params': self.extend(payload, params),
2497
3755
  }
2498
- subscription = {
3756
+ subscription: dict = {
2499
3757
  'method': self.handle_trades_ws,
2500
3758
  }
2501
3759
  trades = await self.watch(url, messageHash, message, messageHash, subscription)
@@ -2548,7 +3806,7 @@ class binance(ccxt.async_support.binance):
2548
3806
  # }
2549
3807
  #
2550
3808
  messageHash = self.safe_string(message, 'id')
2551
- result = self.safe_value(message, 'result', [])
3809
+ result = self.safe_list(message, 'result', [])
2552
3810
  trades = self.parse_trades(result)
2553
3811
  client.resolve(trades, messageHash)
2554
3812
 
@@ -2560,7 +3818,7 @@ class binance(ccxt.async_support.binance):
2560
3818
  :param int [limit]: the maximum number of order structures to retrieve
2561
3819
  :param dict [params]: extra parameters specific to the exchange API endpoint
2562
3820
  :param boolean [params.portfolioMargin]: set to True if you would like to watch trades in a portfolio margin account
2563
- :returns dict[]: a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure
3821
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
2564
3822
  """
2565
3823
  await self.load_markets()
2566
3824
  type = None
@@ -2604,7 +3862,7 @@ class binance(ccxt.async_support.binance):
2604
3862
  if executionType == 'TRADE':
2605
3863
  trade = self.parse_ws_trade(message)
2606
3864
  orderId = self.safe_string(trade, 'order')
2607
- tradeFee = self.safe_value(trade, 'fee', {})
3865
+ tradeFee = self.safe_dict(trade, 'fee', {})
2608
3866
  tradeFee = self.extend({}, tradeFee)
2609
3867
  symbol = self.safe_string(trade, 'symbol')
2610
3868
  if orderId is not None and tradeFee is not None and symbol is not None:
@@ -2639,7 +3897,7 @@ class binance(ccxt.async_support.binance):
2639
3897
  else:
2640
3898
  order['fee'] = tradeFee
2641
3899
  # save self trade in the order
2642
- orderTrades = self.safe_value(order, 'trades', [])
3900
+ orderTrades = self.safe_list(order, 'trades', [])
2643
3901
  orderTrades.append(trade)
2644
3902
  order['trades'] = orderTrades
2645
3903
  # don't append twice cause it breaks newUpdates mode
@@ -2698,7 +3956,7 @@ class binance(ccxt.async_support.binance):
2698
3956
  #
2699
3957
  id = self.safe_string(message, 'id')
2700
3958
  rejected = False
2701
- error = self.safe_value(message, 'error', {})
3959
+ error = self.safe_dict(message, 'error', {})
2702
3960
  code = self.safe_integer(error, 'code')
2703
3961
  msg = self.safe_string(error, 'msg')
2704
3962
  try:
@@ -2707,7 +3965,7 @@ class binance(ccxt.async_support.binance):
2707
3965
  rejected = True
2708
3966
  # private endpoint uses id
2709
3967
  client.reject(e, id)
2710
- # public endpoint stores messageHash in subscriptios
3968
+ # public endpoint stores messageHash in subscriptions
2711
3969
  subscriptionKeys = list(client.subscriptions.keys())
2712
3970
  for i in range(0, len(subscriptionKeys)):
2713
3971
  subscriptionHash = subscriptionKeys[i]
@@ -2717,7 +3975,8 @@ class binance(ccxt.async_support.binance):
2717
3975
  if not rejected:
2718
3976
  client.reject(message, id)
2719
3977
  # reset connection if 5xx error
2720
- if self.safe_string(code, 0) == '5':
3978
+ codeString = self.safe_string(error, 'code')
3979
+ if (codeString is not None) and (codeString[0] == '5'):
2721
3980
  client.reset(message)
2722
3981
 
2723
3982
  def handle_message(self, client: Client, message):
@@ -2734,23 +3993,32 @@ class binance(ccxt.async_support.binance):
2734
3993
  method(client, message)
2735
3994
  return
2736
3995
  # handle other APIs
2737
- methods = {
3996
+ methods: dict = {
2738
3997
  'depthUpdate': self.handle_order_book,
2739
3998
  'trade': self.handle_trade,
2740
3999
  'aggTrade': self.handle_trade,
2741
4000
  'kline': self.handle_ohlcv,
2742
4001
  'markPrice_kline': self.handle_ohlcv,
2743
4002
  'indexPrice_kline': self.handle_ohlcv,
4003
+ '1hTicker@arr': self.handle_tickers,
4004
+ '4hTicker@arr': self.handle_tickers,
4005
+ '1dTicker@arr': self.handle_tickers,
2744
4006
  '24hrTicker@arr': self.handle_tickers,
2745
4007
  '24hrMiniTicker@arr': self.handle_tickers,
2746
- '24hrTicker': self.handle_ticker,
2747
- '24hrMiniTicker': self.handle_ticker,
2748
- 'bookTicker': self.handle_ticker,
4008
+ '1hTicker': self.handle_tickers,
4009
+ '4hTicker': self.handle_tickers,
4010
+ '1dTicker': self.handle_tickers,
4011
+ '24hrTicker': self.handle_tickers,
4012
+ '24hrMiniTicker': self.handle_tickers,
4013
+ 'markPriceUpdate': self.handle_tickers,
4014
+ 'markPriceUpdate@arr': self.handle_tickers,
4015
+ 'bookTicker': self.handle_bids_asks, # there is no "bookTicker@arr" endpoint
2749
4016
  'outboundAccountPosition': self.handle_balance,
2750
4017
  'balanceUpdate': self.handle_balance,
2751
4018
  'ACCOUNT_UPDATE': self.handle_acount_update,
2752
4019
  'executionReport': self.handle_order_update,
2753
4020
  'ORDER_TRADE_UPDATE': self.handle_order_update,
4021
+ 'forceOrder': self.handle_liquidation,
2754
4022
  }
2755
4023
  event = self.safe_string(message, 'e')
2756
4024
  if isinstance(message, list):
@@ -2773,8 +4041,7 @@ class binance(ccxt.async_support.binance):
2773
4041
  # "A": "2.52500800"
2774
4042
  # }
2775
4043
  #
2776
- if event is None:
2777
- self.handle_ticker(client, message)
2778
- self.handle_tickers(client, message)
4044
+ if event is None and ('a' in message) and ('b' in message):
4045
+ self.handle_bids_asks(client, message)
2779
4046
  else:
2780
4047
  method(client, message)