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/coincatch.py ADDED
@@ -0,0 +1,1464 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
4
+ # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
5
+
6
+ import ccxt.async_support
7
+ from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide, ArrayCacheByTimestamp
8
+ import hashlib
9
+ from ccxt.base.types import Balances, Bool, Int, Market, Order, OrderBook, Position, Str, Strings, Ticker, Tickers, Trade
10
+ from ccxt.async_support.base.ws.client import Client
11
+ from typing import List
12
+ from typing import Any
13
+ from ccxt.base.errors import ExchangeError
14
+ from ccxt.base.errors import AuthenticationError
15
+ from ccxt.base.errors import ArgumentsRequired
16
+ from ccxt.base.errors import BadRequest
17
+ from ccxt.base.errors import NotSupported
18
+ from ccxt.base.errors import RateLimitExceeded
19
+ from ccxt.base.errors import ChecksumError
20
+ from ccxt.base.errors import UnsubscribeError
21
+ from ccxt.base.precise import Precise
22
+
23
+
24
+ class coincatch(ccxt.async_support.coincatch):
25
+
26
+ def describe(self):
27
+ return self.deep_extend(super(coincatch, self).describe(), {
28
+ 'has': {
29
+ 'ws': True,
30
+ 'watchTrades': True,
31
+ 'watchTradesForSymbols': True,
32
+ 'watchOrderBook': True,
33
+ 'watchOrderBookForSymbols': True,
34
+ 'watchOHLCV': True,
35
+ 'watchOHLCVForSymbols': False, # todo
36
+ 'watchOrders': True,
37
+ 'watchMyTrades': False,
38
+ 'watchTicker': True,
39
+ 'watchTickers': True,
40
+ 'watchBalance': True,
41
+ 'watchPositions': True,
42
+ },
43
+ 'urls': {
44
+ 'api': {
45
+ 'ws': {
46
+ 'public': 'wss://ws.coincatch.com/public/v1/stream',
47
+ 'private': 'wss://ws.coincatch.com/private/v1/stream',
48
+ },
49
+ },
50
+ },
51
+ 'options': {
52
+ 'tradesLimit': 1000,
53
+ 'OHLCVLimit': 200,
54
+ 'timeframesForWs': {
55
+ '1m': '1m',
56
+ '5m': '5m',
57
+ '15m': '15m',
58
+ '30m': '30m',
59
+ '1h': '1H',
60
+ '4h': '4H',
61
+ '12h': '12H',
62
+ '1d': '1D',
63
+ '1w': '1W',
64
+ },
65
+ 'watchOrderBook': {
66
+ 'checksum': True,
67
+ },
68
+ },
69
+ 'streaming': {
70
+ 'ping': self.ping,
71
+ },
72
+ 'exceptions': {
73
+ 'ws': {
74
+ 'exact': {
75
+ '30001': BadRequest, # Channel does not exist
76
+ '30002': AuthenticationError, # illegal request
77
+ '30003': BadRequest, # invalid op
78
+ '30004': AuthenticationError, # User needs to log in
79
+ '30005': AuthenticationError, # login failed
80
+ '30006': RateLimitExceeded, # request too many
81
+ '30007': RateLimitExceeded, # request over limit,connection close
82
+ '30011': AuthenticationError, # invalid ACCESS_KEY
83
+ '30012': AuthenticationError, # invalid ACCESS_PASSPHRASE
84
+ '30013': AuthenticationError, # invalid ACCESS_TIMESTAMP
85
+ '30014': BadRequest, # Request timestamp expired
86
+ '30015': AuthenticationError, # {event: 'error', code: 30015, msg: 'Invalid sign'}
87
+ },
88
+ 'broad': {},
89
+ },
90
+ },
91
+ })
92
+
93
+ def get_market_from_arg(self, entry):
94
+ instId = self.safe_string(entry, 'instId')
95
+ instType = self.safe_string(entry, 'instType')
96
+ baseAndQuote = self.parseSpotMarketId(instId)
97
+ baseId = baseAndQuote['baseId']
98
+ quoteId = baseAndQuote['quoteId']
99
+ suffix = '_SPBL' # spot suffix
100
+ if instType == 'mc':
101
+ if quoteId == 'USD':
102
+ suffix = '_DMCBL'
103
+ else:
104
+ suffix = '_UMCBL'
105
+ marketId = self.safe_currency_code(baseId) + self.safe_currency_code(quoteId) + suffix
106
+ return self.safeMarketCustom(marketId)
107
+
108
+ async def authenticate(self, params={}):
109
+ self.check_required_credentials()
110
+ url = self.urls['api']['ws']['private']
111
+ client = self.client(url)
112
+ messageHash = 'authenticated'
113
+ future = client.future(messageHash)
114
+ authenticated = self.safe_value(client.subscriptions, messageHash)
115
+ if authenticated is None:
116
+ timestamp = str(self.seconds())
117
+ auth = timestamp + 'GET' + '/user/verify'
118
+ signature = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha256, 'base64')
119
+ operation = 'login'
120
+ request: dict = {
121
+ 'op': operation,
122
+ 'args': [
123
+ {
124
+ 'apiKey': self.apiKey,
125
+ 'passphrase': self.password,
126
+ 'timestamp': timestamp,
127
+ 'sign': signature,
128
+ },
129
+ ],
130
+ }
131
+ message = self.extend(request, params)
132
+ self.watch(url, messageHash, message, messageHash)
133
+ return await future
134
+
135
+ async def watch_public(self, messageHash, subscribeHash, args, params={}):
136
+ url = self.urls['api']['ws']['public']
137
+ request: dict = {
138
+ 'op': 'subscribe',
139
+ 'args': [args],
140
+ }
141
+ message = self.extend(request, params)
142
+ return await self.watch(url, messageHash, message, subscribeHash)
143
+
144
+ async def un_watch_public(self, messageHash, args, params={}):
145
+ url = self.urls['api']['ws']['public']
146
+ request: dict = {
147
+ 'op': 'unsubscribe',
148
+ 'args': [args],
149
+ }
150
+ message = self.extend(request, params)
151
+ return await self.watch(url, messageHash, message, messageHash)
152
+
153
+ async def watch_private(self, messageHash, subscribeHash, args, params={}):
154
+ await self.authenticate()
155
+ url = self.urls['api']['ws']['private']
156
+ request: dict = {
157
+ 'op': 'subscribe',
158
+ 'args': [args],
159
+ }
160
+ message = self.extend(request, params)
161
+ return await self.watch(url, messageHash, message, subscribeHash)
162
+
163
+ async def watch_private_multiple(self, messageHashes, subscribeHashes, args, params={}):
164
+ await self.authenticate()
165
+ url = self.urls['api']['ws']['private']
166
+ request: dict = {
167
+ 'op': 'subscribe',
168
+ 'args': args,
169
+ }
170
+ message = self.extend(request, params)
171
+ return await self.watch_multiple(url, messageHashes, message, subscribeHashes)
172
+
173
+ def handle_authenticate(self, client: Client, message):
174
+ #
175
+ # {event: "login", code: 0}
176
+ #
177
+ messageHash = 'authenticated'
178
+ future = self.safe_value(client.futures, messageHash)
179
+ future.resolve(True)
180
+
181
+ async def watch_public_multiple(self, messageHashes, subscribeHashes, argsArray, params={}):
182
+ url = self.urls['api']['ws']['public']
183
+ request: dict = {
184
+ 'op': 'subscribe',
185
+ 'args': argsArray,
186
+ }
187
+ message = self.extend(request, params)
188
+ return await self.watch_multiple(url, messageHashes, message, subscribeHashes)
189
+
190
+ async def un_watch_channel(self, symbol: str, channel: str, messageHashTopic: str, params={}) -> Any:
191
+ await self.load_markets()
192
+ market = self.market(symbol)
193
+ instType, instId = self.get_public_inst_type_and_id(market)
194
+ messageHash = 'unsubscribe:' + messageHashTopic + ':' + symbol
195
+ args: dict = {
196
+ 'instType': instType,
197
+ 'channel': channel,
198
+ 'instId': instId,
199
+ }
200
+ return await self.un_watch_public(messageHash, args, params)
201
+
202
+ def get_public_inst_type_and_id(self, market: Market):
203
+ instId = market['baseId'] + market['quoteId']
204
+ instType = None
205
+ if market['spot']:
206
+ instType = 'SP'
207
+ elif market['swap']:
208
+ instType = 'MC'
209
+ else:
210
+ raise NotSupported(self.id + ' supports only spot and swap markets')
211
+ return [instType, instId]
212
+
213
+ def handle_dmcbl_market_by_message_hashes(self, market: Market, hash: str, client: Client, timeframe: Str = None):
214
+ marketId = market['id']
215
+ messageHashes = self.find_message_hashes(client, hash)
216
+ # the exchange counts DMCBL markets same market with different quote currencies
217
+ # for example symbols ETHUSD:ETH and ETH/USD:BTC both have the same marketId ETHUSD_DMCBL
218
+ # we need to check all markets with the same marketId to find the correct market that is in messageHashes
219
+ marketsWithCurrentId = self.safe_list(self.markets_by_id, marketId, [])
220
+ suffix = ''
221
+ if timeframe is not None:
222
+ suffix = ':' + timeframe
223
+ for i in range(0, len(marketsWithCurrentId)):
224
+ market = marketsWithCurrentId[i]
225
+ symbol = market['symbol']
226
+ messageHash = hash + symbol + suffix
227
+ if self.in_array(messageHash, messageHashes):
228
+ return market
229
+ return market
230
+
231
+ async def watch_ticker(self, symbol: str, params={}) -> Ticker:
232
+ """
233
+ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
234
+
235
+ https://coincatch.github.io/github.io/en/spot/#tickers-channel
236
+
237
+ :param str symbol: unified symbol of the market to fetch the ticker for
238
+ :param dict [params]: extra parameters specific to the exchange API endpoint
239
+ :param str [params.instType]: the type of the instrument to fetch the ticker for, 'SP' for spot markets, 'MC' for futures markets(default is 'SP')
240
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
241
+ """
242
+ await self.load_markets()
243
+ market = self.market(symbol)
244
+ instType, instId = self.get_public_inst_type_and_id(market)
245
+ channel = 'ticker'
246
+ messageHash = channel + ':' + symbol
247
+ args: dict = {
248
+ 'instType': instType,
249
+ 'channel': channel,
250
+ 'instId': instId,
251
+ }
252
+ return await self.watch_public(messageHash, messageHash, args, params)
253
+
254
+ async def un_watch_ticker(self, symbol: str, params={}) -> Any:
255
+ """
256
+ unsubscribe from the ticker channel
257
+
258
+ https://coincatch.github.io/github.io/en/mix/#tickers-channel
259
+
260
+ :param str symbol: unified symbol of the market to unwatch the ticker for
261
+ :param dict [params]: extra parameters specific to the exchange API endpoint
262
+ :returns any: status of the unwatch request
263
+ """
264
+ await self.load_markets()
265
+ return await self.un_watch_channel(symbol, 'ticker', 'ticker', params)
266
+
267
+ async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
268
+ """
269
+ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
270
+
271
+ https://coincatch.github.io/github.io/en/mix/#tickers-channel
272
+
273
+ :param str[] symbols: unified symbol of the market to watch the tickers for
274
+ :param dict [params]: extra parameters specific to the exchange API endpoint
275
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
276
+ """
277
+ await self.load_markets()
278
+ if symbols is None:
279
+ symbols = self.symbols
280
+ topics = []
281
+ messageHashes = []
282
+ for i in range(0, len(symbols)):
283
+ symbol = symbols[i]
284
+ market = self.market(symbol)
285
+ instType, instId = self.get_public_inst_type_and_id(market)
286
+ args: dict = {
287
+ 'instType': instType,
288
+ 'channel': 'ticker',
289
+ 'instId': instId,
290
+ }
291
+ topics.append(args)
292
+ messageHashes.append('ticker:' + symbol)
293
+ tickers = await self.watch_public_multiple(messageHashes, messageHashes, topics, params)
294
+ if self.newUpdates:
295
+ result: dict = {}
296
+ result[tickers['symbol']] = tickers
297
+ return result
298
+ return self.filter_by_array(self.tickers, 'symbol', symbols)
299
+
300
+ def handle_ticker(self, client: Client, message):
301
+ #
302
+ # action: 'snapshot',
303
+ # arg: {instType: 'sp', channel: 'ticker', instId: 'ETHUSDT'},
304
+ # data: [
305
+ # {
306
+ # instId: 'ETHUSDT',
307
+ # last: '2421.06',
308
+ # open24h: '2416.93',
309
+ # high24h: '2441.47',
310
+ # low24h: '2352.99',
311
+ # bestBid: '2421.03',
312
+ # bestAsk: '2421.06',
313
+ # baseVolume: '9445.2043',
314
+ # quoteVolume: '22807159.1148',
315
+ # ts: 1728131730687,
316
+ # labeId: 0,
317
+ # openUtc: '2414.50',
318
+ # chgUTC: '0.00272',
319
+ # bidSz: '3.866',
320
+ # askSz: '0.124'
321
+ # }
322
+ # ],
323
+ # ts: 1728131730688
324
+ #
325
+ arg = self.safe_dict(message, 'arg', {})
326
+ market = self.get_market_from_arg(arg)
327
+ marketId = market['id']
328
+ hash = 'ticker:'
329
+ if marketId.find('_DMCBL') >= 0:
330
+ market = self.handle_dmcbl_market_by_message_hashes(market, hash, client)
331
+ data = self.safe_list(message, 'data', [])
332
+ ticker = self.parse_ws_ticker(self.safe_dict(data, 0, {}), market)
333
+ symbol = market['symbol']
334
+ self.tickers[symbol] = ticker
335
+ messageHash = hash + symbol
336
+ client.resolve(self.tickers[symbol], messageHash)
337
+
338
+ def parse_ws_ticker(self, ticker, market=None):
339
+ #
340
+ # spot
341
+ # {
342
+ # instId: 'ETHUSDT',
343
+ # last: '2421.06',
344
+ # open24h: '2416.93',
345
+ # high24h: '2441.47',
346
+ # low24h: '2352.99',
347
+ # bestBid: '2421.03',
348
+ # bestAsk: '2421.06',
349
+ # baseVolume: '9445.2043',
350
+ # quoteVolume: '22807159.1148',
351
+ # ts: 1728131730687,
352
+ # labeId: 0,
353
+ # openUtc: '2414.50',
354
+ # chgUTC: '0.00272',
355
+ # bidSz: '3.866',
356
+ # askSz: '0.124'
357
+ # }
358
+ #
359
+ # swap
360
+ # {
361
+ # instId: 'ETHUSDT',
362
+ # last: '2434.47',
363
+ # bestAsk: '2434.48',
364
+ # bestBid: '2434.47',
365
+ # high24h: '2471.68',
366
+ # low24h: '2400.01',
367
+ # priceChangePercent: '0.00674',
368
+ # capitalRate: '0.000082',
369
+ # nextSettleTime: 1728489600000,
370
+ # systemTime: 1728471993602,
371
+ # markPrice: '2434.46',
372
+ # indexPrice: '2435.44',
373
+ # holding: '171450.25',
374
+ # baseVolume: '1699298.91',
375
+ # quoteVolume: '4144522832.32',
376
+ # openUtc: '2439.67',
377
+ # chgUTC: '-0.00213',
378
+ # symbolType: 1,
379
+ # symbolId: 'ETHUSDT_UMCBL',
380
+ # deliveryPrice: '0',
381
+ # bidSz: '26.12',
382
+ # askSz: '49.6'
383
+ # }
384
+ #
385
+ last = self.safe_string(ticker, 'last')
386
+ timestamp = self.safe_integer_2(ticker, 'ts', 'systemTime')
387
+ return self.safe_ticker({
388
+ 'symbol': market['symbol'],
389
+ 'timestamp': timestamp,
390
+ 'datetime': self.iso8601(timestamp),
391
+ 'high': self.safe_string(ticker, 'high24h'),
392
+ 'low': self.safe_string(ticker, 'low24h'),
393
+ 'bid': self.safe_string(ticker, 'bestBid'),
394
+ 'bidVolume': self.safe_string(ticker, 'bidSz'),
395
+ 'ask': self.safe_string(ticker, 'bestAsk'),
396
+ 'askVolume': self.safe_string(ticker, 'askSz'),
397
+ 'vwap': None,
398
+ 'open': self.safe_string_2(ticker, 'open24h', 'openUtc'),
399
+ 'close': last,
400
+ 'last': last,
401
+ 'previousClose': None,
402
+ 'change': None,
403
+ 'percentage': Precise.string_mul(self.safe_string(ticker, 'chgUTC'), '100'),
404
+ 'average': None,
405
+ 'baseVolume': self.safe_number(ticker, 'baseVolume'),
406
+ 'quoteVolume': self.safe_number(ticker, 'quoteVolume'),
407
+ 'indexPrice': self.safe_string(ticker, 'indexPrice'),
408
+ 'markPrice': self.safe_string(ticker, 'markPrice'),
409
+ 'info': ticker,
410
+ }, market)
411
+
412
+ async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
413
+ """
414
+ watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
415
+
416
+ https://coincatch.github.io/github.io/en/spot/#candlesticks-channel
417
+
418
+ :param str symbol: unified symbol of the market to fetch OHLCV data for
419
+ :param str timeframe: the length of time each candle represents
420
+ :param int [since]: timestamp in ms of the earliest candle to fetch(not including)
421
+ :param int [limit]: the maximum amount of candles to fetch(not including)
422
+ :param dict [params]: extra parameters specific to the exchange API endpoint
423
+ :param bool [params.instType]: the type of the instrument to fetch the OHLCV data for, 'SP' for spot markets, 'MC' for futures markets(default is 'SP')
424
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
425
+ """
426
+ await self.load_markets()
427
+ market = self.market(symbol)
428
+ timeframes = self.options['timeframesForWs']
429
+ channel = 'candle' + self.safe_string(timeframes, timeframe)
430
+ instType, instId = self.get_public_inst_type_and_id(market)
431
+ args: dict = {
432
+ 'instType': instType,
433
+ 'channel': channel,
434
+ 'instId': instId,
435
+ }
436
+ messageHash = 'ohlcv:' + symbol + ':' + timeframe
437
+ ohlcv = await self.watch_public(messageHash, messageHash, args, params)
438
+ if self.newUpdates:
439
+ limit = ohlcv.getLimit(symbol, limit)
440
+ return self.filter_by_since_limit(ohlcv, since, limit, 0, True)
441
+
442
+ async def un_watch_ohlcv(self, symbol: str, timeframe='1m', params={}) -> Any:
443
+ """
444
+ unsubscribe from the ohlcv channel
445
+
446
+ https://www.bitget.com/api-doc/spot/websocket/public/Candlesticks-Channel
447
+
448
+ :param str symbol: unified symbol of the market to unwatch the ohlcv for
449
+ @param timeframe
450
+ :param dict [params]: extra parameters specific to the exchange API endpoint
451
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
452
+ """
453
+ await self.load_markets()
454
+ timeframes = self.options['timeframesForWs']
455
+ interval = self.safe_string(timeframes, timeframe)
456
+ channel = 'candle' + interval
457
+ return await self.un_watch_channel(symbol, channel, 'ohlcv:' + interval, params)
458
+
459
+ def handle_ohlcv(self, client: Client, message):
460
+ #
461
+ # {
462
+ # action: 'update',
463
+ # arg: {instType: 'sp', channel: 'candle1D', instId: 'ETHUSDT'},
464
+ # data: [
465
+ # [
466
+ # '1728316800000',
467
+ # '2474.5',
468
+ # '2478.21',
469
+ # '2459.8',
470
+ # '2463.51',
471
+ # '86.0551'
472
+ # ]
473
+ # ],
474
+ # ts: 1728317607657
475
+ # }
476
+ #
477
+ arg = self.safe_dict(message, 'arg', {})
478
+ market = self.get_market_from_arg(arg)
479
+ marketId = market['id']
480
+ hash = 'ohlcv:'
481
+ data = self.safe_list(message, 'data', [])
482
+ channel = self.safe_string(arg, 'channel')
483
+ klineType = channel[6:]
484
+ timeframe = self.find_timeframe(klineType)
485
+ if marketId.find('_DMCBL') >= 0:
486
+ market = self.handle_dmcbl_market_by_message_hashes(market, hash, client, timeframe)
487
+ symbol = market['symbol']
488
+ if not (symbol in self.ohlcvs):
489
+ self.ohlcvs[symbol] = {}
490
+ if not (timeframe in self.ohlcvs[symbol]):
491
+ limit = self.safe_integer(self.options, 'OHLCVLimit', 1000)
492
+ self.ohlcvs[symbol][timeframe] = ArrayCacheByTimestamp(limit)
493
+ stored = self.ohlcvs[symbol][timeframe]
494
+ for i in range(0, len(data)):
495
+ candle = self.safe_list(data, i, [])
496
+ parsed = self.parse_ws_ohlcv(candle, market)
497
+ stored.append(parsed)
498
+ messageHash = hash + symbol + ':' + timeframe
499
+ client.resolve(stored, messageHash)
500
+
501
+ def parse_ws_ohlcv(self, ohlcv, market: Market = None) -> list:
502
+ #
503
+ # [
504
+ # '1728316800000',
505
+ # '2474.5',
506
+ # '2478.21',
507
+ # '2459.8',
508
+ # '2463.51',
509
+ # '86.0551'
510
+ # ]
511
+ #
512
+ return [
513
+ self.safe_integer(ohlcv, 0),
514
+ self.safe_number(ohlcv, 1),
515
+ self.safe_number(ohlcv, 2),
516
+ self.safe_number(ohlcv, 3),
517
+ self.safe_number(ohlcv, 4),
518
+ self.safe_number(ohlcv, 5),
519
+ ]
520
+
521
+ async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
522
+ """
523
+ watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
524
+
525
+ https://coincatch.github.io/github.io/en/spot/#depth-channel
526
+
527
+ :param str symbol: unified symbol of the market to fetch the order book for
528
+ :param int [limit]: the maximum amount of order book entries to return
529
+ :param dict [params]: extra parameters specific to the exchange API endpoint
530
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
531
+ """
532
+ return await self.watch_order_book_for_symbols([symbol], limit, params)
533
+
534
+ async def un_watch_order_book(self, symbol: str, params={}) -> Any:
535
+ """
536
+ unsubscribe from the orderbook channel
537
+
538
+ https://coincatch.github.io/github.io/en/spot/#depth-channel
539
+
540
+ :param str symbol: unified symbol of the market to fetch the order book for
541
+ :param dict [params]: extra parameters specific to the exchange API endpoint
542
+ :param int [params.limit]: orderbook limit, default is None
543
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
544
+ """
545
+ await self.load_markets()
546
+ channel = 'books'
547
+ limit = self.safe_integer(params, 'limit')
548
+ if (limit == 5) or (limit == 15):
549
+ params = self.omit(params, 'limit')
550
+ channel += str(limit)
551
+ return await self.un_watch_channel(symbol, channel, channel, params)
552
+
553
+ async def watch_order_book_for_symbols(self, symbols: List[str], limit: Int = None, params={}) -> OrderBook:
554
+ """
555
+ watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
556
+
557
+ https://coincatch.github.io/github.io/en/spot/#depth-channel
558
+
559
+ @param symbols
560
+ :param int [limit]: the maximum amount of order book entries to return
561
+ :param dict [params]: extra parameters specific to the exchange API endpoint
562
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
563
+ """
564
+ await self.load_markets()
565
+ symbols = self.market_symbols(symbols)
566
+ channel = 'books'
567
+ topics = []
568
+ messageHashes = []
569
+ for i in range(0, len(symbols)):
570
+ symbol = symbols[i]
571
+ market = self.market(symbol)
572
+ instType, instId = self.get_public_inst_type_and_id(market)
573
+ args: dict = {
574
+ 'instType': instType,
575
+ 'channel': channel,
576
+ 'instId': instId,
577
+ }
578
+ topics.append(args)
579
+ messageHashes.append(channel + ':' + symbol)
580
+ orderbook = await self.watch_public_multiple(messageHashes, messageHashes, topics, params)
581
+ return orderbook.limit()
582
+
583
+ def handle_order_book(self, client: Client, message):
584
+ #
585
+ # {
586
+ # action: 'update',
587
+ # arg: {instType: 'sp', channel: 'books', instId: 'ETHUSDT'},
588
+ # data: [
589
+ # {
590
+ # asks: [[2507.07, 0.4248]],
591
+ # bids: [[2507.05, 0.1198]],
592
+ # checksum: -1400923312,
593
+ # ts: '1728339446908'
594
+ # }
595
+ # ],
596
+ # ts: 1728339446908
597
+ # }
598
+ #
599
+ arg = self.safe_dict(message, 'arg', {})
600
+ market = self.get_market_from_arg(arg)
601
+ marketId = market['id']
602
+ hash = 'books:'
603
+ if marketId.find('_DMCBL') >= 0:
604
+ market = self.handle_dmcbl_market_by_message_hashes(market, hash, client)
605
+ symbol = market['symbol']
606
+ channel = self.safe_string(arg, 'channel')
607
+ messageHash = hash + symbol
608
+ data = self.safe_list(message, 'data', [])
609
+ rawOrderBook = self.safe_dict(data, 0)
610
+ timestamp = self.safe_integer(rawOrderBook, 'ts')
611
+ incrementalBook = channel
612
+ if incrementalBook:
613
+ if not (symbol in self.orderbooks):
614
+ ob = self.counted_order_book({})
615
+ ob['symbol'] = symbol
616
+ self.orderbooks[symbol] = ob
617
+ storedOrderBook = self.orderbooks[symbol]
618
+ asks = self.safe_list(rawOrderBook, 'asks', [])
619
+ bids = self.safe_list(rawOrderBook, 'bids', [])
620
+ self.handle_deltas(storedOrderBook['asks'], asks)
621
+ self.handle_deltas(storedOrderBook['bids'], bids)
622
+ storedOrderBook['timestamp'] = timestamp
623
+ storedOrderBook['datetime'] = self.iso8601(timestamp)
624
+ checksum = self.safe_bool(self.options, 'checksum', True)
625
+ isSnapshot = self.safe_string(message, 'action') == 'snapshot'
626
+ if not isSnapshot and checksum:
627
+ storedAsks = storedOrderBook['asks']
628
+ storedBids = storedOrderBook['bids']
629
+ asksLength = len(storedAsks)
630
+ bidsLength = len(storedBids)
631
+ payloadArray = []
632
+ for i in range(0, 25):
633
+ if i < bidsLength:
634
+ payloadArray.append(storedBids[i][2][0])
635
+ payloadArray.append(storedBids[i][2][1])
636
+ if i < asksLength:
637
+ payloadArray.append(storedAsks[i][2][0])
638
+ payloadArray.append(storedAsks[i][2][1])
639
+ payload = ':'.join(payloadArray)
640
+ calculatedChecksum = self.crc32(payload, True)
641
+ responseChecksum = self.safe_integer(rawOrderBook, 'checksum')
642
+ if calculatedChecksum != responseChecksum:
643
+ self.spawn(self.handle_check_sum_error, client, symbol, messageHash)
644
+ return
645
+ else:
646
+ orderbook = self.order_book({})
647
+ parsedOrderbook = self.parse_order_book(rawOrderBook, symbol, timestamp)
648
+ orderbook.reset(parsedOrderbook)
649
+ self.orderbooks[symbol] = orderbook
650
+ client.resolve(self.orderbooks[symbol], messageHash)
651
+
652
+ async def handle_check_sum_error(self, client: Client, symbol: str, messageHash: str):
653
+ await self.un_watch_order_book(symbol)
654
+ error = ChecksumError(self.id + ' ' + self.orderbook_checksum_message(symbol))
655
+ client.reject(error, messageHash)
656
+
657
+ def handle_delta(self, bookside, delta):
658
+ bidAsk = self.parse_bid_ask(delta, 0, 1)
659
+ bidAsk.append(delta)
660
+ bookside.storeArray(bidAsk)
661
+
662
+ def handle_deltas(self, bookside, deltas):
663
+ for i in range(0, len(deltas)):
664
+ self.handle_delta(bookside, deltas[i])
665
+
666
+ async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
667
+ """
668
+ get the list of most recent trades for a particular symbol
669
+
670
+ https://coincatch.github.io/github.io/en/spot/#trades-channel
671
+
672
+ :param str symbol: unified symbol of the market to fetch trades for
673
+ :param int [since]: timestamp in ms of the earliest trade to fetch
674
+ :param int [limit]: the maximum amount of trades to fetch
675
+ :param dict [params]: extra parameters specific to the exchange API endpoint
676
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
677
+ """
678
+ return await self.watch_trades_for_symbols([symbol], since, limit, params)
679
+
680
+ async def watch_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Trade]:
681
+ """
682
+ watches information on multiple trades made in a market
683
+
684
+ https://coincatch.github.io/github.io/en/spot/#trades-channel
685
+
686
+ @param symbols
687
+ :param int [since]: the earliest time in ms to fetch orders for
688
+ :param int [limit]: the maximum number of trade structures to retrieve
689
+ :param dict [params]: extra parameters specific to the exchange API endpoint
690
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
691
+ """
692
+ symbolsLength = len(symbols)
693
+ if symbolsLength == 0:
694
+ raise ArgumentsRequired(self.id + ' watchTradesForSymbols() requires a non-empty array of symbols')
695
+ await self.load_markets()
696
+ symbols = self.market_symbols(symbols)
697
+ topics = []
698
+ messageHashes = []
699
+ for i in range(0, len(symbols)):
700
+ symbol = symbols[i]
701
+ market = self.market(symbol)
702
+ instType, instId = self.get_public_inst_type_and_id(market)
703
+ args: dict = {
704
+ 'instType': instType,
705
+ 'channel': 'trade',
706
+ 'instId': instId,
707
+ }
708
+ topics.append(args)
709
+ messageHashes.append('trade:' + symbol)
710
+ trades = await self.watch_public_multiple(messageHashes, messageHashes, topics, params)
711
+ if self.newUpdates:
712
+ first = self.safe_dict(trades, 0)
713
+ tradeSymbol = self.safe_string(first, 'symbol')
714
+ limit = trades.getLimit(tradeSymbol, limit)
715
+ return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
716
+
717
+ async def un_watch_trades(self, symbol: str, params={}) -> Any:
718
+ """
719
+ unsubscribe from the trades channel
720
+
721
+ https://coincatch.github.io/github.io/en/spot/#trades-channel
722
+
723
+ :param str symbol: unified symbol of the market to unwatch the trades for
724
+ :param dict [params]: extra parameters specific to the exchange API endpoint
725
+ :returns any: status of the unwatch request
726
+ """
727
+ await self.load_markets()
728
+ return await self.un_watch_channel(symbol, 'trade', 'trade', params)
729
+
730
+ def handle_trades(self, client: Client, message):
731
+ #
732
+ # {
733
+ # action: 'update',
734
+ # arg: {instType: 'sp', channel: 'trade', instId: 'ETHUSDT'},
735
+ # data: [['1728341807469', '2421.41', '0.478', 'sell']],
736
+ # ts: 1728341807482
737
+ # }
738
+ #
739
+ arg = self.safe_dict(message, 'arg', {})
740
+ market = self.get_market_from_arg(arg)
741
+ marketId = market['id']
742
+ hash = 'trade:'
743
+ if marketId.find('_DMCBL') >= 0:
744
+ market = self.handle_dmcbl_market_by_message_hashes(market, hash, client)
745
+ symbol = market['symbol']
746
+ if not (symbol in self.trades):
747
+ limit = self.safe_integer(self.options, 'tradesLimit', 1000)
748
+ self.trades[symbol] = ArrayCache(limit)
749
+ stored = self.trades[symbol]
750
+ data = self.safe_list(message, 'data', [])
751
+ if data is not None:
752
+ data = self.sort_by(data, 0)
753
+ for i in range(0, len(data)):
754
+ trade = self.safe_list(data, i)
755
+ parsed = self.parse_ws_trade(trade, market)
756
+ stored.append(parsed)
757
+ messageHash = hash + symbol
758
+ client.resolve(stored, messageHash)
759
+
760
+ def parse_ws_trade(self, trade, market=None) -> Trade:
761
+ #
762
+ # [
763
+ # '1728341807469',
764
+ # '2421.41',
765
+ # '0.478',
766
+ # 'sell'
767
+ # ]
768
+ #
769
+ timestamp = self.safe_integer(trade, 0)
770
+ return self.safe_trade({
771
+ 'id': None,
772
+ 'timestamp': timestamp,
773
+ 'datetime': self.iso8601(timestamp),
774
+ 'symbol': market['symbol'],
775
+ 'side': self.safe_string_lower(trade, 3),
776
+ 'price': self.safe_string(trade, 1),
777
+ 'amount': self.safe_string(trade, 2),
778
+ 'cost': None,
779
+ 'takerOrMaker': None,
780
+ 'type': None,
781
+ 'order': None,
782
+ 'fee': None,
783
+ 'info': trade,
784
+ }, market)
785
+
786
+ async def watch_balance(self, params={}) -> Balances:
787
+ """
788
+ watch balance and get the amount of funds available for trading or funds locked in orders
789
+
790
+ https://coincatch.github.io/github.io/en/spot/#account-channel
791
+ https://coincatch.github.io/github.io/en/mix/#account-channel
792
+
793
+ :param dict [params]: extra parameters specific to the exchange API endpoint
794
+ :param str [params.type]: 'spot' or 'swap'(default is 'spot')
795
+ :param str [params.instType]: *swap only* 'umcbl' or 'dmcbl'(default is 'umcbl')
796
+ :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
797
+ """
798
+ type = None
799
+ type, params = self.handle_market_type_and_params('watchBalance', None, params)
800
+ instType = 'spbl' # must be lower case for spot
801
+ if type == 'swap':
802
+ instType = 'umcbl'
803
+ channel = 'account'
804
+ instType, params = self.handle_option_and_params(params, 'watchBalance', 'instType', instType)
805
+ args: dict = {
806
+ 'instType': instType,
807
+ 'channel': channel,
808
+ 'instId': 'default',
809
+ }
810
+ messageHash = 'balance:' + instType.lower()
811
+ return await self.watch_private(messageHash, messageHash, args, params)
812
+
813
+ def handle_balance(self, client: Client, message):
814
+ #
815
+ # spot
816
+ # {
817
+ # action: 'snapshot',
818
+ # arg: {instType: 'spbl', channel: 'account', instId: 'default'},
819
+ # data: [
820
+ # {
821
+ # coinId: '3',
822
+ # coinName: 'ETH',
823
+ # available: '0.0000832',
824
+ # frozen: '0',
825
+ # lock: '0'
826
+ # }
827
+ # ],
828
+ # ts: 1728464548725
829
+ # }
830
+ #
831
+ # # swap
832
+ # {
833
+ # action: 'snapshot',
834
+ # arg: {instType: 'dmcbl', channel: 'account', instId: 'default'},
835
+ # data: [
836
+ # {
837
+ # marginCoin: 'ETH',
838
+ # locked: '0.00000000',
839
+ # available: '0.00001203',
840
+ # maxOpenPosAvailable: '0.00001203',
841
+ # maxTransferOut: '0.00001203',
842
+ # equity: '0.00001203',
843
+ # usdtEquity: '0.029092328738',
844
+ # coinDisplayName: 'ETH'
845
+ # }
846
+ # ],
847
+ # ts: 1728650777643
848
+ # }
849
+ #
850
+ data = self.safe_list(message, 'data', [])
851
+ for i in range(0, len(data)):
852
+ rawBalance = data[i]
853
+ currencyId = self.safe_string_2(rawBalance, 'coinName', 'marginCoin')
854
+ code = self.safe_currency_code(currencyId)
855
+ account = self.balance[code] if (code in self.balance) else self.account()
856
+ freeQuery = 'maxTransferOut' if ('maxTransferOut' in rawBalance) else 'available'
857
+ account['free'] = self.safe_string(rawBalance, freeQuery)
858
+ account['total'] = self.safe_string(rawBalance, 'equity')
859
+ account['used'] = self.safe_string(rawBalance, 'frozen')
860
+ self.balance[code] = account
861
+ self.balance = self.safe_balance(self.balance)
862
+ arg = self.safe_dict(message, 'arg')
863
+ instType = self.safe_string_lower(arg, 'instType')
864
+ messageHash = 'balance:' + instType
865
+ client.resolve(self.balance, messageHash)
866
+
867
+ async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
868
+ """
869
+ watches information on multiple orders made by the user
870
+
871
+ https://coincatch.github.io/github.io/en/spot/#order-channel
872
+ https://coincatch.github.io/github.io/en/mix/#order-channel
873
+ https://coincatch.github.io/github.io/en/mix/#plan-order-channel
874
+
875
+ :param str symbol: unified market symbol of the market orders were made in
876
+ :param int [since]: the earliest time in ms to fetch orders for
877
+ :param int [limit]: the maximum number of order structures to retrieve
878
+ :param dict [params]: extra parameters specific to the exchange API endpoint
879
+ :param str [params.type]: 'spot' or 'swap'
880
+ :param str [params.instType]: *swap only* 'umcbl' or 'dmcbl'(default is 'umcbl')
881
+ :param bool [params.trigger]: *swap only* whether to watch trigger orders(default is False)
882
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
883
+ """
884
+ methodName = 'watchOrders'
885
+ await self.load_markets()
886
+ market = None
887
+ marketId = None
888
+ if symbol is not None:
889
+ market = self.market(symbol)
890
+ symbol = market['symbol']
891
+ marketId = market['id']
892
+ marketType = None
893
+ marketType, params = self.handle_market_type_and_params(methodName, market, params)
894
+ instType = 'spbl'
895
+ instId = marketId
896
+ if marketType == 'spot':
897
+ if symbol is None:
898
+ raise ArgumentsRequired(self.id + ' ' + methodName + '() requires a symbol argument for ' + marketType + ' markets.')
899
+ else:
900
+ instId = 'default'
901
+ instType = 'umcbl'
902
+ if symbol is None:
903
+ instType, params = self.handle_option_and_params(params, methodName, 'instType', instType)
904
+ else:
905
+ if marketId.find('_DMCBL') >= 0:
906
+ instType = 'dmcbl'
907
+ channel = 'orders'
908
+ isTrigger = self.safe_bool(params, 'trigger')
909
+ if isTrigger:
910
+ channel = 'ordersAlgo' # channel does not return any data
911
+ params = self.omit(params, 'trigger')
912
+ args: dict = {
913
+ 'instType': instType,
914
+ 'channel': channel,
915
+ 'instId': instId,
916
+ }
917
+ messageHash = 'orders'
918
+ if symbol is not None:
919
+ messageHash += ':' + symbol
920
+ orders = await self.watch_private(messageHash, messageHash, args, params)
921
+ if self.newUpdates:
922
+ limit = orders.getLimit(symbol, limit)
923
+ return self.filter_by_symbol_since_limit(orders, symbol, since, limit, True)
924
+
925
+ def handle_order(self, client: Client, message):
926
+ #
927
+ # spot
928
+ #
929
+ # {
930
+ # action: 'snapshot',
931
+ # arg: {instType: 'spbl', channel: 'orders', instId: 'ETHUSDT_SPBL'},
932
+ # data: [
933
+ # {
934
+ # instId: 'ETHUSDT_SPBL',
935
+ # ordId: '1228627925964996608',
936
+ # clOrdId: 'f0cccf74-c535-4523-a53d-dbe3b9958559',
937
+ # px: '2000',
938
+ # sz: '0.001',
939
+ # notional: '2',
940
+ # ordType: 'limit',
941
+ # force: 'normal',
942
+ # side: 'buy',
943
+ # accFillSz: '0',
944
+ # avgPx: '0',
945
+ # status: 'new',
946
+ # cTime: 1728653645030,
947
+ # uTime: 1728653645030,
948
+ # orderFee: [],
949
+ # eps: 'API'
950
+ # }
951
+ # ],
952
+ # ts: 1728653645046
953
+ # }
954
+ #
955
+ # swap
956
+ #
957
+ # {
958
+ # action: 'snapshot',
959
+ # arg: {instType: 'umcbl', channel: 'orders', instId: 'default'},
960
+ # data: [
961
+ # {
962
+ # accFillSz: '0',
963
+ # cTime: 1728653796976,
964
+ # clOrdId: '1228628563272753152',
965
+ # eps: 'API',
966
+ # force: 'normal',
967
+ # hM: 'single_hold',
968
+ # instId: 'ETHUSDT_UMCBL',
969
+ # lever: '5',
970
+ # low: False,
971
+ # notionalUsd: '20',
972
+ # ordId: '1228628563188867072',
973
+ # ordType: 'limit',
974
+ # orderFee: [],
975
+ # posSide: 'net',
976
+ # px: '2000',
977
+ # side: 'buy',
978
+ # status: 'new',
979
+ # sz: '0.01',
980
+ # tS: 'buy_single',
981
+ # tdMode: 'cross',
982
+ # tgtCcy: 'USDT',
983
+ # uTime: 1728653796976
984
+ # }
985
+ # ],
986
+ # ts: 1728653797002
987
+ # }
988
+ #
989
+ #
990
+ arg = self.safe_dict(message, 'arg', {})
991
+ instType = self.safe_string(arg, 'instType')
992
+ argInstId = self.safe_string(arg, 'instId')
993
+ marketType = None
994
+ if instType == 'spbl':
995
+ marketType = 'spot'
996
+ else:
997
+ marketType = 'swap'
998
+ data = self.safe_list(message, 'data', [])
999
+ if self.orders is None:
1000
+ limit = self.safe_integer(self.options, 'ordersLimit', 1000)
1001
+ self.orders = ArrayCacheBySymbolById(limit)
1002
+ hash = 'orders'
1003
+ stored = self.orders
1004
+ symbol: Str = None
1005
+ for i in range(0, len(data)):
1006
+ order = data[i]
1007
+ marketId = self.safe_string(order, 'instId', argInstId)
1008
+ market = self.safe_market(marketId, None, None, marketType)
1009
+ parsed = self.parse_ws_order(order, market)
1010
+ stored.append(parsed)
1011
+ symbol = parsed['symbol']
1012
+ messageHash = 'orders:' + symbol
1013
+ client.resolve(stored, messageHash)
1014
+ client.resolve(stored, hash)
1015
+
1016
+ def parse_ws_order(self, order: dict, market: Market = None) -> Order:
1017
+ #
1018
+ # spot
1019
+ # {
1020
+ # instId: 'ETHUSDT_SPBL',
1021
+ # ordId: '1228627925964996608',
1022
+ # clOrdId: 'f0cccf74-c535-4523-a53d-dbe3b9958559',
1023
+ # px: '2000',
1024
+ # sz: '0.001',
1025
+ # notional: '2',
1026
+ # ordType: 'limit',
1027
+ # force: 'normal',
1028
+ # side: 'buy',
1029
+ # accFillSz: '0',
1030
+ # avgPx: '0',
1031
+ # status: 'new',
1032
+ # cTime: 1728653645030,
1033
+ # uTime: 1728653645030,
1034
+ # orderFee: orderFee: [{fee: '0', feeCcy: 'USDT'}],
1035
+ # eps: 'API'
1036
+ # }
1037
+ #
1038
+ # swap
1039
+ # {
1040
+ # accFillSz: '0',
1041
+ # cTime: 1728653796976,
1042
+ # clOrdId: '1228628563272753152',
1043
+ # eps: 'API',
1044
+ # force: 'normal',
1045
+ # hM: 'single_hold',
1046
+ # instId: 'ETHUSDT_UMCBL',
1047
+ # lever: '5',
1048
+ # low: False,
1049
+ # notionalUsd: '20',
1050
+ # ordId: '1228628563188867072',
1051
+ # ordType: 'limit',
1052
+ # orderFee: [{fee: '0', feeCcy: 'USDT'}],
1053
+ # posSide: 'net',
1054
+ # px: '2000',
1055
+ # side: 'buy',
1056
+ # status: 'new',
1057
+ # sz: '0.01',
1058
+ # tS: 'buy_single',
1059
+ # tdMode: 'cross',
1060
+ # tgtCcy: 'USDT',
1061
+ # uTime: 1728653796976
1062
+ # }
1063
+ #
1064
+ marketId = self.safe_string(order, 'instId')
1065
+ settleId = self.safe_string(order, 'tgtCcy')
1066
+ market = self.safeMarketCustom(marketId, market, settleId)
1067
+ timestamp = self.safe_integer(order, 'cTime')
1068
+ symbol = market['symbol']
1069
+ rawStatus = self.safe_string(order, 'status')
1070
+ orderFee = self.safe_list(order, 'orderFee', [])
1071
+ fee = self.safe_dict(orderFee, 0)
1072
+ feeCost = Precise.string_mul(self.safe_string(fee, 'fee'), '-1')
1073
+ feeCurrency = self.safe_string(fee, 'feeCcy')
1074
+ price = self.omit_zero(self.safe_string(order, 'px'))
1075
+ priceAvg = self.omit_zero(self.safe_string(order, 'avgPx'))
1076
+ if price is None:
1077
+ price = priceAvg
1078
+ type = self.safe_string_lower(order, 'ordType')
1079
+ return self.safe_order({
1080
+ 'id': self.safe_string(order, 'ordId'),
1081
+ 'clientOrderId': self.safe_string(order, 'clOrdId'),
1082
+ 'timestamp': timestamp,
1083
+ 'datetime': self.iso8601(timestamp),
1084
+ 'lastTradeTimestamp': None,
1085
+ 'lastUpdateTimestamp': self.safe_integer(order, 'uTime'),
1086
+ 'status': self.parse_order_status(rawStatus),
1087
+ 'symbol': symbol,
1088
+ 'type': type,
1089
+ 'timeInForce': self.parseOrderTimeInForce(self.safe_string_lower(order, 'force')),
1090
+ 'side': self.safe_string_lower(order, 'side'),
1091
+ 'price': price,
1092
+ 'average': self.safe_string(order, 'avgPx'),
1093
+ 'amount': self.safe_string(order, 'sz'),
1094
+ 'filled': self.safe_string(order, 'accFillSz'),
1095
+ 'remaining': None,
1096
+ 'triggerPrice': None,
1097
+ 'takeProfitPrice': None,
1098
+ 'stopLossPrice': None,
1099
+ 'cost': self.safe_string(order, 'notional'),
1100
+ 'trades': None,
1101
+ 'fee': {
1102
+ 'currency': feeCurrency,
1103
+ 'cost': feeCost,
1104
+ },
1105
+ 'reduceOnly': self.safe_bool(order, 'low'),
1106
+ 'postOnly': None,
1107
+ 'info': order,
1108
+ }, market)
1109
+
1110
+ async def watch_positions(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]:
1111
+ """
1112
+ watch all open positions
1113
+
1114
+ https://coincatch.github.io/github.io/en/mix/#positions-channel
1115
+
1116
+ :param str[]|None symbols: list of unified market symbols
1117
+ @param since
1118
+ @param limit
1119
+ :param dict params: extra parameters specific to the exchange API endpoint
1120
+ :returns dict[]: a list of `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
1121
+ """
1122
+ await self.load_markets()
1123
+ symbols = self.market_symbols(symbols, 'swap')
1124
+ messageHashes = []
1125
+ hash = 'positions'
1126
+ instTypes = []
1127
+ if symbols is not None:
1128
+ for i in range(0, len(symbols)):
1129
+ symbol = symbols[i]
1130
+ market = self.market(symbol)
1131
+ instType = self.get_private_inst_type(market)
1132
+ if not self.in_array(instType, instTypes):
1133
+ instTypes.append(instType)
1134
+ messageHashes.append(hash + '::' + symbol)
1135
+ else:
1136
+ instTypes = ['umcbl', 'dmcbl']
1137
+ messageHashes.append(hash)
1138
+ args = []
1139
+ subscribeHashes = []
1140
+ for i in range(0, len(instTypes)):
1141
+ instType = instTypes[i]
1142
+ arg: dict = {
1143
+ 'instType': instType,
1144
+ 'channel': hash,
1145
+ 'instId': 'default',
1146
+ }
1147
+ subscribeHashes.append(hash + '::' + instType)
1148
+ args.append(arg)
1149
+ newPositions = await self.watch_private_multiple(messageHashes, subscribeHashes, args, params)
1150
+ if self.newUpdates:
1151
+ return newPositions
1152
+ return self.filter_by_symbols_since_limit(newPositions, symbols, since, limit, True)
1153
+
1154
+ def get_private_inst_type(self, market: Market):
1155
+ marketId = market['id']
1156
+ if marketId.find('_DMCBL') >= 0:
1157
+ return 'dmcbl'
1158
+ return 'umcbl'
1159
+
1160
+ def handle_positions(self, client: Client, message):
1161
+ #
1162
+ # {
1163
+ # action: 'snapshot',
1164
+ # arg: {instType: 'umcbl', channel: 'positions', instId: 'default'},
1165
+ # data: [
1166
+ # {
1167
+ # posId: '1221355728745619456',
1168
+ # instId: 'ETHUSDT_UMCBL',
1169
+ # instName: 'ETHUSDT',
1170
+ # marginCoin: 'USDT',
1171
+ # margin: '5.27182',
1172
+ # marginMode: 'crossed',
1173
+ # holdSide: 'long',
1174
+ # holdMode: 'single_hold',
1175
+ # total: '0.01',
1176
+ # available: '0.01',
1177
+ # locked: '0',
1178
+ # averageOpenPrice: '2635.91',
1179
+ # leverage: 5,
1180
+ # achievedProfits: '0',
1181
+ # upl: '-0.0267',
1182
+ # uplRate: '-0.005064664576',
1183
+ # liqPx: '-3110.66866033',
1184
+ # keepMarginRate: '0.0033',
1185
+ # marginRate: '0.002460827254',
1186
+ # cTime: '1726919818102',
1187
+ # uTime: '1728919604312',
1188
+ # markPrice: '2633.24',
1189
+ # autoMargin: 'off'
1190
+ # }
1191
+ # ],
1192
+ # ts: 1728919604329
1193
+ # }
1194
+ #
1195
+ if self.positions is None:
1196
+ self.positions = ArrayCacheBySymbolBySide()
1197
+ cache = self.positions
1198
+ rawPositions = self.safe_value(message, 'data', [])
1199
+ dataLength = len(rawPositions)
1200
+ if dataLength == 0:
1201
+ return
1202
+ newPositions = []
1203
+ symbols = []
1204
+ for i in range(0, len(rawPositions)):
1205
+ rawPosition = rawPositions[i]
1206
+ position = self.parse_ws_position(rawPosition)
1207
+ symbols.append(position['symbol'])
1208
+ newPositions.append(position)
1209
+ cache.append(position)
1210
+ hash = 'positions'
1211
+ messageHashes = self.find_message_hashes(client, hash)
1212
+ for i in range(0, len(messageHashes)):
1213
+ messageHash = messageHashes[i]
1214
+ parts = messageHash.split('::')
1215
+ symbol = parts[1]
1216
+ if self.in_array(symbol, symbols):
1217
+ positionsForSymbol = []
1218
+ for j in range(0, len(newPositions)):
1219
+ position = newPositions[j]
1220
+ if position['symbol'] == symbol:
1221
+ positionsForSymbol.append(position)
1222
+ client.resolve(positionsForSymbol, messageHash)
1223
+ client.resolve(newPositions, hash)
1224
+
1225
+ def parse_ws_position(self, position, market=None):
1226
+ #
1227
+ # {
1228
+ # posId: '1221355728745619456',
1229
+ # instId: 'ETHUSDT_UMCBL',
1230
+ # instName: 'ETHUSDT',
1231
+ # marginCoin: 'USDT',
1232
+ # margin: '5.27182',
1233
+ # marginMode: 'crossed',
1234
+ # holdSide: 'long',
1235
+ # holdMode: 'single_hold',
1236
+ # total: '0.01',
1237
+ # available: '0.01',
1238
+ # locked: '0',
1239
+ # averageOpenPrice: '2635.91',
1240
+ # leverage: 5,
1241
+ # achievedProfits: '0',
1242
+ # upl: '-0.0267',
1243
+ # uplRate: '-0.005064664576',
1244
+ # liqPx: '-3110.66866033',
1245
+ # keepMarginRate: '0.0033',
1246
+ # marginRate: '0.002460827254',
1247
+ # cTime: '1726919818102',
1248
+ # uTime: '1728919604312',
1249
+ # markPrice: '2633.24',
1250
+ # autoMargin: 'off'
1251
+ # }
1252
+ #
1253
+ marketId = self.safe_string(position, 'symbol')
1254
+ settleId = self.safe_string(position, 'marginCoin')
1255
+ market = self.safeMarketCustom(marketId, market, settleId)
1256
+ timestamp = self.safe_integer(position, 'cTime')
1257
+ marginModeId = self.safe_string(position, 'marginMode')
1258
+ marginMode = self.get_supported_mapping(marginModeId, {
1259
+ 'crossed': 'cross',
1260
+ 'isolated': 'isolated',
1261
+ })
1262
+ isHedged: Bool = None
1263
+ holdMode = self.safe_string(position, 'holdMode')
1264
+ if holdMode == 'double_hold':
1265
+ isHedged = True
1266
+ elif holdMode == 'single_hold':
1267
+ isHedged = False
1268
+ percentageDecimal = self.safe_string(position, 'uplRate')
1269
+ percentage = Precise.string_mul(percentageDecimal, '100')
1270
+ margin = self.safe_number(position, 'margin')
1271
+ return self.safe_position({
1272
+ 'symbol': market['symbol'],
1273
+ 'id': None,
1274
+ 'timestamp': timestamp,
1275
+ 'datetime': self.iso8601(timestamp),
1276
+ 'contracts': self.safe_number(position, 'total'),
1277
+ 'contractSize': None,
1278
+ 'side': self.safe_string_lower(position, 'holdSide'),
1279
+ 'notional': margin, # todo check
1280
+ 'leverage': self.safe_integer(position, 'leverage'),
1281
+ 'unrealizedPnl': self.safe_number(position, 'upl'),
1282
+ 'realizedPnl': self.safe_number(position, 'achievedProfits'),
1283
+ 'collateral': None, # todo check
1284
+ 'entryPrice': self.safe_number(position, 'averageOpenPrice'),
1285
+ 'markPrice': self.safe_number(position, 'markPrice'),
1286
+ 'liquidationPrice': self.safe_number(position, 'liqPx'),
1287
+ 'marginMode': marginMode,
1288
+ 'hedged': isHedged,
1289
+ 'maintenanceMargin': None, # todo check
1290
+ 'maintenanceMarginPercentage': self.safe_number(position, 'keepMarginRate'),
1291
+ 'initialMargin': margin, # todo check
1292
+ 'initialMarginPercentage': None,
1293
+ 'marginRatio': self.safe_number(position, 'marginRate'),
1294
+ 'lastUpdateTimestamp': self.safe_integer(position, 'uTime'),
1295
+ 'lastPrice': None,
1296
+ 'stopLossPrice': None,
1297
+ 'takeProfitPrice': None,
1298
+ 'percentage': percentage,
1299
+ 'info': position,
1300
+ })
1301
+
1302
+ def handle_error_message(self, client: Client, message):
1303
+ #
1304
+ # {event: "error", code: 30001, msg: "Channel does not exist"}
1305
+ #
1306
+ event = self.safe_string(message, 'event')
1307
+ try:
1308
+ if event == 'error':
1309
+ code = self.safe_string(message, 'code')
1310
+ feedback = self.id + ' ' + self.json(message)
1311
+ self.throw_exactly_matched_exception(self.exceptions['ws']['exact'], code, feedback)
1312
+ msg = self.safe_string(message, 'msg', '')
1313
+ self.throw_broadly_matched_exception(self.exceptions['ws']['broad'], msg, feedback)
1314
+ raise ExchangeError(feedback)
1315
+ return False
1316
+ except Exception as e:
1317
+ if isinstance(e, AuthenticationError):
1318
+ messageHash = 'authenticated'
1319
+ client.reject(e, messageHash)
1320
+ if messageHash in client.subscriptions:
1321
+ del client.subscriptions[messageHash]
1322
+ else:
1323
+ client.reject(e)
1324
+ return True
1325
+
1326
+ def handle_message(self, client: Client, message):
1327
+ # todo handle with subscribe and unsubscribe
1328
+ if self.handle_error_message(client, message):
1329
+ return
1330
+ content = self.safe_string(message, 'message')
1331
+ if content == 'pong':
1332
+ self.handle_pong(client, message)
1333
+ return
1334
+ if message == 'pong':
1335
+ self.handle_pong(client, message)
1336
+ return
1337
+ event = self.safe_string(message, 'event')
1338
+ if event == 'login':
1339
+ self.handle_authenticate(client, message)
1340
+ return
1341
+ if event == 'subscribe':
1342
+ self.handle_subscription_status(client, message)
1343
+ return
1344
+ if event == 'unsubscribe':
1345
+ self.handle_un_subscription_status(client, message)
1346
+ return
1347
+ data = self.safe_dict(message, 'arg', {})
1348
+ channel = self.safe_string(data, 'channel')
1349
+ if channel == 'ticker':
1350
+ self.handle_ticker(client, message)
1351
+ if channel.find('candle') >= 0:
1352
+ self.handle_ohlcv(client, message)
1353
+ if channel.find('books') >= 0:
1354
+ self.handle_order_book(client, message)
1355
+ if channel == 'trade':
1356
+ self.handle_trades(client, message)
1357
+ if channel == 'account':
1358
+ self.handle_balance(client, message)
1359
+ if (channel == 'orders') or (channel == 'ordersAlgo'):
1360
+ self.handle_order(client, message)
1361
+ if channel == 'positions':
1362
+ self.handle_positions(client, message)
1363
+
1364
+ def ping(self, client: Client):
1365
+ return 'ping'
1366
+
1367
+ def handle_pong(self, client: Client, message):
1368
+ client.lastPong = self.milliseconds()
1369
+ return message
1370
+
1371
+ def handle_subscription_status(self, client: Client, message):
1372
+ return message
1373
+
1374
+ def handle_un_subscription_status(self, client: Client, message):
1375
+ argsList = self.safe_list(message, 'args')
1376
+ if argsList is None:
1377
+ argsList = [self.safe_dict(message, 'arg', {})]
1378
+ for i in range(0, len(argsList)):
1379
+ arg = argsList[i]
1380
+ channel = self.safe_string(arg, 'channel')
1381
+ if channel == 'books':
1382
+ self.handle_order_book_un_subscription(client, message)
1383
+ elif channel == 'trade':
1384
+ self.handle_trades_un_subscription(client, message)
1385
+ elif channel == 'ticker':
1386
+ self.handle_ticker_un_subscription(client, message)
1387
+ elif channel.startswith('candle'):
1388
+ self.handle_ohlcv_un_subscription(client, message)
1389
+ return message
1390
+
1391
+ def handle_order_book_un_subscription(self, client: Client, message):
1392
+ arg = self.safe_dict(message, 'arg', {})
1393
+ instType = self.safe_string_lower(arg, 'instType')
1394
+ type = 'spot' if (instType == 'sp') else 'swap'
1395
+ instId = self.safe_string(arg, 'instId')
1396
+ market = self.safe_market(instId, None, None, type)
1397
+ symbol = market['symbol']
1398
+ messageHash = 'unsubscribe:orderbook:' + market['symbol']
1399
+ subMessageHash = 'orderbook:' + symbol
1400
+ if symbol in self.orderbooks:
1401
+ del self.orderbooks[symbol]
1402
+ if subMessageHash in client.subscriptions:
1403
+ del client.subscriptions[subMessageHash]
1404
+ if messageHash in client.subscriptions:
1405
+ del client.subscriptions[messageHash]
1406
+ error = UnsubscribeError(self.id + 'orderbook ' + symbol)
1407
+ client.reject(error, subMessageHash)
1408
+ client.resolve(True, messageHash)
1409
+
1410
+ def handle_trades_un_subscription(self, client: Client, message):
1411
+ arg = self.safe_dict(message, 'arg', {})
1412
+ instType = self.safe_string_lower(arg, 'instType')
1413
+ type = 'spot' if (instType == 'sp') else 'swap'
1414
+ instId = self.safe_string(arg, 'instId')
1415
+ market = self.safe_market(instId, None, None, type)
1416
+ symbol = market['symbol']
1417
+ messageHash = 'unsubscribe:trade:' + market['symbol']
1418
+ subMessageHash = 'trade:' + symbol
1419
+ if symbol in self.trades:
1420
+ del self.trades[symbol]
1421
+ if subMessageHash in client.subscriptions:
1422
+ del client.subscriptions[subMessageHash]
1423
+ if messageHash in client.subscriptions:
1424
+ del client.subscriptions[messageHash]
1425
+ error = UnsubscribeError(self.id + 'trades ' + symbol)
1426
+ client.reject(error, subMessageHash)
1427
+ client.resolve(True, messageHash)
1428
+
1429
+ def handle_ticker_un_subscription(self, client: Client, message):
1430
+ arg = self.safe_dict(message, 'arg', {})
1431
+ instType = self.safe_string_lower(arg, 'instType')
1432
+ type = 'spot' if (instType == 'sp') else 'swap'
1433
+ instId = self.safe_string(arg, 'instId')
1434
+ market = self.safe_market(instId, None, None, type)
1435
+ symbol = market['symbol']
1436
+ messageHash = 'unsubscribe:ticker:' + market['symbol']
1437
+ subMessageHash = 'ticker:' + symbol
1438
+ if symbol in self.tickers:
1439
+ del self.tickers[symbol]
1440
+ if subMessageHash in client.subscriptions:
1441
+ del client.subscriptions[subMessageHash]
1442
+ if messageHash in client.subscriptions:
1443
+ del client.subscriptions[messageHash]
1444
+ error = UnsubscribeError(self.id + 'ticker ' + symbol)
1445
+ client.reject(error, subMessageHash)
1446
+ client.resolve(True, messageHash)
1447
+
1448
+ def handle_ohlcv_un_subscription(self, client: Client, message):
1449
+ arg = self.safe_dict(message, 'arg', {})
1450
+ instType = self.safe_string_lower(arg, 'instType')
1451
+ type = 'spot' if (instType == 'sp') else 'swap'
1452
+ instId = self.safe_string(arg, 'instId')
1453
+ channel = self.safe_string(arg, 'channel')
1454
+ interval = channel.replace('candle', '')
1455
+ timeframes = self.safe_dict(self.options, 'timeframesForWs')
1456
+ timeframe = self.find_timeframe(interval, timeframes)
1457
+ market = self.safe_market(instId, None, None, type)
1458
+ symbol = market['symbol']
1459
+ messageHash = 'unsubscribe:ohlcv:' + timeframe + ':' + market['symbol']
1460
+ subMessageHash = 'ohlcv:' + symbol + ':' + timeframe
1461
+ if symbol in self.ohlcvs:
1462
+ if timeframe in self.ohlcvs[symbol]:
1463
+ del self.ohlcvs[symbol][timeframe]
1464
+ self.clean_unsubscription(client, subMessageHash, messageHash)