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/okx.py CHANGED
@@ -6,13 +6,16 @@
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, FundingRate, FundingRates, Trade
10
10
  from ccxt.async_support.base.ws.client import Client
11
11
  from typing import List
12
+ from typing import Any
13
+ from ccxt.base.errors import ExchangeError
14
+ from ccxt.base.errors import AuthenticationError
12
15
  from ccxt.base.errors import ArgumentsRequired
13
16
  from ccxt.base.errors import BadRequest
14
17
  from ccxt.base.errors import InvalidNonce
15
- from ccxt.base.errors import AuthenticationError
18
+ from ccxt.base.errors import ChecksumError
16
19
 
17
20
 
18
21
  class okx(ccxt.async_support.okx):
@@ -22,17 +25,26 @@ class okx(ccxt.async_support.okx):
22
25
  'has': {
23
26
  'ws': True,
24
27
  'watchTicker': True,
28
+ 'watchMarkPrice': True,
29
+ 'watchMarkPrices': True,
25
30
  'watchTickers': True,
31
+ 'watchBidsAsks': True,
26
32
  'watchOrderBook': True,
27
33
  'watchTrades': True,
28
34
  'watchTradesForSymbols': True,
29
35
  'watchOrderBookForSymbols': True,
30
36
  'watchBalance': True,
37
+ 'watchLiquidations': 'emulated',
38
+ 'watchLiquidationsForSymbols': True,
39
+ 'watchMyLiquidations': 'emulated',
40
+ 'watchMyLiquidationsForSymbols': True,
31
41
  'watchOHLCV': True,
32
42
  'watchOHLCVForSymbols': True,
33
43
  'watchOrders': True,
34
44
  'watchMyTrades': True,
35
45
  'watchPositions': True,
46
+ 'watchFundingRate': True,
47
+ 'watchFundingRates': True,
36
48
  'createOrderWs': True,
37
49
  'editOrderWs': True,
38
50
  'cancelOrderWs': True,
@@ -49,6 +61,7 @@ class okx(ccxt.async_support.okx):
49
61
  },
50
62
  'options': {
51
63
  'watchOrderBook': {
64
+ 'checksum': True,
52
65
  #
53
66
  # bbo-tbt
54
67
  # 1. Newly added channel that sends tick-by-tick Level 1 data
@@ -96,13 +109,12 @@ class okx(ccxt.async_support.okx):
96
109
  'ws': {
97
110
  # 'inflate': True,
98
111
  },
99
- 'checksum': True,
100
112
  },
101
113
  'streaming': {
102
114
  # okex does not support built-in ws protocol-level ping-pong
103
115
  # instead it requires a custom text-based ping-pong
104
116
  'ping': self.ping,
105
- 'keepAlive': 20000,
117
+ 'keepAlive': 18000,
106
118
  },
107
119
  })
108
120
 
@@ -125,33 +137,33 @@ class okx(ccxt.async_support.okx):
125
137
  symbols = self.symbols
126
138
  symbols = self.market_symbols(symbols)
127
139
  url = self.get_url(channel, access)
128
- messageHash = channel
140
+ messageHashes = []
129
141
  args = []
130
- messageHash += '::' + ','.join(symbols)
131
142
  for i in range(0, len(symbols)):
132
143
  marketId = self.market_id(symbols[i])
133
- arg = {
144
+ arg: dict = {
134
145
  'channel': channel,
135
146
  'instId': marketId,
136
147
  }
137
148
  args.append(self.extend(arg, params))
138
- request = {
149
+ messageHashes.append(channel + '::' + symbols[i])
150
+ request: dict = {
139
151
  'op': 'subscribe',
140
152
  'args': args,
141
153
  }
142
- return await self.watch(url, messageHash, request, messageHash)
154
+ return await self.watch_multiple(url, messageHashes, request, messageHashes)
143
155
 
144
156
  async def subscribe(self, access, messageHash, channel, symbol, params={}):
145
157
  await self.load_markets()
146
158
  url = self.get_url(channel, access)
147
- firstArgument = {
159
+ firstArgument: dict = {
148
160
  'channel': channel,
149
161
  }
150
162
  if symbol is not None:
151
163
  market = self.market(symbol)
152
164
  messageHash += ':' + market['id']
153
165
  firstArgument['instId'] = market['id']
154
- request = {
166
+ request: dict = {
155
167
  'op': 'subscribe',
156
168
  'args': [
157
169
  self.deep_extend(firstArgument, params),
@@ -173,7 +185,7 @@ class okx(ccxt.async_support.okx):
173
185
  async def watch_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Trade]:
174
186
  """
175
187
  get the list of most recent trades for a particular symbol
176
- :param str symbol: unified symbol of the market to fetch trades for
188
+ :param str symbols:
177
189
  :param int [since]: timestamp in ms of the earliest trade to fetch
178
190
  :param int [limit]: the maximum amount of trades to fetch
179
191
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -191,12 +203,12 @@ class okx(ccxt.async_support.okx):
191
203
  symbol = symbols[i]
192
204
  messageHashes.append(channel + ':' + symbol)
193
205
  marketId = self.market_id(symbol)
194
- topic = {
206
+ topic: dict = {
195
207
  'channel': channel,
196
208
  'instId': marketId,
197
209
  }
198
210
  topics.append(topic)
199
- request = {
211
+ request: dict = {
200
212
  'op': 'subscribe',
201
213
  'args': topics,
202
214
  }
@@ -208,6 +220,43 @@ class okx(ccxt.async_support.okx):
208
220
  limit = trades.getLimit(tradeSymbol, limit)
209
221
  return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
210
222
 
223
+ async def un_watch_trades_for_symbols(self, symbols: List[str], params={}) -> Any:
224
+ """
225
+ unWatches from the stream channel
226
+ :param str[] symbols:
227
+ :param dict [params]: extra parameters specific to the exchange API endpoint
228
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
229
+ """
230
+ await self.load_markets()
231
+ symbols = self.market_symbols(symbols, None, False)
232
+ channel = 'trades'
233
+ topics = []
234
+ messageHashes = []
235
+ for i in range(0, len(symbols)):
236
+ symbol = symbols[i]
237
+ messageHashes.append('unsubscribe:trades:' + symbol)
238
+ marketId = self.market_id(symbol)
239
+ topic: dict = {
240
+ 'channel': channel,
241
+ 'instId': marketId,
242
+ }
243
+ topics.append(topic)
244
+ request: dict = {
245
+ 'op': 'unsubscribe',
246
+ 'args': topics,
247
+ }
248
+ url = self.get_url(channel, 'public')
249
+ return await self.watch_multiple(url, messageHashes, request, messageHashes)
250
+
251
+ async def un_watch_trades(self, symbol: str, params={}) -> Any:
252
+ """
253
+ unWatches from the stream channel
254
+ :param str symbol: unified symbol of the market to fetch trades for
255
+ :param dict [params]: extra parameters specific to the exchange API endpoint
256
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
257
+ """
258
+ return await self.un_watch_trades_for_symbols([symbol], params)
259
+
211
260
  def handle_trades(self, client: Client, message):
212
261
  #
213
262
  # {
@@ -240,9 +289,90 @@ class okx(ccxt.async_support.okx):
240
289
  stored.append(trade)
241
290
  client.resolve(stored, messageHash)
242
291
 
292
+ async def watch_funding_rate(self, symbol: str, params={}) -> FundingRate:
293
+ """
294
+ watch the current funding rate
295
+
296
+ https://www.okx.com/docs-v5/en/#public-data-websocket-funding-rate-channel
297
+
298
+ :param str symbol: unified market symbol
299
+ :param dict [params]: extra parameters specific to the exchange API endpoint
300
+ :returns dict: a `funding rate structure <https://docs.ccxt.com/#/?id=funding-rate-structure>`
301
+ """
302
+ symbol = self.symbol(symbol)
303
+ fr = await self.watch_funding_rates([symbol], params)
304
+ return fr[symbol]
305
+
306
+ async def watch_funding_rates(self, symbols: List[str], params={}) -> FundingRates:
307
+ """
308
+ watch the funding rate for multiple markets
309
+
310
+ https://www.okx.com/docs-v5/en/#public-data-websocket-funding-rate-channel
311
+
312
+ :param str[] symbols: list of unified market symbols
313
+ :param dict [params]: extra parameters specific to the exchange API endpoint
314
+ :returns dict: a dictionary of `funding rates structures <https://docs.ccxt.com/#/?id=funding-rates-structure>`, indexe by market symbols
315
+ """
316
+ await self.load_markets()
317
+ symbols = self.market_symbols(symbols)
318
+ channel = 'funding-rate'
319
+ topics = []
320
+ messageHashes = []
321
+ for i in range(0, len(symbols)):
322
+ symbol = symbols[i]
323
+ messageHashes.append(channel + ':' + symbol)
324
+ marketId = self.market_id(symbol)
325
+ topic: dict = {
326
+ 'channel': channel,
327
+ 'instId': marketId,
328
+ }
329
+ topics.append(topic)
330
+ request: dict = {
331
+ 'op': 'subscribe',
332
+ 'args': topics,
333
+ }
334
+ url = self.get_url(channel, 'public')
335
+ fundingRate = await self.watch_multiple(url, messageHashes, request, messageHashes)
336
+ if self.newUpdates:
337
+ symbol = self.safe_string(fundingRate, 'symbol')
338
+ result: dict = {}
339
+ result[symbol] = fundingRate
340
+ return result
341
+ return self.filter_by_array(self.fundingRates, 'symbol', symbols)
342
+
343
+ def handle_funding_rate(self, client: Client, message):
344
+ #
345
+ # "data":[
346
+ # {
347
+ # "fundingRate":"0.0001875391284828",
348
+ # "fundingTime":"1700726400000",
349
+ # "instId":"BTC-USD-SWAP",
350
+ # "instType":"SWAP",
351
+ # "method": "next_period",
352
+ # "maxFundingRate":"0.00375",
353
+ # "minFundingRate":"-0.00375",
354
+ # "nextFundingRate":"0.0002608059239328",
355
+ # "nextFundingTime":"1700755200000",
356
+ # "premium": "0.0001233824646391",
357
+ # "settFundingRate":"0.0001699799259033",
358
+ # "settState":"settled",
359
+ # "ts":"1700724675402"
360
+ # }
361
+ # ]
362
+ #
363
+ data = self.safe_list(message, 'data', [])
364
+ for i in range(0, len(data)):
365
+ rawfr = data[i]
366
+ fundingRate = self.parse_funding_rate(rawfr)
367
+ symbol = fundingRate['symbol']
368
+ self.fundingRates[symbol] = fundingRate
369
+ client.resolve(fundingRate, 'funding-rate' + ':' + fundingRate['symbol'])
370
+
243
371
  async def watch_ticker(self, symbol: str, params={}) -> Ticker:
244
372
  """
245
- :see: https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-tickers-channel
373
+
374
+ https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-tickers-channel
375
+
246
376
  watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
247
377
  :param str symbol: unified symbol of the market to fetch the ticker for
248
378
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -252,20 +382,37 @@ class okx(ccxt.async_support.okx):
252
382
  channel = None
253
383
  channel, params = self.handle_option_and_params(params, 'watchTicker', 'channel', 'tickers')
254
384
  params['channel'] = channel
385
+ market = self.market(symbol)
386
+ symbol = market['symbol']
255
387
  ticker = await self.watch_tickers([symbol], params)
256
388
  return self.safe_value(ticker, symbol)
257
389
 
390
+ async def un_watch_ticker(self, symbol: str, params={}) -> Any:
391
+ """
392
+
393
+ https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-tickers-channel
394
+
395
+ unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
396
+ :param str symbol: unified symbol of the market to fetch the ticker for
397
+ :param dict [params]: extra parameters specific to the exchange API endpoint
398
+ :param str [params.channel]: the channel to subscribe to, tickers by default. Can be tickers, sprd-tickers, index-tickers, block-tickers
399
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
400
+ """
401
+ return await self.un_watch_tickers([symbol], params)
402
+
258
403
  async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
259
404
  """
260
- :see: https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-tickers-channel
405
+
406
+ https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-tickers-channel
407
+
261
408
  watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
262
409
  :param str[] [symbols]: unified symbol of the market to fetch the ticker for
263
410
  :param dict [params]: extra parameters specific to the exchange API endpoint
264
411
  :param str [params.channel]: the channel to subscribe to, tickers by default. Can be tickers, sprd-tickers, index-tickers, block-tickers
265
412
  :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
266
413
  """
267
- if self.is_empty(symbols):
268
- raise ArgumentsRequired(self.id + ' watchTickers requires a list of symbols')
414
+ await self.load_markets()
415
+ symbols = self.market_symbols(symbols, None, False)
269
416
  channel = None
270
417
  channel, params = self.handle_option_and_params(params, 'watchTickers', 'channel', 'tickers')
271
418
  newTickers = await self.subscribe_multiple('public', channel, symbols, params)
@@ -273,6 +420,78 @@ class okx(ccxt.async_support.okx):
273
420
  return newTickers
274
421
  return self.filter_by_array(self.tickers, 'symbol', symbols)
275
422
 
423
+ async def watch_mark_price(self, symbol: str, params={}) -> Ticker:
424
+ """
425
+
426
+ https://www.okx.com/docs-v5/en/#public-data-websocket-mark-price-channel
427
+
428
+ watches a mark price
429
+ :param str symbol: unified symbol of the market to fetch the ticker for
430
+ :param dict [params]: extra parameters specific to the exchange API endpoint
431
+ :param str [params.channel]: the channel to subscribe to, tickers by default. Can be tickers, sprd-tickers, index-tickers, block-tickers
432
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
433
+ """
434
+ channel = None
435
+ channel, params = self.handle_option_and_params(params, 'watchMarkPrice', 'channel', 'mark-price')
436
+ params['channel'] = channel
437
+ market = self.market(symbol)
438
+ symbol = market['symbol']
439
+ ticker = await self.watch_mark_prices([symbol], params)
440
+ return ticker[symbol]
441
+
442
+ async def watch_mark_prices(self, symbols: Strings = None, params={}) -> Tickers:
443
+ """
444
+
445
+ https://www.okx.com/docs-v5/en/#public-data-websocket-mark-price-channel
446
+
447
+ watches mark prices
448
+ :param str[] [symbols]: unified symbol of the market to fetch the ticker for
449
+ :param dict [params]: extra parameters specific to the exchange API endpoint
450
+ :param str [params.channel]: the channel to subscribe to, tickers by default. Can be tickers, sprd-tickers, index-tickers, block-tickers
451
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
452
+ """
453
+ await self.load_markets()
454
+ symbols = self.market_symbols(symbols, None, False)
455
+ channel = None
456
+ channel, params = self.handle_option_and_params(params, 'watchMarkPrices', 'channel', 'mark-price')
457
+ newTickers = await self.subscribe_multiple('public', channel, symbols, params)
458
+ if self.newUpdates:
459
+ return newTickers
460
+ return self.filter_by_array(self.tickers, 'symbol', symbols)
461
+
462
+ async def un_watch_tickers(self, symbols: Strings = None, params={}) -> Any:
463
+ """
464
+
465
+ https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-tickers-channel
466
+
467
+ unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
468
+ :param str[] [symbols]: unified symbol of the market to fetch the ticker for
469
+ :param dict [params]: extra parameters specific to the exchange API endpoint
470
+ :param str [params.channel]: the channel to subscribe to, tickers by default. Can be tickers, sprd-tickers, index-tickers, block-tickers
471
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
472
+ """
473
+ await self.load_markets()
474
+ symbols = self.market_symbols(symbols, None, False)
475
+ channel = None
476
+ channel, params = self.handle_option_and_params(params, 'watchTickers', 'channel', 'tickers')
477
+ topics = []
478
+ messageHashes = []
479
+ for i in range(0, len(symbols)):
480
+ symbol = symbols[i]
481
+ messageHashes.append('unsubscribe:ticker:' + symbol)
482
+ marketId = self.market_id(symbol)
483
+ topic: dict = {
484
+ 'channel': channel,
485
+ 'instId': marketId,
486
+ }
487
+ topics.append(topic)
488
+ request: dict = {
489
+ 'op': 'unsubscribe',
490
+ 'args': topics,
491
+ }
492
+ url = self.get_url(channel, 'public')
493
+ return await self.watch_multiple(url, messageHashes, request, messageHashes)
494
+
276
495
  def handle_ticker(self, client: Client, message):
277
496
  #
278
497
  # {
@@ -299,27 +518,370 @@ class okx(ccxt.async_support.okx):
299
518
  # ]
300
519
  # }
301
520
  #
521
+ self.handle_bid_ask(client, message)
302
522
  arg = self.safe_value(message, 'arg', {})
523
+ marketId = self.safe_string(arg, 'instId')
524
+ market = self.safe_market(marketId, None, '-')
525
+ symbol = market['symbol']
303
526
  channel = self.safe_string(arg, 'channel')
304
527
  data = self.safe_value(message, 'data', [])
305
- newTickers = []
528
+ newTickers: dict = {}
306
529
  for i in range(0, len(data)):
307
530
  ticker = self.parse_ticker(data[i])
308
- symbol = ticker['symbol']
309
531
  self.tickers[symbol] = ticker
310
- newTickers.append(ticker)
311
- messageHashes = self.find_message_hashes(client, channel + '::')
312
- for i in range(0, len(messageHashes)):
313
- messageHash = messageHashes[i]
314
- parts = messageHash.split('::')
315
- symbolsString = parts[1]
316
- symbols = symbolsString.split(',')
317
- tickers = self.filter_by_array(newTickers, 'symbol', symbols)
318
- tickersSymbols = list(tickers.keys())
319
- numTickers = len(tickersSymbols)
320
- if numTickers > 0:
321
- client.resolve(tickers, messageHash)
322
- return message
532
+ newTickers[symbol] = ticker
533
+ messageHash = channel + '::' + symbol
534
+ client.resolve(newTickers, messageHash)
535
+
536
+ async def watch_bids_asks(self, symbols: Strings = None, params={}) -> Tickers:
537
+ """
538
+
539
+ https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-tickers-channel
540
+
541
+ watches best bid & ask for symbols
542
+ :param str[] symbols: unified symbol of the market to fetch the ticker for
543
+ :param dict [params]: extra parameters specific to the exchange API endpoint
544
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
545
+ """
546
+ await self.load_markets()
547
+ symbols = self.market_symbols(symbols, None, False)
548
+ channel = None
549
+ channel, params = self.handle_option_and_params(params, 'watchBidsAsks', 'channel', 'tickers')
550
+ url = self.get_url(channel, 'public')
551
+ messageHashes = []
552
+ args = []
553
+ for i in range(0, len(symbols)):
554
+ marketId = self.market_id(symbols[i])
555
+ arg: dict = {
556
+ 'channel': channel,
557
+ 'instId': marketId,
558
+ }
559
+ args.append(self.extend(arg, params))
560
+ messageHashes.append('bidask::' + symbols[i])
561
+ request: dict = {
562
+ 'op': 'subscribe',
563
+ 'args': args,
564
+ }
565
+ newTickers = await self.watch_multiple(url, messageHashes, request, messageHashes)
566
+ if self.newUpdates:
567
+ tickers: dict = {}
568
+ tickers[newTickers['symbol']] = newTickers
569
+ return tickers
570
+ return self.filter_by_array(self.bidsasks, 'symbol', symbols)
571
+
572
+ def handle_bid_ask(self, client: Client, message):
573
+ #
574
+ # {
575
+ # "arg": {channel: "tickers", instId: "BTC-USDT"},
576
+ # "data": [
577
+ # {
578
+ # "instType": "SPOT",
579
+ # "instId": "BTC-USDT",
580
+ # "last": "31500.1",
581
+ # "lastSz": "0.00001754",
582
+ # "askPx": "31500.1",
583
+ # "askSz": "0.00998144",
584
+ # "bidPx": "31500",
585
+ # "bidSz": "3.05652439",
586
+ # "open24h": "31697",
587
+ # "high24h": "32248",
588
+ # "low24h": "31165.6",
589
+ # "sodUtc0": "31385.5",
590
+ # "sodUtc8": "32134.9",
591
+ # "volCcy24h": "503403597.38138519",
592
+ # "vol24h": "15937.10781721",
593
+ # "ts": "1626526618762"
594
+ # }
595
+ # ]
596
+ # }
597
+ #
598
+ data = self.safe_list(message, 'data', [])
599
+ ticker = self.safe_dict(data, 0, {})
600
+ parsedTicker = self.parse_ws_bid_ask(ticker)
601
+ symbol = parsedTicker['symbol']
602
+ self.bidsasks[symbol] = parsedTicker
603
+ messageHash = 'bidask::' + symbol
604
+ client.resolve(parsedTicker, messageHash)
605
+
606
+ def parse_ws_bid_ask(self, ticker, market=None):
607
+ marketId = self.safe_string(ticker, 'instId')
608
+ market = self.safe_market(marketId, market)
609
+ symbol = self.safe_string(market, 'symbol')
610
+ timestamp = self.safe_integer(ticker, 'ts')
611
+ return self.safe_ticker({
612
+ 'symbol': symbol,
613
+ 'timestamp': timestamp,
614
+ 'datetime': self.iso8601(timestamp),
615
+ 'ask': self.safe_string(ticker, 'askPx'),
616
+ 'askVolume': self.safe_string(ticker, 'askSz'),
617
+ 'bid': self.safe_string(ticker, 'bidPx'),
618
+ 'bidVolume': self.safe_string(ticker, 'bidSz'),
619
+ 'info': ticker,
620
+ }, market)
621
+
622
+ async def watch_liquidations_for_symbols(self, symbols: List[str] = None, since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
623
+ """
624
+ watch the public liquidations of a trading pair
625
+
626
+ https://www.okx.com/docs-v5/en/#public-data-websocket-liquidation-orders-channel
627
+
628
+ :param str symbols:
629
+ :param int [since]: the earliest time in ms to fetch liquidations for
630
+ :param int [limit]: the maximum number of liquidation structures to retrieve
631
+ :param dict [params]: exchange specific parameters for the okx api endpoint
632
+ :returns dict: an array of `liquidation structures <https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure>`
633
+ """
634
+ await self.load_markets()
635
+ symbols = self.market_symbols(symbols, None, True, True)
636
+ messageHash = 'liquidations'
637
+ messageHashes = []
638
+ if symbols is not None:
639
+ for i in range(0, len(symbols)):
640
+ symbol = symbols[i]
641
+ messageHashes.append(messageHash + '::' + symbol)
642
+ else:
643
+ messageHashes.append(messageHash)
644
+ market = self.get_market_from_symbols(symbols)
645
+ type = None
646
+ type, params = self.handle_market_type_and_params('watchliquidationsForSymbols', market, params)
647
+ channel = 'liquidation-orders'
648
+ if type == 'spot':
649
+ type = 'SWAP'
650
+ elif type == 'future':
651
+ type = 'futures'
652
+ uppercaseType = type.upper()
653
+ request = {
654
+ 'op': 'subscribe',
655
+ 'args': [
656
+ {
657
+ 'channel': channel,
658
+ 'instType': uppercaseType,
659
+ },
660
+ ],
661
+ }
662
+ url = self.get_url(channel, 'public')
663
+ newLiquidations = await self.watch_multiple(url, messageHashes, request, messageHashes)
664
+ if self.newUpdates:
665
+ return newLiquidations
666
+ return self.filter_by_symbols_since_limit(self.liquidations, symbols, since, limit, True)
667
+
668
+ def handle_liquidation(self, client: Client, message):
669
+ #
670
+ # {
671
+ # "arg": {
672
+ # "channel": "liquidation-orders",
673
+ # "instType": "SWAP"
674
+ # },
675
+ # "data": [
676
+ # {
677
+ # "details": [
678
+ # {
679
+ # "bkLoss": "0",
680
+ # "bkPx": "0.007831",
681
+ # "ccy": "",
682
+ # "posSide": "short",
683
+ # "side": "buy",
684
+ # "sz": "13",
685
+ # "ts": "1692266434010"
686
+ # }
687
+ # ],
688
+ # "instFamily": "IOST-USDT",
689
+ # "instId": "IOST-USDT-SWAP",
690
+ # "instType": "SWAP",
691
+ # "uly": "IOST-USDT"
692
+ # }
693
+ # ]
694
+ # }
695
+ #
696
+ rawLiquidations = self.safe_list(message, 'data', [])
697
+ for i in range(0, len(rawLiquidations)):
698
+ rawLiquidation = rawLiquidations[i]
699
+ liquidation = self.parse_ws_liquidation(rawLiquidation)
700
+ symbol = self.safe_string(liquidation, 'symbol')
701
+ liquidations = self.safe_value(self.liquidations, symbol)
702
+ if liquidations is None:
703
+ limit = self.safe_integer(self.options, 'liquidationsLimit', 1000)
704
+ liquidations = ArrayCache(limit)
705
+ liquidations.append(liquidation)
706
+ self.liquidations[symbol] = liquidations
707
+ client.resolve([liquidation], 'liquidations')
708
+ client.resolve([liquidation], 'liquidations::' + symbol)
709
+
710
+ async def watch_my_liquidations_for_symbols(self, symbols: List[str] = None, since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
711
+ """
712
+ watch the private liquidations of a trading pair
713
+
714
+ https://www.okx.com/docs-v5/en/#trading-account-websocket-balance-and-position-channel
715
+
716
+ :param str[] symbols:
717
+ :param int [since]: the earliest time in ms to fetch liquidations for
718
+ :param int [limit]: the maximum number of liquidation structures to retrieve
719
+ :param dict [params]: exchange specific parameters for the okx api endpoint
720
+ :returns dict: an array of `liquidation structures <https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure>`
721
+ """
722
+ await self.load_markets()
723
+ isTrigger = self.safe_value_2(params, 'stop', 'trigger', False)
724
+ params = self.omit(params, ['stop', 'trigger'])
725
+ await self.authenticate({'access': 'business' if isTrigger else 'private'})
726
+ symbols = self.market_symbols(symbols, None, True, True)
727
+ messageHash = 'myLiquidations'
728
+ messageHashes = []
729
+ if symbols is not None:
730
+ for i in range(0, len(symbols)):
731
+ symbol = symbols[i]
732
+ messageHashes.append(messageHash + '::' + symbol)
733
+ else:
734
+ messageHashes.append(messageHash)
735
+ channel = 'balance_and_position'
736
+ request: dict = {
737
+ 'op': 'subscribe',
738
+ 'args': [
739
+ {
740
+ 'channel': channel,
741
+ },
742
+ ],
743
+ }
744
+ url = self.get_url(channel, 'private')
745
+ newLiquidations = await self.watch_multiple(url, messageHashes, self.deep_extend(request, params), messageHashes)
746
+ if self.newUpdates:
747
+ return newLiquidations
748
+ return self.filter_by_symbols_since_limit(self.liquidations, symbols, since, limit, True)
749
+
750
+ def handle_my_liquidation(self, client: Client, message):
751
+ #
752
+ # {
753
+ # "arg": {
754
+ # "channel": "balance_and_position",
755
+ # "uid": "77982378738415879"
756
+ # },
757
+ # "data": [{
758
+ # "pTime": "1597026383085",
759
+ # "eventType": "snapshot",
760
+ # "balData": [{
761
+ # "ccy": "BTC",
762
+ # "cashBal": "1",
763
+ # "uTime": "1597026383085"
764
+ # }],
765
+ # "posData": [{
766
+ # "posId": "1111111111",
767
+ # "tradeId": "2",
768
+ # "instId": "BTC-USD-191018",
769
+ # "instType": "FUTURES",
770
+ # "mgnMode": "cross",
771
+ # "posSide": "long",
772
+ # "pos": "10",
773
+ # "ccy": "BTC",
774
+ # "posCcy": "",
775
+ # "avgPx": "3320",
776
+ # "uTIme": "1597026383085"
777
+ # }],
778
+ # "trades": [{
779
+ # "instId": "BTC-USD-191018",
780
+ # "tradeId": "2",
781
+ # }]
782
+ # }]
783
+ # }
784
+ #
785
+ rawLiquidations = self.safe_list(message, 'data', [])
786
+ for i in range(0, len(rawLiquidations)):
787
+ rawLiquidation = rawLiquidations[i]
788
+ eventType = self.safe_string(rawLiquidation, 'eventType')
789
+ if eventType != 'liquidation':
790
+ return
791
+ liquidation = self.parse_ws_my_liquidation(rawLiquidation)
792
+ symbol = self.safe_string(liquidation, 'symbol')
793
+ liquidations = self.safe_value(self.liquidations, symbol)
794
+ if liquidations is None:
795
+ limit = self.safe_integer(self.options, 'myLiquidationsLimit', 1000)
796
+ liquidations = ArrayCache(limit)
797
+ liquidations.append(liquidation)
798
+ self.liquidations[symbol] = liquidations
799
+ client.resolve([liquidation], 'myLiquidations')
800
+ client.resolve([liquidation], 'myLiquidations::' + symbol)
801
+
802
+ def parse_ws_my_liquidation(self, liquidation, market=None):
803
+ #
804
+ # {
805
+ # "pTime": "1597026383085",
806
+ # "eventType": "snapshot",
807
+ # "balData": [{
808
+ # "ccy": "BTC",
809
+ # "cashBal": "1",
810
+ # "uTime": "1597026383085"
811
+ # }],
812
+ # "posData": [{
813
+ # "posId": "1111111111",
814
+ # "tradeId": "2",
815
+ # "instId": "BTC-USD-191018",
816
+ # "instType": "FUTURES",
817
+ # "mgnMode": "cross",
818
+ # "posSide": "long",
819
+ # "pos": "10",
820
+ # "ccy": "BTC",
821
+ # "posCcy": "",
822
+ # "avgPx": "3320",
823
+ # "uTIme": "1597026383085"
824
+ # }],
825
+ # "trades": [{
826
+ # "instId": "BTC-USD-191018",
827
+ # "tradeId": "2",
828
+ # }]
829
+ # }
830
+ #
831
+ posData = self.safe_list(liquidation, 'posData', [])
832
+ firstPosData = self.safe_dict(posData, 0, {})
833
+ marketId = self.safe_string(firstPosData, 'instId')
834
+ market = self.safe_market(marketId, market)
835
+ timestamp = self.safe_integer(firstPosData, 'uTIme')
836
+ return self.safe_liquidation({
837
+ 'info': liquidation,
838
+ 'symbol': self.safe_symbol(marketId, market),
839
+ 'contracts': self.safe_number(firstPosData, 'pos'),
840
+ 'contractSize': self.safe_number(market, 'contractSize'),
841
+ 'price': self.safe_number(liquidation, 'avgPx'),
842
+ 'baseValue': None,
843
+ 'quoteValue': None,
844
+ 'timestamp': timestamp,
845
+ 'datetime': self.iso8601(timestamp),
846
+ })
847
+
848
+ def parse_ws_liquidation(self, liquidation, market=None):
849
+ #
850
+ # public liquidation
851
+ # {
852
+ # "details": [
853
+ # {
854
+ # "bkLoss": "0",
855
+ # "bkPx": "0.007831",
856
+ # "ccy": "",
857
+ # "posSide": "short",
858
+ # "side": "buy",
859
+ # "sz": "13",
860
+ # "ts": "1692266434010"
861
+ # }
862
+ # ],
863
+ # "instFamily": "IOST-USDT",
864
+ # "instId": "IOST-USDT-SWAP",
865
+ # "instType": "SWAP",
866
+ # "uly": "IOST-USDT"
867
+ # }
868
+ #
869
+ details = self.safe_list(liquidation, 'details', [])
870
+ liquidationDetails = self.safe_dict(details, 0, {})
871
+ marketId = self.safe_string(liquidation, 'instId')
872
+ market = self.safe_market(marketId, market)
873
+ timestamp = self.safe_integer(liquidationDetails, 'ts')
874
+ return self.safe_liquidation({
875
+ 'info': liquidation,
876
+ 'symbol': self.safe_symbol(marketId, market),
877
+ 'contracts': self.safe_number(liquidationDetails, 'sz'),
878
+ 'contractSize': self.safe_number(market, 'contractSize'),
879
+ 'price': self.safe_number(liquidationDetails, 'bkPx'),
880
+ 'baseValue': None,
881
+ 'quoteValue': None,
882
+ 'timestamp': timestamp,
883
+ 'datetime': self.iso8601(timestamp),
884
+ })
323
885
 
324
886
  async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
325
887
  """
@@ -340,6 +902,17 @@ class okx(ccxt.async_support.okx):
340
902
  limit = ohlcv.getLimit(symbol, limit)
341
903
  return self.filter_by_since_limit(ohlcv, since, limit, 0, True)
342
904
 
905
+ async def un_watch_ohlcv(self, symbol: str, timeframe='1m', params={}) -> Any:
906
+ """
907
+ watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
908
+ :param str symbol: unified symbol of the market to fetch OHLCV data for
909
+ :param str timeframe: the length of time each candle represents
910
+ :param dict [params]: extra parameters specific to the exchange API endpoint
911
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
912
+ """
913
+ await self.load_markets()
914
+ return await self.un_watch_ohlcv_for_symbols([[symbol, timeframe]], params)
915
+
343
916
  async def watch_ohlcv_for_symbols(self, symbolsAndTimeframes: List[List[str]], since: Int = None, limit: Int = None, params={}):
344
917
  """
345
918
  watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
@@ -362,13 +935,13 @@ class okx(ccxt.async_support.okx):
362
935
  marketId = self.market_id(sym)
363
936
  interval = self.safe_string(self.timeframes, tf, tf)
364
937
  channel = 'candle' + interval
365
- topic = {
938
+ topic: dict = {
366
939
  'channel': channel,
367
940
  'instId': marketId,
368
941
  }
369
942
  topics.append(topic)
370
943
  messageHashes.append('multi:' + channel + ':' + sym)
371
- request = {
944
+ request: dict = {
372
945
  'op': 'subscribe',
373
946
  'args': topics,
374
947
  }
@@ -379,6 +952,39 @@ class okx(ccxt.async_support.okx):
379
952
  filtered = self.filter_by_since_limit(candles, since, limit, 0, True)
380
953
  return self.create_ohlcv_object(symbol, timeframe, filtered)
381
954
 
955
+ async def un_watch_ohlcv_for_symbols(self, symbolsAndTimeframes: List[List[str]], params={}) -> Any:
956
+ """
957
+ unWatches historical candlestick data containing the open, high, low, and close price, and the volume of a market
958
+ :param str[][] symbolsAndTimeframes: array of arrays containing unified symbols and timeframes to fetch OHLCV data for, example [['BTC/USDT', '1m'], ['LTC/USDT', '5m']]
959
+ :param dict [params]: extra parameters specific to the exchange API endpoint
960
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
961
+ """
962
+ symbolsLength = len(symbolsAndTimeframes)
963
+ if symbolsLength == 0 or not isinstance(symbolsAndTimeframes[0], list):
964
+ raise ArgumentsRequired(self.id + " watchOHLCVForSymbols() requires a an array of symbols and timeframes, like [['BTC/USDT', '1m'], ['LTC/USDT', '5m']]")
965
+ await self.load_markets()
966
+ topics = []
967
+ messageHashes = []
968
+ for i in range(0, len(symbolsAndTimeframes)):
969
+ symbolAndTimeframe = symbolsAndTimeframes[i]
970
+ sym = symbolAndTimeframe[0]
971
+ tf = symbolAndTimeframe[1]
972
+ marketId = self.market_id(sym)
973
+ interval = self.safe_string(self.timeframes, tf, tf)
974
+ channel = 'candle' + interval
975
+ topic: dict = {
976
+ 'channel': channel,
977
+ 'instId': marketId,
978
+ }
979
+ topics.append(topic)
980
+ messageHashes.append('unsubscribe:multi:' + channel + ':' + sym)
981
+ request: dict = {
982
+ 'op': 'unsubscribe',
983
+ 'args': topics,
984
+ }
985
+ url = self.get_url('candle', 'public')
986
+ return await self.watch_multiple(url, messageHashes, request, messageHashes)
987
+
382
988
  def handle_ohlcv(self, client: Client, message):
383
989
  #
384
990
  # {
@@ -424,10 +1030,14 @@ class okx(ccxt.async_support.okx):
424
1030
 
425
1031
  async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
426
1032
  """
1033
+
1034
+ https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-order-book-channel
1035
+
427
1036
  watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
428
1037
  :param str symbol: unified symbol of the market to fetch the order book for
429
1038
  :param int [limit]: the maximum amount of order book entries to return
430
1039
  :param dict [params]: extra parameters specific to the exchange API endpoint
1040
+ :param str [params.depth]: okx order book depth, can be books, books5, books-l2-tbt, books50-l2-tbt, bbo-tbt
431
1041
  :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
432
1042
  """
433
1043
  #
@@ -457,17 +1067,32 @@ class okx(ccxt.async_support.okx):
457
1067
 
458
1068
  async def watch_order_book_for_symbols(self, symbols: List[str], limit: Int = None, params={}) -> OrderBook:
459
1069
  """
1070
+
1071
+ https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-order-book-channel
1072
+
460
1073
  watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
461
1074
  :param str[] symbols: unified array of symbols
462
- :param int [limit]: the maximum amount of order book entries to return
1075
+ :param int [limit]: 1,5, 400, 50(l2-tbt, vip4+) or 40000(vip5+) the maximum amount of order book entries to return
463
1076
  :param dict [params]: extra parameters specific to the exchange API endpoint
1077
+ :param str [params.depth]: okx order book depth, can be books, books5, books-l2-tbt, books50-l2-tbt, bbo-tbt
464
1078
  :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
465
1079
  """
466
1080
  await self.load_markets()
467
1081
  symbols = self.market_symbols(symbols)
468
- options = self.safe_value(self.options, 'watchOrderBook', {})
469
- depth = self.safe_string(options, 'depth', 'books')
1082
+ depth = None
1083
+ depth, params = self.handle_option_and_params(params, 'watchOrderBook', 'depth', 'books')
1084
+ if limit is not None:
1085
+ if limit == 1:
1086
+ depth = 'bbo-tbt'
1087
+ elif limit > 1 and limit <= 5:
1088
+ depth = 'books5'
1089
+ elif limit == 50:
1090
+ depth = 'books50-l2-tbt' # Make sure you have VIP4 and above
1091
+ elif limit == 400:
1092
+ depth = 'books'
470
1093
  if (depth == 'books-l2-tbt') or (depth == 'books50-l2-tbt'):
1094
+ if not self.check_required_credentials(False):
1095
+ raise AuthenticationError(self.id + ' watchOrderBook/watchOrderBookForSymbols requires authentication for self depth. Add credentials or change the depth option to books or books5')
471
1096
  await self.authenticate({'access': 'public'})
472
1097
  topics = []
473
1098
  messageHashes = []
@@ -475,12 +1100,12 @@ class okx(ccxt.async_support.okx):
475
1100
  symbol = symbols[i]
476
1101
  messageHashes.append(depth + ':' + symbol)
477
1102
  marketId = self.market_id(symbol)
478
- topic = {
1103
+ topic: dict = {
479
1104
  'channel': depth,
480
1105
  'instId': marketId,
481
1106
  }
482
1107
  topics.append(topic)
483
- request = {
1108
+ request: dict = {
484
1109
  'op': 'subscribe',
485
1110
  'args': topics,
486
1111
  }
@@ -488,6 +1113,66 @@ class okx(ccxt.async_support.okx):
488
1113
  orderbook = await self.watch_multiple(url, messageHashes, request, messageHashes)
489
1114
  return orderbook.limit()
490
1115
 
1116
+ async def un_watch_order_book_for_symbols(self, symbols: List[str], params={}) -> Any:
1117
+ """
1118
+
1119
+ https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-order-book-channel
1120
+
1121
+ unWatches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
1122
+ :param str[] symbols: unified array of symbols
1123
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1124
+ :param int [params.limit]: the maximum amount of order book entries to return
1125
+ :param str [params.depth]: okx order book depth, can be books, books5, books-l2-tbt, books50-l2-tbt, bbo-tbt
1126
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
1127
+ """
1128
+ await self.load_markets()
1129
+ symbols = self.market_symbols(symbols, None, False)
1130
+ depth = None
1131
+ depth, params = self.handle_option_and_params(params, 'watchOrderBook', 'depth', 'books')
1132
+ limit = self.safe_integer(params, 'limit')
1133
+ if limit is not None:
1134
+ if limit == 1:
1135
+ depth = 'bbo-tbt'
1136
+ elif limit > 1 and limit <= 5:
1137
+ depth = 'books5'
1138
+ elif limit == 50:
1139
+ depth = 'books50-l2-tbt' # Make sure you have VIP4 and above
1140
+ elif limit == 400:
1141
+ depth = 'books'
1142
+ topics = []
1143
+ subMessageHashes = []
1144
+ messageHashes = []
1145
+ for i in range(0, len(symbols)):
1146
+ symbol = symbols[i]
1147
+ subMessageHashes.append(depth + ':' + symbol)
1148
+ messageHashes.append('unsubscribe:orderbook:' + symbol)
1149
+ marketId = self.market_id(symbol)
1150
+ topic: dict = {
1151
+ 'channel': depth,
1152
+ 'instId': marketId,
1153
+ }
1154
+ topics.append(topic)
1155
+ request: dict = {
1156
+ 'op': 'unsubscribe',
1157
+ 'args': topics,
1158
+ }
1159
+ url = self.get_url(depth, 'public')
1160
+ return await self.watch_multiple(url, messageHashes, request, messageHashes)
1161
+
1162
+ async def un_watch_order_book(self, symbol: str, params={}) -> Any:
1163
+ """
1164
+
1165
+ https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-order-book-channel
1166
+
1167
+ unWatches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
1168
+ :param str symbol: unified array of symbols
1169
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1170
+ :param int [params.limit]: the maximum amount of order book entries to return
1171
+ :param str [params.depth]: okx order book depth, can be books, books5, books-l2-tbt, books50-l2-tbt, bbo-tbt
1172
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
1173
+ """
1174
+ return await self.un_watch_order_book_for_symbols([symbol], params)
1175
+
491
1176
  def handle_delta(self, bookside, delta):
492
1177
  #
493
1178
  # [
@@ -505,7 +1190,7 @@ class okx(ccxt.async_support.okx):
505
1190
  for i in range(0, len(deltas)):
506
1191
  self.handle_delta(bookside, deltas[i])
507
1192
 
508
- def handle_order_book_message(self, client: Client, message, orderbook, messageHash):
1193
+ def handle_order_book_message(self, client: Client, message, orderbook, messageHash, market=None):
509
1194
  #
510
1195
  # {
511
1196
  # "asks": [
@@ -520,6 +1205,9 @@ class okx(ccxt.async_support.okx):
520
1205
  # ],
521
1206
  # "instId": "BTC-USDT",
522
1207
  # "ts": "1626537446491"
1208
+ # "checksum": -855196043,
1209
+ # "prevSeqId": 123456,
1210
+ # "seqId": 123457
523
1211
  # }
524
1212
  #
525
1213
  asks = self.safe_value(message, 'asks', [])
@@ -528,8 +1216,13 @@ class okx(ccxt.async_support.okx):
528
1216
  storedBids = orderbook['bids']
529
1217
  self.handle_deltas(storedAsks, asks)
530
1218
  self.handle_deltas(storedBids, bids)
531
- checksum = self.safe_bool(self.options, 'checksum', True)
1219
+ marketId = self.safe_string(message, 'instId')
1220
+ symbol = self.safe_symbol(marketId, market)
1221
+ checksum = self.handle_option('watchOrderBook', 'checksum', True)
1222
+ seqId = self.safe_integer(message, 'seqId')
532
1223
  if checksum:
1224
+ prevSeqId = self.safe_integer(message, 'prevSeqId')
1225
+ nonce = orderbook['nonce']
533
1226
  asksLength = len(storedAsks)
534
1227
  bidsLength = len(storedBids)
535
1228
  payloadArray = []
@@ -543,10 +1236,17 @@ class okx(ccxt.async_support.okx):
543
1236
  payload = ':'.join(payloadArray)
544
1237
  responseChecksum = self.safe_integer(message, 'checksum')
545
1238
  localChecksum = self.crc32(payload, True)
1239
+ error = None
1240
+ if prevSeqId != -1 and nonce != prevSeqId:
1241
+ error = InvalidNonce(self.id + ' watchOrderBook received invalid nonce')
546
1242
  if responseChecksum != localChecksum:
547
- error = InvalidNonce(self.id + ' invalid checksum')
1243
+ error = ChecksumError(self.id + ' ' + self.orderbook_checksum_message(symbol))
1244
+ if error is not None:
1245
+ del client.subscriptions[messageHash]
1246
+ del self.orderbooks[symbol]
548
1247
  client.reject(error, messageHash)
549
1248
  timestamp = self.safe_integer(message, 'ts')
1249
+ orderbook['nonce'] = seqId
550
1250
  orderbook['timestamp'] = timestamp
551
1251
  orderbook['datetime'] = self.iso8601(timestamp)
552
1252
  return orderbook
@@ -637,14 +1337,14 @@ class okx(ccxt.async_support.okx):
637
1337
  # ]
638
1338
  # }
639
1339
  #
640
- arg = self.safe_value(message, 'arg', {})
1340
+ arg = self.safe_dict(message, 'arg', {})
641
1341
  channel = self.safe_string(arg, 'channel')
642
1342
  action = self.safe_string(message, 'action')
643
- data = self.safe_value(message, 'data', [])
1343
+ data = self.safe_list(message, 'data', [])
644
1344
  marketId = self.safe_string(arg, 'instId')
645
1345
  market = self.safe_market(marketId)
646
1346
  symbol = market['symbol']
647
- depths = {
1347
+ depths: dict = {
648
1348
  'bbo-tbt': 1,
649
1349
  'books': 400,
650
1350
  'books5': 5,
@@ -666,13 +1366,12 @@ class okx(ccxt.async_support.okx):
666
1366
  orderbook = self.orderbooks[symbol]
667
1367
  for i in range(0, len(data)):
668
1368
  update = data[i]
669
- self.handle_order_book_message(client, update, orderbook, messageHash)
1369
+ self.handle_order_book_message(client, update, orderbook, messageHash, market)
670
1370
  client.resolve(orderbook, messageHash)
671
1371
  elif (channel == 'books5') or (channel == 'bbo-tbt'):
672
- orderbook = self.safe_value(self.orderbooks, symbol)
673
- if orderbook is None:
674
- orderbook = self.order_book({}, limit)
675
- self.orderbooks[symbol] = orderbook
1372
+ if not (symbol in self.orderbooks):
1373
+ self.orderbooks[symbol] = self.order_book({}, limit)
1374
+ orderbook = self.orderbooks[symbol]
676
1375
  for i in range(0, len(data)):
677
1376
  update = data[i]
678
1377
  timestamp = self.safe_integer(update, 'ts')
@@ -697,7 +1396,7 @@ class okx(ccxt.async_support.okx):
697
1396
  auth = timestamp + method + path
698
1397
  signature = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha256, 'base64')
699
1398
  operation = 'login'
700
- request = {
1399
+ request: dict = {
701
1400
  'op': operation,
702
1401
  'args': [
703
1402
  {
@@ -708,8 +1407,10 @@ class okx(ccxt.async_support.okx):
708
1407
  },
709
1408
  ],
710
1409
  }
711
- message = self.extend(request, params)
712
- self.watch(url, messageHash, message, messageHash)
1410
+ # Only add params['access'] to prevent sending custom parameters, such.
1411
+ if 'access' in params:
1412
+ request['access'] = params['access']
1413
+ self.watch(url, messageHash, request, messageHash)
713
1414
  return await future
714
1415
 
715
1416
  async def watch_balance(self, params={}) -> Balances:
@@ -722,6 +1423,9 @@ class okx(ccxt.async_support.okx):
722
1423
  await self.authenticate()
723
1424
  return await self.subscribe('private', 'account', 'account', None, params)
724
1425
 
1426
+ def handle_balance_and_position(self, client: Client, message):
1427
+ self.handle_my_liquidation(client, message)
1428
+
725
1429
  def handle_balance(self, client: Client, message):
726
1430
  #
727
1431
  # {
@@ -802,24 +1506,26 @@ class okx(ccxt.async_support.okx):
802
1506
  async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
803
1507
  """
804
1508
  watches information on multiple trades made by the user
805
- :see: https://www.okx.com/docs-v5/en/#order-book-trading-trade-ws-order-channel
1509
+
1510
+ https://www.okx.com/docs-v5/en/#order-book-trading-trade-ws-order-channel
1511
+
806
1512
  :param str [symbol]: unified market symbol of the market trades were made in
807
1513
  :param int [since]: the earliest time in ms to fetch trades for
808
1514
  :param int [limit]: the maximum number of trade structures to retrieve
809
1515
  :param dict [params]: extra parameters specific to the exchange API endpoint
810
- :param bool [params.stop]: True if fetching trigger or conditional trades
1516
+ :param bool [params.trigger]: True if fetching trigger or conditional trades
811
1517
  :param str [params.type]: 'spot', 'swap', 'future', 'option', 'ANY', 'SPOT', 'MARGIN', 'SWAP', 'FUTURES' or 'OPTION'
812
1518
  :param str [params.marginMode]: 'cross' or 'isolated', for automatically setting the type to spot margin
813
- :returns dict[]: a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure
1519
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
814
1520
  """
815
1521
  # By default, receive order updates from any instrument type
816
1522
  type = None
817
1523
  type, params = self.handle_option_and_params(params, 'watchMyTrades', 'type', 'ANY')
818
- isStop = self.safe_bool(params, 'stop', False)
819
- params = self.omit(params, ['stop'])
1524
+ isTrigger = self.safe_bool_2(params, 'trigger', 'stop', False)
1525
+ params = self.omit(params, ['trigger', 'stop'])
820
1526
  await self.load_markets()
821
- await self.authenticate({'access': 'business' if isStop else 'private'})
822
- channel = 'orders-algo' if isStop else 'orders'
1527
+ await self.authenticate({'access': 'business' if isTrigger else 'private'})
1528
+ channel = 'orders-algo' if isTrigger else 'orders'
823
1529
  messageHash = channel + '::myTrades'
824
1530
  market = None
825
1531
  if symbol is not None:
@@ -835,7 +1541,7 @@ class okx(ccxt.async_support.okx):
835
1541
  if uppercaseType == 'SPOT':
836
1542
  if marginMode is not None:
837
1543
  uppercaseType = 'MARGIN'
838
- request = {
1544
+ request: dict = {
839
1545
  'instType': uppercaseType,
840
1546
  }
841
1547
  orders = await self.subscribe('private', messageHash, channel, None, self.extend(request, params))
@@ -845,27 +1551,31 @@ class okx(ccxt.async_support.okx):
845
1551
 
846
1552
  async def watch_positions(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]:
847
1553
  """
848
- :see: https://www.okx.com/docs-v5/en/#trading-account-websocket-positions-channel
1554
+
1555
+ https://www.okx.com/docs-v5/en/#trading-account-websocket-positions-channel
1556
+
849
1557
  watch all open positions
850
1558
  :param str[]|None symbols: list of unified market symbols
1559
+ @param since
1560
+ @param limit
851
1561
  :param dict params: extra parameters specific to the exchange API endpoint
852
1562
  :returns dict[]: a list of `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
853
1563
  """
854
1564
  await self.load_markets()
855
1565
  await self.authenticate(params)
856
1566
  symbols = self.market_symbols(symbols)
857
- request = {
1567
+ request: dict = {
858
1568
  'instType': 'ANY',
859
1569
  }
860
1570
  channel = 'positions'
861
1571
  newPositions = None
862
1572
  if symbols is None:
863
- arg = {
1573
+ arg: dict = {
864
1574
  'channel': 'positions',
865
1575
  'instType': 'ANY',
866
1576
  }
867
- args = [arg]
868
- nonSymbolRequest = {
1577
+ args = [self.extend(arg, params)]
1578
+ nonSymbolRequest: dict = {
869
1579
  'op': 'subscribe',
870
1580
  'args': args,
871
1581
  }
@@ -945,6 +1655,9 @@ class okx(ccxt.async_support.okx):
945
1655
  # }
946
1656
  #
947
1657
  arg = self.safe_value(message, 'arg', {})
1658
+ marketId = self.safe_string(arg, 'instId')
1659
+ market = self.safe_market(marketId, None, '-')
1660
+ symbol = market['symbol']
948
1661
  channel = self.safe_string(arg, 'channel', '')
949
1662
  data = self.safe_value(message, 'data', [])
950
1663
  if self.positions is None:
@@ -954,28 +1667,30 @@ class okx(ccxt.async_support.okx):
954
1667
  for i in range(0, len(data)):
955
1668
  rawPosition = data[i]
956
1669
  position = self.parse_position(rawPosition)
1670
+ if position['contracts'] == 0:
1671
+ position['side'] = 'long'
1672
+ shortPosition = self.clone(position)
1673
+ shortPosition['side'] = 'short'
1674
+ cache.append(shortPosition)
1675
+ newPositions.append(shortPosition)
957
1676
  newPositions.append(position)
958
1677
  cache.append(position)
959
- messageHashes = self.find_message_hashes(client, channel + '::')
960
- for i in range(0, len(messageHashes)):
961
- messageHash = messageHashes[i]
962
- parts = messageHash.split('::')
963
- symbolsString = parts[1]
964
- symbols = symbolsString.split(',')
965
- positions = self.filter_by_array(newPositions, 'symbol', symbols, False)
966
- if not self.is_empty(positions):
967
- client.resolve(positions, messageHash)
968
- client.resolve(newPositions, channel)
1678
+ messageHash = channel
1679
+ if symbol is not None:
1680
+ messageHash = channel + '::' + symbol
1681
+ client.resolve(newPositions, messageHash)
969
1682
 
970
1683
  async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
971
1684
  """
972
1685
  watches information on multiple orders made by the user
973
- :see: https://www.okx.com/docs-v5/en/#order-book-trading-trade-ws-order-channel
1686
+
1687
+ https://www.okx.com/docs-v5/en/#order-book-trading-trade-ws-order-channel
1688
+
974
1689
  :param str [symbol]: unified market symbol of the market the orders were made in
975
1690
  :param int [since]: the earliest time in ms to fetch orders for
976
1691
  :param int [limit]: the maximum number of order structures to retrieve
977
1692
  :param dict [params]: extra parameters specific to the exchange API endpoint
978
- :param bool [params.stop]: True if fetching trigger or conditional orders
1693
+ :param bool [params.trigger]: True if fetching trigger or conditional orders
979
1694
  :param str [params.type]: 'spot', 'swap', 'future', 'option', 'ANY', 'SPOT', 'MARGIN', 'SWAP', 'FUTURES' or 'OPTION'
980
1695
  :param str [params.marginMode]: 'cross' or 'isolated', for automatically setting the type to spot margin
981
1696
  :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
@@ -983,10 +1698,10 @@ class okx(ccxt.async_support.okx):
983
1698
  type = None
984
1699
  # By default, receive order updates from any instrument type
985
1700
  type, params = self.handle_option_and_params(params, 'watchOrders', 'type', 'ANY')
986
- isStop = self.safe_value_2(params, 'stop', 'trigger', False)
1701
+ isTrigger = self.safe_value_2(params, 'stop', 'trigger', False)
987
1702
  params = self.omit(params, ['stop', 'trigger'])
988
1703
  await self.load_markets()
989
- await self.authenticate({'access': 'business' if isStop else 'private'})
1704
+ await self.authenticate({'access': 'business' if isTrigger else 'private'})
990
1705
  market = None
991
1706
  if symbol is not None:
992
1707
  market = self.market(symbol)
@@ -1000,10 +1715,10 @@ class okx(ccxt.async_support.okx):
1000
1715
  if uppercaseType == 'SPOT':
1001
1716
  if marginMode is not None:
1002
1717
  uppercaseType = 'MARGIN'
1003
- request = {
1718
+ request: dict = {
1004
1719
  'instType': uppercaseType,
1005
1720
  }
1006
- channel = 'orders-algo' if isStop else 'orders'
1721
+ channel = 'orders-algo' if isTrigger else 'orders'
1007
1722
  orders = await self.subscribe('private', channel, channel, symbol, self.extend(request, params))
1008
1723
  if self.newUpdates:
1009
1724
  limit = orders.getLimit(symbol, limit)
@@ -1161,7 +1876,7 @@ class okx(ccxt.async_support.okx):
1161
1876
  limit = self.safe_integer(self.options, 'tradesLimit', 1000)
1162
1877
  self.myTrades = ArrayCacheBySymbolById(limit)
1163
1878
  myTrades = self.myTrades
1164
- symbols = {}
1879
+ symbols: dict = {}
1165
1880
  for i in range(0, len(filteredOrders)):
1166
1881
  rawTrade = filteredOrders[i]
1167
1882
  trade = self.order_to_trade(rawTrade)
@@ -1173,17 +1888,25 @@ class okx(ccxt.async_support.okx):
1173
1888
  tradeSymbols = list(symbols.keys())
1174
1889
  for i in range(0, len(tradeSymbols)):
1175
1890
  symbolMessageHash = messageHash + '::' + tradeSymbols[i]
1176
- client.resolve(self.orders, symbolMessageHash)
1891
+ client.resolve(self.myTrades, symbolMessageHash)
1892
+
1893
+ def request_id(self):
1894
+ ts = str(self.milliseconds())
1895
+ randomNumber = self.rand_number(4)
1896
+ randomPart = str(randomNumber)
1897
+ return ts + randomPart
1177
1898
 
1178
1899
  async def create_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}) -> Order:
1179
1900
  """
1180
- :see: https://www.okx.com/docs-v5/en/#websocket-api-trade-place-order
1901
+
1902
+ https://www.okx.com/docs-v5/en/#websocket-api-trade-place-order
1903
+
1181
1904
  create a trade order
1182
1905
  :param str symbol: unified symbol of the market to create an order in
1183
1906
  :param str type: 'market' or 'limit'
1184
1907
  :param str side: 'buy' or 'sell'
1185
1908
  :param float amount: how much of currency you want to trade in units of base currency
1186
- :param float|None [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
1909
+ :param float|None [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
1187
1910
  :param dict [params]: extra parameters specific to the exchange API endpoint
1188
1911
  :param boolean params['test']: test order, default False
1189
1912
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
@@ -1191,7 +1914,7 @@ class okx(ccxt.async_support.okx):
1191
1914
  await self.load_markets()
1192
1915
  await self.authenticate()
1193
1916
  url = self.get_url('private', 'private')
1194
- messageHash = str(self.nonce())
1917
+ messageHash = self.request_id()
1195
1918
  op = None
1196
1919
  op, params = self.handle_option_and_params(params, 'createOrderWs', 'op', 'batch-orders')
1197
1920
  args = self.create_order_request(symbol, type, side, amount, price, params)
@@ -1200,7 +1923,7 @@ class okx(ccxt.async_support.okx):
1200
1923
  raise BadRequest(self.id + ' createOrderWs() does not support algo trading. self.options["createOrderWs"]["op"] must be either order or batch-order')
1201
1924
  if (op != 'order') and (op != 'batch-orders'):
1202
1925
  raise BadRequest(self.id + ' createOrderWs() does not support algo trading. self.options["createOrderWs"]["op"] must be either order or privatePostTradeOrder or privatePostTradeOrderAlgo')
1203
- request = {
1926
+ request: dict = {
1204
1927
  'id': messageHash,
1205
1928
  'op': op,
1206
1929
  'args': [args],
@@ -1234,32 +1957,35 @@ class okx(ccxt.async_support.okx):
1234
1957
  if self.is_empty(args):
1235
1958
  method = self.safe_string(message, 'op')
1236
1959
  stringMsg = self.json(message)
1237
- self.handle_errors(None, None, client.url, method, None, stringMsg, stringMsg, None, None)
1960
+ self.handle_errors(None, None, client.url, method, None, stringMsg, message, None, None)
1238
1961
  orders = self.parse_orders(args, None, None, None)
1239
- client.resolve(orders, messageHash)
1962
+ first = self.safe_dict(orders, 0, {})
1963
+ client.resolve(first, messageHash)
1240
1964
 
1241
- async def edit_order_ws(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}) -> Order:
1965
+ async def edit_order_ws(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}) -> Order:
1242
1966
  """
1243
1967
  edit a trade order
1244
- :see: https://www.okx.com/docs-v5/en/#order-book-trading-trade-ws-amend-order
1245
- :see: https://www.okx.com/docs-v5/en/#order-book-trading-trade-ws-amend-multiple-orders
1968
+
1969
+ https://www.okx.com/docs-v5/en/#order-book-trading-trade-ws-amend-order
1970
+ https://www.okx.com/docs-v5/en/#order-book-trading-trade-ws-amend-multiple-orders
1971
+
1246
1972
  :param str id: order id
1247
1973
  :param str symbol: unified symbol of the market to create an order in
1248
1974
  :param str type: 'market' or 'limit'
1249
1975
  :param str side: 'buy' or 'sell'
1250
1976
  :param float amount: how much of the currency you want to trade in units of the base currency
1251
- :param float|None [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
1977
+ :param float|None [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
1252
1978
  :param dict [params]: extra parameters specific to the exchange API endpoint
1253
1979
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1254
1980
  """
1255
1981
  await self.load_markets()
1256
1982
  await self.authenticate()
1257
1983
  url = self.get_url('private', 'private')
1258
- messageHash = str(self.nonce())
1984
+ messageHash = self.request_id()
1259
1985
  op = None
1260
1986
  op, params = self.handle_option_and_params(params, 'editOrderWs', 'op', 'amend-order')
1261
1987
  args = self.edit_order_request(id, symbol, type, side, amount, price, params)
1262
- request = {
1988
+ request: dict = {
1263
1989
  'id': messageHash,
1264
1990
  'op': op,
1265
1991
  'args': [args],
@@ -1268,7 +1994,9 @@ class okx(ccxt.async_support.okx):
1268
1994
 
1269
1995
  async def cancel_order_ws(self, id: str, symbol: Str = None, params={}) -> Order:
1270
1996
  """
1271
- :see: https://okx-docs.github.io/apidocs/websocket_api/en/#cancel-order-trade
1997
+
1998
+ https://okx-docs.github.io/apidocs/websocket_api/en/#cancel-order-trade
1999
+
1272
2000
  cancel multiple orders
1273
2001
  :param str id: order id
1274
2002
  :param str symbol: unified market symbol, default is None
@@ -1281,17 +2009,17 @@ class okx(ccxt.async_support.okx):
1281
2009
  await self.load_markets()
1282
2010
  await self.authenticate()
1283
2011
  url = self.get_url('private', 'private')
1284
- messageHash = str(self.nonce())
2012
+ messageHash = self.request_id()
1285
2013
  clientOrderId = self.safe_string_2(params, 'clOrdId', 'clientOrderId')
1286
2014
  params = self.omit(params, ['clientOrderId', 'clOrdId'])
1287
- arg = {
2015
+ arg: dict = {
1288
2016
  'instId': self.market_id(symbol),
1289
2017
  }
1290
2018
  if clientOrderId is not None:
1291
2019
  arg['clOrdId'] = clientOrderId
1292
2020
  else:
1293
2021
  arg['ordId'] = id
1294
- request = {
2022
+ request: dict = {
1295
2023
  'id': messageHash,
1296
2024
  'op': 'cancel-order',
1297
2025
  'args': [self.extend(arg, params)],
@@ -1300,14 +2028,16 @@ class okx(ccxt.async_support.okx):
1300
2028
 
1301
2029
  async def cancel_orders_ws(self, ids: List[str], symbol: Str = None, params={}):
1302
2030
  """
1303
- :see: https://www.okx.com/docs-v5/en/#order-book-trading-trade-ws-mass-cancel-order
2031
+
2032
+ https://www.okx.com/docs-v5/en/#order-book-trading-trade-ws-mass-cancel-order
2033
+
1304
2034
  cancel multiple orders
1305
2035
  :param str[] ids: order ids
1306
2036
  :param str symbol: unified market symbol, default is None
1307
2037
  :param dict [params]: extra parameters specific to the exchange API endpoint
1308
2038
  :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1309
2039
  """
1310
- idsLength = len(ids)
2040
+ idsLength: number = len(ids)
1311
2041
  if idsLength > 20:
1312
2042
  raise BadRequest(self.id + ' cancelOrdersWs() accepts up to 20 ids at a time')
1313
2043
  if symbol is None:
@@ -1315,15 +2045,15 @@ class okx(ccxt.async_support.okx):
1315
2045
  await self.load_markets()
1316
2046
  await self.authenticate()
1317
2047
  url = self.get_url('private', 'private')
1318
- messageHash = str(self.nonce())
2048
+ messageHash = self.request_id()
1319
2049
  args = []
1320
2050
  for i in range(0, idsLength):
1321
- arg = {
2051
+ arg: dict = {
1322
2052
  'instId': self.market_id(symbol),
1323
2053
  'ordId': ids[i],
1324
2054
  }
1325
2055
  args.append(arg)
1326
- request = {
2056
+ request: dict = {
1327
2057
  'id': messageHash,
1328
2058
  'op': 'batch-cancel-orders',
1329
2059
  'args': args,
@@ -1332,7 +2062,9 @@ class okx(ccxt.async_support.okx):
1332
2062
 
1333
2063
  async def cancel_all_orders_ws(self, symbol: Str = None, params={}):
1334
2064
  """
1335
- :see: https://docs.okx.com/websockets/#message-cancelAll
2065
+
2066
+ https://docs.okx.com/websockets/#message-cancelAll
2067
+
1336
2068
  cancel all open orders of a type. Only applicable to Option in Portfolio Margin mode, and MMP privilege is required.
1337
2069
  :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
1338
2070
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -1346,8 +2078,8 @@ class okx(ccxt.async_support.okx):
1346
2078
  if market['type'] != 'option':
1347
2079
  raise BadRequest(self.id + 'cancelAllOrdersWs is only applicable to Option in Portfolio Margin mode, and MMP privilege is required.')
1348
2080
  url = self.get_url('private', 'private')
1349
- messageHash = str(self.nonce())
1350
- request = {
2081
+ messageHash = self.request_id()
2082
+ request: dict = {
1351
2083
  'id': messageHash,
1352
2084
  'op': 'mass-cancel',
1353
2085
  'args': [self.extend({
@@ -1390,9 +2122,9 @@ class okx(ccxt.async_support.okx):
1390
2122
  future = self.safe_value(client.futures, 'authenticated')
1391
2123
  future.resolve(True)
1392
2124
 
1393
- def ping(self, client):
1394
- # okex does not support built-in ws protocol-level ping-pong
1395
- # instead it requires custom text-based ping-pong
2125
+ def ping(self, client: Client):
2126
+ # OKX does not support the built-in WebSocket protocol-level ping-pong.
2127
+ # Instead, it requires a custom text-based ping-pong mechanism.
1396
2128
  return 'ping'
1397
2129
 
1398
2130
  def handle_pong(self, client: Client, message):
@@ -1403,24 +2135,44 @@ class okx(ccxt.async_support.okx):
1403
2135
  #
1404
2136
  # {event: 'error', msg: "Illegal request: {"op":"subscribe","args":["spot/ticker:BTC-USDT"]}", code: "60012"}
1405
2137
  # {event: 'error", msg: "channel:ticker,instId:BTC-USDT doesn"t exist", code: "60018"}
2138
+ # {"event":"error","msg":"Illegal request: {\\"id\\":\\"17321173472466905\\",\\"op\\":\\"amend-order\\",\\"args\\":[{\\"instId\\":\\"ETH-USDC\\",\\"ordId\\":\\"2000345622407479296\\",\\"newSz\\":\\"0.050857\\",\\"newPx\\":\\"2949.4\\",\\"postOnly\\":true}],\\"postOnly\\":true}","code":"60012","connId":"0808af6c"}
1406
2139
  #
1407
- errorCode = self.safe_integer(message, 'code')
2140
+ errorCode = self.safe_string(message, 'code')
1408
2141
  try:
1409
- if errorCode:
2142
+ if errorCode and errorCode != '0':
1410
2143
  feedback = self.id + ' ' + self.json(message)
1411
- self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
2144
+ if errorCode != '1':
2145
+ self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
1412
2146
  messageString = self.safe_value(message, 'msg')
1413
2147
  if messageString is not None:
1414
2148
  self.throw_broadly_matched_exception(self.exceptions['broad'], messageString, feedback)
2149
+ else:
2150
+ data = self.safe_list(message, 'data', [])
2151
+ for i in range(0, len(data)):
2152
+ d = data[i]
2153
+ errorCode = self.safe_string(d, 'sCode')
2154
+ if errorCode is not None:
2155
+ self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
2156
+ messageString = self.safe_value(message, 'sMsg')
2157
+ if messageString is not None:
2158
+ self.throw_broadly_matched_exception(self.exceptions['broad'], messageString, feedback)
2159
+ raise ExchangeError(feedback)
1415
2160
  except Exception as e:
1416
- if isinstance(e, AuthenticationError):
1417
- messageHash = 'authenticated'
1418
- client.reject(e, messageHash)
1419
- if messageHash in client.subscriptions:
1420
- del client.subscriptions[messageHash]
2161
+ # if the message contains an id, it means it is a response to a request
2162
+ # so we only reject that promise, instead of deleting all futures, destroying the authentication future
2163
+ id = self.safe_string(message, 'id')
2164
+ if id is None:
2165
+ # try to parse it from the stringified json inside msg
2166
+ msg = self.safe_string(message, 'msg')
2167
+ if msg is not None and msg.startswith('Illegal request: {'):
2168
+ stringifiedJson = msg.replace('Illegal request: ', '')
2169
+ parsedJson = self.parse_json(stringifiedJson)
2170
+ id = self.safe_string(parsedJson, 'id')
2171
+ if id is not None:
2172
+ client.reject(e, id)
1421
2173
  return False
1422
- else:
1423
- client.reject(e)
2174
+ client.reject(e)
2175
+ return False
1424
2176
  return message
1425
2177
 
1426
2178
  def handle_message(self, client: Client, message):
@@ -1472,11 +2224,12 @@ class okx(ccxt.async_support.okx):
1472
2224
  # if table is None:
1473
2225
  event = self.safe_string_2(message, 'event', 'op')
1474
2226
  if event is not None:
1475
- methods = {
2227
+ methods: dict = {
1476
2228
  # 'info': self.handleSystemStatus,
1477
2229
  # 'book': 'handleOrderBook',
1478
2230
  'login': self.handle_authenticate,
1479
2231
  'subscribe': self.handle_subscription_status,
2232
+ 'unsubscribe': self.handle_unsubscription,
1480
2233
  'order': self.handle_place_orders,
1481
2234
  'batch-orders': self.handle_place_orders,
1482
2235
  'amend-order': self.handle_place_orders,
@@ -1490,22 +2243,26 @@ class okx(ccxt.async_support.okx):
1490
2243
  else:
1491
2244
  arg = self.safe_value(message, 'arg', {})
1492
2245
  channel = self.safe_string(arg, 'channel')
1493
- methods = {
2246
+ methods: dict = {
1494
2247
  'bbo-tbt': self.handle_order_book, # newly added channel that sends tick-by-tick Level 1 data, all API users can subscribe, public depth channel, verification not required
1495
2248
  'books': self.handle_order_book, # all API users can subscribe, public depth channel, verification not required
1496
2249
  'books5': self.handle_order_book, # all API users can subscribe, public depth channel, verification not required, data feeds will be delivered every 100ms(vs. every 200ms now)
1497
2250
  'books50-l2-tbt': self.handle_order_book, # only users who're VIP4 and above can subscribe, identity verification required before subscription
1498
2251
  'books-l2-tbt': self.handle_order_book, # only users who're VIP5 and above can subscribe, identity verification required before subscription
1499
2252
  'tickers': self.handle_ticker,
2253
+ 'mark-price': self.handle_ticker,
1500
2254
  'positions': self.handle_positions,
1501
2255
  'index-tickers': self.handle_ticker,
1502
2256
  'sprd-tickers': self.handle_ticker,
1503
2257
  'block-tickers': self.handle_ticker,
1504
2258
  'trades': self.handle_trades,
1505
2259
  'account': self.handle_balance,
2260
+ 'funding-rate': self.handle_funding_rate,
1506
2261
  # 'margin_account': self.handle_balance,
1507
2262
  'orders': self.handle_orders,
1508
2263
  'orders-algo': self.handle_orders,
2264
+ 'liquidation-orders': self.handle_liquidation,
2265
+ 'balance_and_position': self.handle_balance_and_position,
1509
2266
  }
1510
2267
  method = self.safe_value(methods, channel)
1511
2268
  if method is None:
@@ -1513,3 +2270,57 @@ class okx(ccxt.async_support.okx):
1513
2270
  self.handle_ohlcv(client, message)
1514
2271
  else:
1515
2272
  method(client, message)
2273
+
2274
+ def handle_un_subscription_trades(self, client: Client, symbol: str):
2275
+ subMessageHash = 'trades:' + symbol
2276
+ messageHash = 'unsubscribe:trades:' + symbol
2277
+ self.clean_unsubscription(client, subMessageHash, messageHash)
2278
+ if symbol in self.trades:
2279
+ del self.trades[symbol]
2280
+
2281
+ def handle_unsubscription_order_book(self, client: Client, symbol: str, channel: str):
2282
+ subMessageHash = channel + ':' + symbol
2283
+ messageHash = 'unsubscribe:orderbook:' + symbol
2284
+ self.clean_unsubscription(client, subMessageHash, messageHash)
2285
+ if symbol in self.orderbooks:
2286
+ del self.orderbooks[symbol]
2287
+
2288
+ def handle_unsubscription_ohlcv(self, client: Client, symbol: str, channel: str):
2289
+ tf = channel.replace('candle', '')
2290
+ timeframe = self.find_timeframe(tf)
2291
+ subMessageHash = 'multi:' + channel + ':' + symbol
2292
+ messageHash = 'unsubscribe:' + subMessageHash
2293
+ self.clean_unsubscription(client, subMessageHash, messageHash)
2294
+ if timeframe in self.ohlcvs[symbol]:
2295
+ del self.ohlcvs[symbol][timeframe]
2296
+
2297
+ def handle_unsubscription_ticker(self, client: Client, symbol: str, channel):
2298
+ subMessageHash = channel + '::' + symbol
2299
+ messageHash = 'unsubscribe:ticker:' + symbol
2300
+ self.clean_unsubscription(client, subMessageHash, messageHash)
2301
+ if symbol in self.tickers:
2302
+ del self.tickers[symbol]
2303
+
2304
+ def handle_unsubscription(self, client: Client, message):
2305
+ #
2306
+ # {
2307
+ # "event": "unsubscribe",
2308
+ # "arg": {
2309
+ # "channel": "tickers",
2310
+ # "instId": "LTC-USD-200327"
2311
+ # },
2312
+ # "connId": "a4d3ae55"
2313
+ # }
2314
+ # arg might be an array or list
2315
+ arg = self.safe_dict(message, 'arg', {})
2316
+ channel = self.safe_string(arg, 'channel', '')
2317
+ marketId = self.safe_string(arg, 'instId')
2318
+ symbol = self.safe_symbol(marketId)
2319
+ if channel == 'trades':
2320
+ self.handle_un_subscription_trades(client, symbol)
2321
+ elif channel.startswith('bbo') or channel.startswith('book'):
2322
+ self.handle_unsubscription_order_book(client, symbol, channel)
2323
+ elif channel.find('tickers') > -1:
2324
+ self.handle_unsubscription_ticker(client, symbol, channel)
2325
+ elif channel.startswith('candle'):
2326
+ self.handle_unsubscription_ohlcv(client, symbol, channel)