ccxt-ir 4.3.46.0.3__py2.py3-none-any.whl → 4.5.1__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (529) hide show
  1. ccxt/__init__.py +39 -35
  2. ccxt/abantether.py +8 -8
  3. ccxt/abstract/alpaca.py +4 -0
  4. ccxt/abstract/apex.py +31 -0
  5. ccxt/abstract/bigone.py +1 -1
  6. ccxt/abstract/binance.py +106 -48
  7. ccxt/abstract/binancecoinm.py +106 -48
  8. ccxt/abstract/binanceus.py +141 -83
  9. ccxt/abstract/binanceusdm.py +106 -48
  10. ccxt/abstract/bingx.py +50 -1
  11. ccxt/abstract/bitbank.py +5 -0
  12. ccxt/abstract/bitfinex.py +136 -65
  13. ccxt/abstract/bitflyer.py +1 -0
  14. ccxt/abstract/bitget.py +67 -0
  15. ccxt/abstract/bitmart.py +19 -1
  16. ccxt/abstract/bitopro.py +1 -0
  17. ccxt/abstract/bitrue.py +68 -68
  18. ccxt/abstract/bitstamp.py +1 -0
  19. ccxt/abstract/blofin.py +30 -0
  20. ccxt/abstract/btcbox.py +2 -0
  21. ccxt/abstract/bybit.py +28 -13
  22. ccxt/abstract/cex.py +28 -29
  23. ccxt/abstract/coinbaseexchange.py +1 -0
  24. ccxt/abstract/coinbaseinternational.py +1 -1
  25. ccxt/abstract/cryptocom.py +16 -0
  26. ccxt/abstract/cryptomus.py +20 -0
  27. ccxt/abstract/defx.py +69 -0
  28. ccxt/abstract/deribit.py +1 -0
  29. ccxt/abstract/derive.py +117 -0
  30. ccxt/abstract/digifinex.py +1 -0
  31. ccxt/abstract/ellipx.py +25 -0
  32. ccxt/abstract/foxbit.py +26 -0
  33. ccxt/abstract/gate.py +19 -0
  34. ccxt/abstract/gateio.py +19 -0
  35. ccxt/abstract/gemini.py +1 -0
  36. ccxt/abstract/hibachi.py +26 -0
  37. ccxt/abstract/hyperliquid.py +1 -1
  38. ccxt/abstract/independentreserve.py +6 -0
  39. ccxt/abstract/kraken.py +1 -0
  40. ccxt/abstract/krakenfutures.py +4 -0
  41. ccxt/abstract/kucoin.py +10 -0
  42. ccxt/abstract/kucoinfutures.py +18 -0
  43. ccxt/abstract/lbank.py +2 -1
  44. ccxt/abstract/luno.py +1 -0
  45. ccxt/abstract/mexc.py +2 -0
  46. ccxt/abstract/modetrade.py +119 -0
  47. ccxt/abstract/myokx.py +349 -0
  48. ccxt/abstract/oceanex.py +5 -0
  49. ccxt/abstract/okx.py +25 -0
  50. ccxt/abstract/okxus.py +349 -0
  51. ccxt/abstract/onetrading.py +0 -12
  52. ccxt/abstract/paradex.py +23 -0
  53. ccxt/abstract/phemex.py +2 -0
  54. ccxt/abstract/poloniex.py +36 -0
  55. ccxt/abstract/tradeogre.py +3 -1
  56. ccxt/abstract/upbit.py +51 -34
  57. ccxt/abstract/whitebit.py +16 -0
  58. ccxt/abstract/woo.py +64 -6
  59. ccxt/abstract/xt.py +10 -5
  60. ccxt/afratether.py +8 -8
  61. ccxt/alpaca.py +828 -51
  62. ccxt/apex.py +1875 -0
  63. ccxt/arzinja.py +7 -7
  64. ccxt/arzplus.py +9 -9
  65. ccxt/ascendex.py +501 -306
  66. ccxt/async_support/__init__.py +39 -35
  67. ccxt/async_support/abantether.py +8 -8
  68. ccxt/async_support/afratether.py +10 -10
  69. ccxt/async_support/alpaca.py +828 -51
  70. ccxt/async_support/apex.py +1875 -0
  71. ccxt/async_support/arzinja.py +10 -10
  72. ccxt/async_support/arzplus.py +12 -12
  73. ccxt/async_support/ascendex.py +502 -306
  74. ccxt/async_support/base/exchange.py +303 -89
  75. ccxt/async_support/base/ws/cache.py +9 -3
  76. ccxt/async_support/base/ws/client.py +173 -38
  77. ccxt/async_support/base/ws/future.py +25 -37
  78. ccxt/async_support/bequant.py +5 -3
  79. ccxt/async_support/bigone.py +279 -144
  80. ccxt/async_support/binance.py +2347 -1158
  81. ccxt/async_support/binancecoinm.py +9 -3
  82. ccxt/async_support/binanceus.py +17 -3
  83. ccxt/async_support/binanceusdm.py +9 -4
  84. ccxt/async_support/bingx.py +2962 -920
  85. ccxt/async_support/bit2c.py +147 -27
  86. ccxt/async_support/bitbank.py +151 -23
  87. ccxt/async_support/bitbns.py +104 -30
  88. ccxt/async_support/bitfinex.py +3291 -1113
  89. ccxt/async_support/bitflyer.py +202 -27
  90. ccxt/async_support/bitget.py +3683 -1538
  91. ccxt/async_support/bithumb.py +195 -38
  92. ccxt/async_support/bitimen.py +12 -12
  93. ccxt/async_support/bitir.py +38 -38
  94. ccxt/async_support/bitmart.py +1288 -350
  95. ccxt/async_support/bitmex.py +260 -75
  96. ccxt/async_support/bitopro.py +262 -62
  97. ccxt/async_support/bitpin.py +17 -16
  98. ccxt/async_support/bitrue.py +459 -290
  99. ccxt/async_support/bitso.py +199 -54
  100. ccxt/async_support/bitstamp.py +230 -96
  101. ccxt/async_support/bitteam.py +167 -25
  102. ccxt/async_support/{huobijp.py → bittrade.py} +158 -30
  103. ccxt/async_support/bitvavo.py +213 -49
  104. ccxt/async_support/blockchaincom.py +160 -46
  105. ccxt/async_support/blofin.py +502 -120
  106. ccxt/async_support/btcalpha.py +169 -31
  107. ccxt/async_support/btcbox.py +292 -23
  108. ccxt/async_support/btcmarkets.py +211 -58
  109. ccxt/async_support/btcturk.py +161 -38
  110. ccxt/async_support/bybit.py +1775 -1030
  111. ccxt/async_support/cex.py +1440 -1303
  112. ccxt/async_support/coinbase.py +724 -212
  113. ccxt/async_support/coinbaseadvanced.py +2 -1
  114. ccxt/async_support/coinbaseexchange.py +388 -89
  115. ccxt/async_support/coinbaseinternational.py +412 -57
  116. ccxt/async_support/coincatch.py +177 -78
  117. ccxt/async_support/coincheck.py +135 -19
  118. ccxt/async_support/coinex.py +606 -232
  119. ccxt/async_support/coinmate.py +189 -63
  120. ccxt/async_support/coinmetro.py +195 -54
  121. ccxt/async_support/coinone.py +158 -51
  122. ccxt/async_support/coinsph.py +336 -61
  123. ccxt/async_support/coinspot.py +151 -52
  124. ccxt/async_support/cryptocom.py +661 -111
  125. ccxt/async_support/cryptomus.py +1137 -0
  126. ccxt/async_support/defx.py +2071 -0
  127. ccxt/async_support/delta.py +299 -99
  128. ccxt/async_support/deribit.py +348 -126
  129. ccxt/async_support/derive.py +2572 -0
  130. ccxt/async_support/digifinex.py +430 -214
  131. ccxt/async_support/ellipx.py +2029 -0
  132. ccxt/async_support/eterex.py +10 -10
  133. ccxt/async_support/excoino.py +31 -31
  134. ccxt/async_support/exir.py +14 -14
  135. ccxt/async_support/exmo.py +344 -131
  136. ccxt/async_support/exnovin.py +10 -10
  137. ccxt/async_support/farhadexchange.py +12 -12
  138. ccxt/async_support/fmfwio.py +2 -1
  139. ccxt/async_support/foxbit.py +1935 -0
  140. ccxt/async_support/gate.py +1351 -529
  141. ccxt/async_support/gateio.py +2 -1
  142. ccxt/async_support/gemini.py +144 -39
  143. ccxt/async_support/hashkey.py +152 -109
  144. ccxt/async_support/hibachi.py +2080 -0
  145. ccxt/async_support/hitbtc.py +395 -167
  146. ccxt/async_support/hitobit.py +12 -12
  147. ccxt/async_support/hollaex.py +307 -119
  148. ccxt/async_support/htx.py +851 -383
  149. ccxt/async_support/huobi.py +2 -1
  150. ccxt/async_support/hyperliquid.py +1848 -536
  151. ccxt/async_support/independentreserve.py +288 -15
  152. ccxt/async_support/indodax.py +190 -33
  153. ccxt/async_support/jibitex.py +12 -12
  154. ccxt/async_support/kraken.py +795 -351
  155. ccxt/async_support/krakenfutures.py +214 -62
  156. ccxt/async_support/kucoin.py +715 -396
  157. ccxt/async_support/kucoinfutures.py +652 -89
  158. ccxt/async_support/latoken.py +217 -113
  159. ccxt/async_support/lbank.py +425 -97
  160. ccxt/async_support/luno.py +382 -35
  161. ccxt/async_support/mercado.py +113 -6
  162. ccxt/async_support/mexc.py +874 -437
  163. ccxt/async_support/modetrade.py +2818 -0
  164. ccxt/async_support/myokx.py +54 -0
  165. ccxt/async_support/ndax.py +221 -64
  166. ccxt/async_support/nobitex.py +32 -38
  167. ccxt/async_support/novadax.py +190 -34
  168. ccxt/async_support/oceanex.py +217 -28
  169. ccxt/async_support/okcoin.py +253 -145
  170. ccxt/async_support/okexchange.py +11 -11
  171. ccxt/async_support/okx.py +1088 -351
  172. ccxt/async_support/okxus.py +54 -0
  173. ccxt/async_support/ompfinex.py +32 -27
  174. ccxt/async_support/onetrading.py +213 -392
  175. ccxt/async_support/oxfun.py +245 -166
  176. ccxt/async_support/p2b.py +151 -29
  177. ccxt/async_support/paradex.py +562 -49
  178. ccxt/async_support/paymium.py +82 -19
  179. ccxt/async_support/phemex.py +713 -172
  180. ccxt/async_support/poloniex.py +1602 -283
  181. ccxt/async_support/probit.py +224 -95
  182. ccxt/async_support/ramzinex.py +34 -30
  183. ccxt/async_support/sarmayex.py +9 -9
  184. ccxt/async_support/sarrafex.py +13 -13
  185. ccxt/async_support/tabdeal.py +15 -14
  186. ccxt/async_support/tetherland.py +9 -9
  187. ccxt/async_support/timex.py +210 -51
  188. ccxt/async_support/tokocrypto.py +167 -47
  189. ccxt/async_support/tradeogre.py +266 -31
  190. ccxt/async_support/twox.py +9 -9
  191. ccxt/async_support/ubitex.py +12 -12
  192. ccxt/async_support/upbit.py +568 -165
  193. ccxt/async_support/vertex.py +160 -32
  194. ccxt/async_support/wallex.py +12 -12
  195. ccxt/async_support/wavesexchange.py +165 -30
  196. ccxt/async_support/whitebit.py +975 -127
  197. ccxt/async_support/woo.py +1918 -1016
  198. ccxt/async_support/woofipro.py +433 -141
  199. ccxt/async_support/xt.py +649 -193
  200. ccxt/async_support/yobit.py +195 -70
  201. ccxt/async_support/zaif.py +91 -15
  202. ccxt/async_support/zonda.py +151 -36
  203. ccxt/base/decimal_to_precision.py +14 -10
  204. ccxt/base/errors.py +49 -18
  205. ccxt/base/exchange.py +1556 -450
  206. ccxt/base/precise.py +10 -0
  207. ccxt/base/types.py +114 -6
  208. ccxt/bequant.py +5 -3
  209. ccxt/bigone.py +279 -144
  210. ccxt/binance.py +2347 -1158
  211. ccxt/binancecoinm.py +9 -3
  212. ccxt/binanceus.py +17 -3
  213. ccxt/binanceusdm.py +9 -4
  214. ccxt/bingx.py +2962 -920
  215. ccxt/bit2c.py +147 -27
  216. ccxt/bitbank.py +151 -23
  217. ccxt/bitbns.py +104 -30
  218. ccxt/bitfinex.py +3290 -1113
  219. ccxt/bitflyer.py +202 -27
  220. ccxt/bitget.py +3683 -1538
  221. ccxt/bithumb.py +194 -38
  222. ccxt/bitimen.py +9 -9
  223. ccxt/bitir.py +35 -35
  224. ccxt/bitmart.py +1288 -350
  225. ccxt/bitmex.py +260 -75
  226. ccxt/bitopro.py +262 -62
  227. ccxt/bitpin.py +15 -14
  228. ccxt/bitrue.py +459 -290
  229. ccxt/bitso.py +199 -54
  230. ccxt/bitstamp.py +230 -96
  231. ccxt/bitteam.py +167 -25
  232. ccxt/{huobijp.py → bittrade.py} +158 -30
  233. ccxt/bitvavo.py +213 -49
  234. ccxt/blockchaincom.py +160 -46
  235. ccxt/blofin.py +502 -120
  236. ccxt/btcalpha.py +169 -31
  237. ccxt/btcbox.py +291 -23
  238. ccxt/btcmarkets.py +211 -58
  239. ccxt/btcturk.py +161 -38
  240. ccxt/bybit.py +1775 -1030
  241. ccxt/cex.py +1439 -1303
  242. ccxt/coinbase.py +724 -212
  243. ccxt/coinbaseadvanced.py +2 -1
  244. ccxt/coinbaseexchange.py +388 -89
  245. ccxt/coinbaseinternational.py +412 -57
  246. ccxt/coincatch.py +177 -78
  247. ccxt/coincheck.py +135 -19
  248. ccxt/coinex.py +606 -232
  249. ccxt/coinmate.py +189 -63
  250. ccxt/coinmetro.py +194 -54
  251. ccxt/coinone.py +158 -51
  252. ccxt/coinsph.py +336 -61
  253. ccxt/coinspot.py +151 -52
  254. ccxt/cryptocom.py +661 -111
  255. ccxt/cryptomus.py +1137 -0
  256. ccxt/defx.py +2070 -0
  257. ccxt/delta.py +299 -99
  258. ccxt/deribit.py +348 -126
  259. ccxt/derive.py +2571 -0
  260. ccxt/digifinex.py +430 -214
  261. ccxt/ellipx.py +2029 -0
  262. ccxt/eterex.py +7 -7
  263. ccxt/excoino.py +29 -29
  264. ccxt/exir.py +11 -11
  265. ccxt/exmo.py +343 -131
  266. ccxt/exnovin.py +8 -8
  267. ccxt/farhadexchange.py +10 -10
  268. ccxt/fmfwio.py +2 -1
  269. ccxt/foxbit.py +1935 -0
  270. ccxt/gate.py +1351 -529
  271. ccxt/gateio.py +2 -1
  272. ccxt/gemini.py +144 -39
  273. ccxt/hashkey.py +152 -109
  274. ccxt/hibachi.py +2079 -0
  275. ccxt/hitbtc.py +395 -167
  276. ccxt/hitobit.py +9 -9
  277. ccxt/hollaex.py +307 -119
  278. ccxt/htx.py +851 -383
  279. ccxt/huobi.py +2 -1
  280. ccxt/hyperliquid.py +1848 -536
  281. ccxt/independentreserve.py +287 -15
  282. ccxt/indodax.py +190 -33
  283. ccxt/jibitex.py +9 -9
  284. ccxt/kraken.py +794 -351
  285. ccxt/krakenfutures.py +214 -62
  286. ccxt/kucoin.py +715 -396
  287. ccxt/kucoinfutures.py +652 -89
  288. ccxt/latoken.py +217 -113
  289. ccxt/lbank.py +425 -97
  290. ccxt/luno.py +382 -35
  291. ccxt/mercado.py +113 -6
  292. ccxt/mexc.py +873 -437
  293. ccxt/modetrade.py +2818 -0
  294. ccxt/myokx.py +54 -0
  295. ccxt/ndax.py +221 -64
  296. ccxt/nobitex.py +30 -36
  297. ccxt/novadax.py +190 -34
  298. ccxt/oceanex.py +217 -28
  299. ccxt/okcoin.py +253 -145
  300. ccxt/okexchange.py +9 -9
  301. ccxt/okx.py +1088 -351
  302. ccxt/okxus.py +54 -0
  303. ccxt/ompfinex.py +29 -24
  304. ccxt/onetrading.py +213 -392
  305. ccxt/oxfun.py +245 -166
  306. ccxt/p2b.py +151 -29
  307. ccxt/paradex.py +562 -49
  308. ccxt/paymium.py +82 -19
  309. ccxt/phemex.py +712 -172
  310. ccxt/poloniex.py +1601 -283
  311. ccxt/pro/__init__.py +76 -17
  312. ccxt/pro/alpaca.py +21 -6
  313. ccxt/pro/apex.py +984 -0
  314. ccxt/pro/ascendex.py +58 -10
  315. ccxt/pro/bequant.py +6 -1
  316. ccxt/pro/binance.py +728 -156
  317. ccxt/pro/binancecoinm.py +6 -2
  318. ccxt/pro/binanceus.py +8 -4
  319. ccxt/pro/binanceusdm.py +7 -2
  320. ccxt/pro/bingx.py +333 -142
  321. ccxt/pro/bitfinex.py +727 -262
  322. ccxt/pro/bitget.py +570 -79
  323. ccxt/pro/bithumb.py +20 -6
  324. ccxt/pro/bitmart.py +216 -87
  325. ccxt/pro/bitmex.py +47 -9
  326. ccxt/pro/bitopro.py +26 -14
  327. ccxt/pro/bitrue.py +22 -22
  328. ccxt/pro/bitstamp.py +54 -21
  329. ccxt/pro/{huobijp.py → bittrade.py} +7 -6
  330. ccxt/pro/bitvavo.py +191 -67
  331. ccxt/pro/blockchaincom.py +21 -8
  332. ccxt/pro/blofin.py +9 -1
  333. ccxt/pro/bybit.py +632 -245
  334. ccxt/pro/cex.py +59 -24
  335. ccxt/pro/coinbase.py +102 -73
  336. ccxt/pro/coinbaseadvanced.py +2 -1
  337. ccxt/pro/coinbaseexchange.py +8 -8
  338. ccxt/pro/coinbaseinternational.py +181 -25
  339. ccxt/pro/coincatch.py +6 -7
  340. ccxt/pro/coincheck.py +11 -6
  341. ccxt/pro/coinex.py +967 -665
  342. ccxt/pro/coinone.py +16 -9
  343. ccxt/pro/cryptocom.py +448 -45
  344. ccxt/pro/defx.py +831 -0
  345. ccxt/pro/deribit.py +150 -14
  346. ccxt/pro/derive.py +704 -0
  347. ccxt/pro/exmo.py +239 -6
  348. ccxt/pro/gate.py +623 -65
  349. ccxt/pro/gateio.py +2 -1
  350. ccxt/pro/gemini.py +27 -11
  351. ccxt/pro/hashkey.py +2 -2
  352. ccxt/pro/hitbtc.py +196 -91
  353. ccxt/pro/hollaex.py +23 -7
  354. ccxt/pro/htx.py +51 -14
  355. ccxt/pro/huobi.py +2 -1
  356. ccxt/pro/hyperliquid.py +591 -27
  357. ccxt/pro/independentreserve.py +9 -6
  358. ccxt/pro/kraken.py +640 -320
  359. ccxt/pro/krakenfutures.py +62 -35
  360. ccxt/pro/kucoin.py +267 -46
  361. ccxt/pro/kucoinfutures.py +165 -21
  362. ccxt/pro/lbank.py +102 -21
  363. ccxt/pro/luno.py +12 -8
  364. ccxt/pro/mexc.py +877 -111
  365. ccxt/pro/modetrade.py +1271 -0
  366. ccxt/pro/myokx.py +38 -0
  367. ccxt/pro/ndax.py +15 -2
  368. ccxt/pro/okcoin.py +23 -4
  369. ccxt/pro/okx.py +573 -98
  370. ccxt/pro/okxus.py +38 -0
  371. ccxt/pro/onetrading.py +30 -13
  372. ccxt/pro/oxfun.py +131 -27
  373. ccxt/pro/p2b.py +88 -22
  374. ccxt/pro/paradex.py +3 -3
  375. ccxt/pro/phemex.py +75 -21
  376. ccxt/pro/poloniex.py +124 -41
  377. ccxt/pro/probit.py +87 -80
  378. ccxt/pro/tradeogre.py +272 -0
  379. ccxt/pro/upbit.py +152 -12
  380. ccxt/pro/vertex.py +8 -3
  381. ccxt/pro/whitebit.py +58 -5
  382. ccxt/pro/woo.py +228 -37
  383. ccxt/pro/woofipro.py +106 -18
  384. ccxt/pro/xt.py +111 -5
  385. ccxt/probit.py +224 -95
  386. ccxt/protobuf/__init__.py +0 -0
  387. ccxt/protobuf/mexc/PrivateAccountV3Api_pb2.py +37 -0
  388. ccxt/protobuf/mexc/PrivateDealsV3Api_pb2.py +37 -0
  389. ccxt/protobuf/mexc/PrivateOrdersV3Api_pb2.py +37 -0
  390. ccxt/protobuf/mexc/PublicAggreBookTickerV3Api_pb2.py +37 -0
  391. ccxt/protobuf/mexc/PublicAggreDealsV3Api_pb2.py +39 -0
  392. ccxt/protobuf/mexc/PublicAggreDepthsV3Api_pb2.py +39 -0
  393. ccxt/protobuf/mexc/PublicBookTickerBatchV3Api_pb2.py +38 -0
  394. ccxt/protobuf/mexc/PublicBookTickerV3Api_pb2.py +37 -0
  395. ccxt/protobuf/mexc/PublicDealsV3Api_pb2.py +39 -0
  396. ccxt/protobuf/mexc/PublicIncreaseDepthsBatchV3Api_pb2.py +38 -0
  397. ccxt/protobuf/mexc/PublicIncreaseDepthsV3Api_pb2.py +39 -0
  398. ccxt/protobuf/mexc/PublicLimitDepthsV3Api_pb2.py +39 -0
  399. ccxt/protobuf/mexc/PublicMiniTickerV3Api_pb2.py +37 -0
  400. ccxt/protobuf/mexc/PublicMiniTickersV3Api_pb2.py +38 -0
  401. ccxt/protobuf/mexc/PublicSpotKlineV3Api_pb2.py +37 -0
  402. ccxt/protobuf/mexc/PushDataV3ApiWrapper_pb2.py +52 -0
  403. ccxt/protobuf/mexc/__init__.py +0 -0
  404. ccxt/ramzinex.py +32 -28
  405. ccxt/sarmayex.py +7 -7
  406. ccxt/sarrafex.py +10 -10
  407. ccxt/static_dependencies/__init__.py +1 -1
  408. ccxt/static_dependencies/lark/py.typed +0 -0
  409. ccxt/static_dependencies/marshmallow/py.typed +0 -0
  410. ccxt/static_dependencies/marshmallow_dataclass/py.typed +0 -0
  411. ccxt/static_dependencies/marshmallow_oneofschema/py.typed +0 -0
  412. ccxt/tabdeal.py +13 -12
  413. ccxt/test/tests_async.py +261 -57
  414. ccxt/test/tests_helpers.py +1 -3
  415. ccxt/test/tests_init.py +4 -3
  416. ccxt/test/tests_sync.py +261 -57
  417. ccxt/tetherland.py +7 -7
  418. ccxt/timex.py +210 -51
  419. ccxt/tokocrypto.py +167 -47
  420. ccxt/tradeogre.py +266 -31
  421. ccxt/twox.py +7 -7
  422. ccxt/ubitex.py +9 -9
  423. ccxt/upbit.py +568 -165
  424. ccxt/vertex.py +160 -32
  425. ccxt/wallex.py +9 -9
  426. ccxt/wavesexchange.py +165 -30
  427. ccxt/whitebit.py +975 -127
  428. ccxt/woo.py +1917 -1016
  429. ccxt/woofipro.py +432 -141
  430. ccxt/xt.py +649 -193
  431. ccxt/yobit.py +194 -70
  432. ccxt/zaif.py +91 -15
  433. ccxt/zonda.py +151 -36
  434. {ccxt_ir-4.3.46.0.3.dist-info → ccxt_ir-4.5.1.dist-info}/METADATA +225 -73
  435. ccxt_ir-4.5.1.dist-info/RECORD +743 -0
  436. {ccxt_ir-4.3.46.0.3.dist-info → ccxt_ir-4.5.1.dist-info}/WHEEL +1 -1
  437. ccxt/__test__.py +0 -7
  438. ccxt/abstract/ace.py +0 -15
  439. ccxt/abstract/bitbay.py +0 -53
  440. ccxt/abstract/bitcoincom.py +0 -115
  441. ccxt/abstract/bitfinex2.py +0 -139
  442. ccxt/abstract/bitpanda.py +0 -35
  443. ccxt/abstract/bl3p.py +0 -19
  444. ccxt/abstract/coinlist.py +0 -54
  445. ccxt/abstract/currencycom.py +0 -68
  446. ccxt/abstract/hitbtc3.py +0 -115
  447. ccxt/abstract/idex.py +0 -26
  448. ccxt/abstract/kuna.py +0 -182
  449. ccxt/abstract/lykke.py +0 -29
  450. ccxt/abstract/poloniexfutures.py +0 -48
  451. ccxt/abstract/wazirx.py +0 -30
  452. ccxt/ace.py +0 -1012
  453. ccxt/async_support/ace.py +0 -1012
  454. ccxt/async_support/base/ws/aiohttp_client.py +0 -125
  455. ccxt/async_support/base/ws/fast_client.py +0 -96
  456. ccxt/async_support/bitbay.py +0 -17
  457. ccxt/async_support/bitcoincom.py +0 -17
  458. ccxt/async_support/bitfinex2.py +0 -3552
  459. ccxt/async_support/bitpanda.py +0 -16
  460. ccxt/async_support/bl3p.py +0 -485
  461. ccxt/async_support/coinlist.py +0 -2243
  462. ccxt/async_support/currencycom.py +0 -1950
  463. ccxt/async_support/hitbtc3.py +0 -16
  464. ccxt/async_support/idex.py +0 -1766
  465. ccxt/async_support/kuna.py +0 -1841
  466. ccxt/async_support/lykke.py +0 -1270
  467. ccxt/async_support/poloniexfutures.py +0 -1717
  468. ccxt/async_support/wazirx.py +0 -1224
  469. ccxt/bitbay.py +0 -17
  470. ccxt/bitcoincom.py +0 -17
  471. ccxt/bitfinex2.py +0 -3552
  472. ccxt/bitpanda.py +0 -16
  473. ccxt/bl3p.py +0 -485
  474. ccxt/coinlist.py +0 -2243
  475. ccxt/currencycom.py +0 -1950
  476. ccxt/hitbtc3.py +0 -16
  477. ccxt/idex.py +0 -1766
  478. ccxt/kuna.py +0 -1841
  479. ccxt/lykke.py +0 -1270
  480. ccxt/poloniexfutures.py +0 -1717
  481. ccxt/pro/bitcoincom.py +0 -34
  482. ccxt/pro/bitfinex2.py +0 -1083
  483. ccxt/pro/bitpanda.py +0 -15
  484. ccxt/pro/currencycom.py +0 -536
  485. ccxt/pro/idex.py +0 -672
  486. ccxt/pro/poloniexfutures.py +0 -990
  487. ccxt/pro/wazirx.py +0 -749
  488. ccxt/test/base/__init__.py +0 -29
  489. ccxt/test/base/test_account.py +0 -26
  490. ccxt/test/base/test_balance.py +0 -56
  491. ccxt/test/base/test_borrow_interest.py +0 -35
  492. ccxt/test/base/test_borrow_rate.py +0 -32
  493. ccxt/test/base/test_calculate_fee.py +0 -51
  494. ccxt/test/base/test_crypto.py +0 -127
  495. ccxt/test/base/test_currency.py +0 -76
  496. ccxt/test/base/test_datetime.py +0 -109
  497. ccxt/test/base/test_decimal_to_precision.py +0 -392
  498. ccxt/test/base/test_deep_extend.py +0 -68
  499. ccxt/test/base/test_deposit_withdrawal.py +0 -50
  500. ccxt/test/base/test_exchange_datetime_functions.py +0 -76
  501. ccxt/test/base/test_funding_rate_history.py +0 -29
  502. ccxt/test/base/test_last_price.py +0 -31
  503. ccxt/test/base/test_ledger_entry.py +0 -45
  504. ccxt/test/base/test_ledger_item.py +0 -48
  505. ccxt/test/base/test_leverage_tier.py +0 -33
  506. ccxt/test/base/test_liquidation.py +0 -50
  507. ccxt/test/base/test_margin_mode.py +0 -24
  508. ccxt/test/base/test_margin_modification.py +0 -35
  509. ccxt/test/base/test_market.py +0 -193
  510. ccxt/test/base/test_number.py +0 -411
  511. ccxt/test/base/test_ohlcv.py +0 -33
  512. ccxt/test/base/test_open_interest.py +0 -32
  513. ccxt/test/base/test_order.py +0 -64
  514. ccxt/test/base/test_order_book.py +0 -69
  515. ccxt/test/base/test_position.py +0 -60
  516. ccxt/test/base/test_shared_methods.py +0 -353
  517. ccxt/test/base/test_status.py +0 -24
  518. ccxt/test/base/test_throttle.py +0 -126
  519. ccxt/test/base/test_ticker.py +0 -92
  520. ccxt/test/base/test_trade.py +0 -47
  521. ccxt/test/base/test_trading_fee.py +0 -26
  522. ccxt/test/base/test_transaction.py +0 -39
  523. ccxt/test/test_async.py +0 -1649
  524. ccxt/test/test_sync.py +0 -1648
  525. ccxt/wazirx.py +0 -1224
  526. ccxt_ir-4.3.46.0.3.dist-info/RECORD +0 -773
  527. /ccxt/abstract/{huobijp.py → bittrade.py} +0 -0
  528. {ccxt_ir-4.3.46.0.3.dist-info → ccxt_ir-4.5.1.dist-info/licenses}/LICENSE.txt +0 -0
  529. {ccxt_ir-4.3.46.0.3.dist-info → ccxt_ir-4.5.1.dist-info}/top_level.txt +0 -0
ccxt/pro/okx.py CHANGED
@@ -6,7 +6,7 @@
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, Liquidation, Num, Order, OrderBook, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade
9
+ from ccxt.base.types import Any, Balances, Bool, 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
12
  from ccxt.base.errors import ExchangeError
@@ -14,16 +14,20 @@ from ccxt.base.errors import AuthenticationError
14
14
  from ccxt.base.errors import ArgumentsRequired
15
15
  from ccxt.base.errors import BadRequest
16
16
  from ccxt.base.errors import InvalidNonce
17
+ from ccxt.base.errors import ChecksumError
17
18
 
18
19
 
19
20
  class okx(ccxt.async_support.okx):
20
21
 
21
- def describe(self):
22
+ def describe(self) -> Any:
22
23
  return self.deep_extend(super(okx, self).describe(), {
23
24
  'has': {
24
25
  'ws': True,
25
26
  'watchTicker': True,
27
+ 'watchMarkPrice': True,
28
+ 'watchMarkPrices': True,
26
29
  'watchTickers': True,
30
+ 'watchBidsAsks': True,
27
31
  'watchOrderBook': True,
28
32
  'watchTrades': True,
29
33
  'watchTradesForSymbols': True,
@@ -56,6 +60,7 @@ class okx(ccxt.async_support.okx):
56
60
  },
57
61
  'options': {
58
62
  'watchOrderBook': {
63
+ 'checksum': True,
59
64
  #
60
65
  # bbo-tbt
61
66
  # 1. Newly added channel that sends tick-by-tick Level 1 data
@@ -103,13 +108,12 @@ class okx(ccxt.async_support.okx):
103
108
  'ws': {
104
109
  # 'inflate': True,
105
110
  },
106
- 'checksum': True,
107
111
  },
108
112
  'streaming': {
109
113
  # okex does not support built-in ws protocol-level ping-pong
110
114
  # instead it requires a custom text-based ping-pong
111
115
  'ping': self.ping,
112
- 'keepAlive': 20000,
116
+ 'keepAlive': 18000,
113
117
  },
114
118
  })
115
119
 
@@ -132,9 +136,8 @@ class okx(ccxt.async_support.okx):
132
136
  symbols = self.symbols
133
137
  symbols = self.market_symbols(symbols)
134
138
  url = self.get_url(channel, access)
135
- messageHash = channel
139
+ messageHashes = []
136
140
  args = []
137
- messageHash += '::' + ','.join(symbols)
138
141
  for i in range(0, len(symbols)):
139
142
  marketId = self.market_id(symbols[i])
140
143
  arg: dict = {
@@ -142,11 +145,12 @@ class okx(ccxt.async_support.okx):
142
145
  'instId': marketId,
143
146
  }
144
147
  args.append(self.extend(arg, params))
148
+ messageHashes.append(channel + '::' + symbols[i])
145
149
  request: dict = {
146
150
  'op': 'subscribe',
147
151
  'args': args,
148
152
  }
149
- return await self.watch(url, messageHash, request, messageHash)
153
+ return await self.watch_multiple(url, messageHashes, request, messageHashes)
150
154
 
151
155
  async def subscribe(self, access, messageHash, channel, symbol, params={}):
152
156
  await self.load_markets()
@@ -180,7 +184,7 @@ class okx(ccxt.async_support.okx):
180
184
  async def watch_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Trade]:
181
185
  """
182
186
  get the list of most recent trades for a particular symbol
183
- :param str symbol: unified symbol of the market to fetch trades for
187
+ :param str symbols:
184
188
  :param int [since]: timestamp in ms of the earliest trade to fetch
185
189
  :param int [limit]: the maximum amount of trades to fetch
186
190
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -215,6 +219,43 @@ class okx(ccxt.async_support.okx):
215
219
  limit = trades.getLimit(tradeSymbol, limit)
216
220
  return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
217
221
 
222
+ async def un_watch_trades_for_symbols(self, symbols: List[str], params={}) -> Any:
223
+ """
224
+ unWatches from the stream channel
225
+ :param str[] symbols:
226
+ :param dict [params]: extra parameters specific to the exchange API endpoint
227
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
228
+ """
229
+ await self.load_markets()
230
+ symbols = self.market_symbols(symbols, None, False)
231
+ channel = 'trades'
232
+ topics = []
233
+ messageHashes = []
234
+ for i in range(0, len(symbols)):
235
+ symbol = symbols[i]
236
+ messageHashes.append('unsubscribe:trades:' + symbol)
237
+ marketId = self.market_id(symbol)
238
+ topic: dict = {
239
+ 'channel': channel,
240
+ 'instId': marketId,
241
+ }
242
+ topics.append(topic)
243
+ request: dict = {
244
+ 'op': 'unsubscribe',
245
+ 'args': topics,
246
+ }
247
+ url = self.get_url(channel, 'public')
248
+ return await self.watch_multiple(url, messageHashes, request, messageHashes)
249
+
250
+ async def un_watch_trades(self, symbol: str, params={}) -> Any:
251
+ """
252
+ unWatches from the stream channel
253
+ :param str symbol: unified symbol of the market to fetch trades for
254
+ :param dict [params]: extra parameters specific to the exchange API endpoint
255
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
256
+ """
257
+ return await self.un_watch_trades_for_symbols([symbol], params)
258
+
218
259
  def handle_trades(self, client: Client, message):
219
260
  #
220
261
  # {
@@ -250,7 +291,9 @@ class okx(ccxt.async_support.okx):
250
291
  async def watch_funding_rate(self, symbol: str, params={}) -> FundingRate:
251
292
  """
252
293
  watch the current funding rate
253
- :see: https://www.okx.com/docs-v5/en/#public-data-websocket-funding-rate-channel
294
+
295
+ https://www.okx.com/docs-v5/en/#public-data-websocket-funding-rate-channel
296
+
254
297
  :param str symbol: unified market symbol
255
298
  :param dict [params]: extra parameters specific to the exchange API endpoint
256
299
  :returns dict: a `funding rate structure <https://docs.ccxt.com/#/?id=funding-rate-structure>`
@@ -262,7 +305,9 @@ class okx(ccxt.async_support.okx):
262
305
  async def watch_funding_rates(self, symbols: List[str], params={}) -> FundingRates:
263
306
  """
264
307
  watch the funding rate for multiple markets
265
- :see: https://www.okx.com/docs-v5/en/#public-data-websocket-funding-rate-channel
308
+
309
+ https://www.okx.com/docs-v5/en/#public-data-websocket-funding-rate-channel
310
+
266
311
  :param str[] symbols: list of unified market symbols
267
312
  :param dict [params]: extra parameters specific to the exchange API endpoint
268
313
  :returns dict: a dictionary of `funding rates structures <https://docs.ccxt.com/#/?id=funding-rates-structure>`, indexe by market symbols
@@ -324,7 +369,9 @@ class okx(ccxt.async_support.okx):
324
369
 
325
370
  async def watch_ticker(self, symbol: str, params={}) -> Ticker:
326
371
  """
327
- :see: https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-tickers-channel
372
+
373
+ https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-tickers-channel
374
+
328
375
  watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
329
376
  :param str symbol: unified symbol of the market to fetch the ticker for
330
377
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -334,20 +381,37 @@ class okx(ccxt.async_support.okx):
334
381
  channel = None
335
382
  channel, params = self.handle_option_and_params(params, 'watchTicker', 'channel', 'tickers')
336
383
  params['channel'] = channel
384
+ market = self.market(symbol)
385
+ symbol = market['symbol']
337
386
  ticker = await self.watch_tickers([symbol], params)
338
387
  return self.safe_value(ticker, symbol)
339
388
 
389
+ async def un_watch_ticker(self, symbol: str, params={}) -> Any:
390
+ """
391
+
392
+ https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-tickers-channel
393
+
394
+ unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
395
+ :param str symbol: unified symbol of the market to fetch the ticker for
396
+ :param dict [params]: extra parameters specific to the exchange API endpoint
397
+ :param str [params.channel]: the channel to subscribe to, tickers by default. Can be tickers, sprd-tickers, index-tickers, block-tickers
398
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
399
+ """
400
+ return await self.un_watch_tickers([symbol], params)
401
+
340
402
  async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
341
403
  """
342
- :see: https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-tickers-channel
404
+
405
+ https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-tickers-channel
406
+
343
407
  watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
344
408
  :param str[] [symbols]: unified symbol of the market to fetch the ticker for
345
409
  :param dict [params]: extra parameters specific to the exchange API endpoint
346
410
  :param str [params.channel]: the channel to subscribe to, tickers by default. Can be tickers, sprd-tickers, index-tickers, block-tickers
347
411
  :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
348
412
  """
349
- if self.is_empty(symbols):
350
- raise ArgumentsRequired(self.id + ' watchTickers requires a list of symbols')
413
+ await self.load_markets()
414
+ symbols = self.market_symbols(symbols, None, False)
351
415
  channel = None
352
416
  channel, params = self.handle_option_and_params(params, 'watchTickers', 'channel', 'tickers')
353
417
  newTickers = await self.subscribe_multiple('public', channel, symbols, params)
@@ -355,6 +419,78 @@ class okx(ccxt.async_support.okx):
355
419
  return newTickers
356
420
  return self.filter_by_array(self.tickers, 'symbol', symbols)
357
421
 
422
+ async def watch_mark_price(self, symbol: str, params={}) -> Ticker:
423
+ """
424
+
425
+ https://www.okx.com/docs-v5/en/#public-data-websocket-mark-price-channel
426
+
427
+ watches a mark price
428
+ :param str symbol: unified symbol of the market to fetch the ticker for
429
+ :param dict [params]: extra parameters specific to the exchange API endpoint
430
+ :param str [params.channel]: the channel to subscribe to, tickers by default. Can be tickers, sprd-tickers, index-tickers, block-tickers
431
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
432
+ """
433
+ channel = None
434
+ channel, params = self.handle_option_and_params(params, 'watchMarkPrice', 'channel', 'mark-price')
435
+ params['channel'] = channel
436
+ market = self.market(symbol)
437
+ symbol = market['symbol']
438
+ ticker = await self.watch_mark_prices([symbol], params)
439
+ return ticker[symbol]
440
+
441
+ async def watch_mark_prices(self, symbols: Strings = None, params={}) -> Tickers:
442
+ """
443
+
444
+ https://www.okx.com/docs-v5/en/#public-data-websocket-mark-price-channel
445
+
446
+ watches mark prices
447
+ :param str[] [symbols]: unified symbol of the market to fetch the ticker for
448
+ :param dict [params]: extra parameters specific to the exchange API endpoint
449
+ :param str [params.channel]: the channel to subscribe to, tickers by default. Can be tickers, sprd-tickers, index-tickers, block-tickers
450
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
451
+ """
452
+ await self.load_markets()
453
+ symbols = self.market_symbols(symbols, None, False)
454
+ channel = None
455
+ channel, params = self.handle_option_and_params(params, 'watchMarkPrices', 'channel', 'mark-price')
456
+ newTickers = await self.subscribe_multiple('public', channel, symbols, params)
457
+ if self.newUpdates:
458
+ return newTickers
459
+ return self.filter_by_array(self.tickers, 'symbol', symbols)
460
+
461
+ async def un_watch_tickers(self, symbols: Strings = None, params={}) -> Any:
462
+ """
463
+
464
+ https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-tickers-channel
465
+
466
+ unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
467
+ :param str[] [symbols]: unified symbol of the market to fetch the ticker for
468
+ :param dict [params]: extra parameters specific to the exchange API endpoint
469
+ :param str [params.channel]: the channel to subscribe to, tickers by default. Can be tickers, sprd-tickers, index-tickers, block-tickers
470
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
471
+ """
472
+ await self.load_markets()
473
+ symbols = self.market_symbols(symbols, None, False)
474
+ channel = None
475
+ channel, params = self.handle_option_and_params(params, 'watchTickers', 'channel', 'tickers')
476
+ topics = []
477
+ messageHashes = []
478
+ for i in range(0, len(symbols)):
479
+ symbol = symbols[i]
480
+ messageHashes.append('unsubscribe:ticker:' + symbol)
481
+ marketId = self.market_id(symbol)
482
+ topic: dict = {
483
+ 'channel': channel,
484
+ 'instId': marketId,
485
+ }
486
+ topics.append(topic)
487
+ request: dict = {
488
+ 'op': 'unsubscribe',
489
+ 'args': topics,
490
+ }
491
+ url = self.get_url(channel, 'public')
492
+ return await self.watch_multiple(url, messageHashes, request, messageHashes)
493
+
358
494
  def handle_ticker(self, client: Client, message):
359
495
  #
360
496
  # {
@@ -381,33 +517,114 @@ class okx(ccxt.async_support.okx):
381
517
  # ]
382
518
  # }
383
519
  #
520
+ self.handle_bid_ask(client, message)
384
521
  arg = self.safe_value(message, 'arg', {})
522
+ marketId = self.safe_string(arg, 'instId')
523
+ market = self.safe_market(marketId, None, '-')
524
+ symbol = market['symbol']
385
525
  channel = self.safe_string(arg, 'channel')
386
526
  data = self.safe_value(message, 'data', [])
387
- newTickers = []
527
+ newTickers: dict = {}
388
528
  for i in range(0, len(data)):
389
529
  ticker = self.parse_ticker(data[i])
390
- symbol = ticker['symbol']
391
530
  self.tickers[symbol] = ticker
392
- newTickers.append(ticker)
393
- messageHashes = self.find_message_hashes(client, channel + '::')
394
- for i in range(0, len(messageHashes)):
395
- messageHash = messageHashes[i]
396
- parts = messageHash.split('::')
397
- symbolsString = parts[1]
398
- symbols = symbolsString.split(',')
399
- tickers = self.filter_by_array(newTickers, 'symbol', symbols)
400
- tickersSymbols = list(tickers.keys())
401
- numTickers = len(tickersSymbols)
402
- if numTickers > 0:
403
- client.resolve(tickers, messageHash)
404
- return message
531
+ newTickers[symbol] = ticker
532
+ messageHash = channel + '::' + symbol
533
+ client.resolve(newTickers, messageHash)
405
534
 
406
- async def watch_liquidations_for_symbols(self, symbols: List[str] = None, since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
535
+ async def watch_bids_asks(self, symbols: Strings = None, params={}) -> Tickers:
536
+ """
537
+
538
+ https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-tickers-channel
539
+
540
+ watches best bid & ask for symbols
541
+ :param str[] symbols: unified symbol of the market to fetch the ticker for
542
+ :param dict [params]: extra parameters specific to the exchange API endpoint
543
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
544
+ """
545
+ await self.load_markets()
546
+ symbols = self.market_symbols(symbols, None, False)
547
+ channel = None
548
+ channel, params = self.handle_option_and_params(params, 'watchBidsAsks', 'channel', 'tickers')
549
+ url = self.get_url(channel, 'public')
550
+ messageHashes = []
551
+ args = []
552
+ for i in range(0, len(symbols)):
553
+ marketId = self.market_id(symbols[i])
554
+ arg: dict = {
555
+ 'channel': channel,
556
+ 'instId': marketId,
557
+ }
558
+ args.append(self.extend(arg, params))
559
+ messageHashes.append('bidask::' + symbols[i])
560
+ request: dict = {
561
+ 'op': 'subscribe',
562
+ 'args': args,
563
+ }
564
+ newTickers = await self.watch_multiple(url, messageHashes, request, messageHashes)
565
+ if self.newUpdates:
566
+ tickers: dict = {}
567
+ tickers[newTickers['symbol']] = newTickers
568
+ return tickers
569
+ return self.filter_by_array(self.bidsasks, 'symbol', symbols)
570
+
571
+ def handle_bid_ask(self, client: Client, message):
572
+ #
573
+ # {
574
+ # "arg": {channel: "tickers", instId: "BTC-USDT"},
575
+ # "data": [
576
+ # {
577
+ # "instType": "SPOT",
578
+ # "instId": "BTC-USDT",
579
+ # "last": "31500.1",
580
+ # "lastSz": "0.00001754",
581
+ # "askPx": "31500.1",
582
+ # "askSz": "0.00998144",
583
+ # "bidPx": "31500",
584
+ # "bidSz": "3.05652439",
585
+ # "open24h": "31697",
586
+ # "high24h": "32248",
587
+ # "low24h": "31165.6",
588
+ # "sodUtc0": "31385.5",
589
+ # "sodUtc8": "32134.9",
590
+ # "volCcy24h": "503403597.38138519",
591
+ # "vol24h": "15937.10781721",
592
+ # "ts": "1626526618762"
593
+ # }
594
+ # ]
595
+ # }
596
+ #
597
+ data = self.safe_list(message, 'data', [])
598
+ ticker = self.safe_dict(data, 0, {})
599
+ parsedTicker = self.parse_ws_bid_ask(ticker)
600
+ symbol = parsedTicker['symbol']
601
+ self.bidsasks[symbol] = parsedTicker
602
+ messageHash = 'bidask::' + symbol
603
+ client.resolve(parsedTicker, messageHash)
604
+
605
+ def parse_ws_bid_ask(self, ticker, market=None):
606
+ marketId = self.safe_string(ticker, 'instId')
607
+ market = self.safe_market(marketId, market)
608
+ symbol = self.safe_string(market, 'symbol')
609
+ timestamp = self.safe_integer(ticker, 'ts')
610
+ return self.safe_ticker({
611
+ 'symbol': symbol,
612
+ 'timestamp': timestamp,
613
+ 'datetime': self.iso8601(timestamp),
614
+ 'ask': self.safe_string(ticker, 'askPx'),
615
+ 'askVolume': self.safe_string(ticker, 'askSz'),
616
+ 'bid': self.safe_string(ticker, 'bidPx'),
617
+ 'bidVolume': self.safe_string(ticker, 'bidSz'),
618
+ 'info': ticker,
619
+ }, market)
620
+
621
+ async def watch_liquidations_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
407
622
  """
408
623
  watch the public liquidations of a trading pair
409
- :see: https://www.okx.com/docs-v5/en/#public-data-websocket-liquidation-orders-channel
410
- :param str symbol: unified CCXT market symbol
624
+
625
+ https://www.okx.com/docs-v5/en/#public-data-websocket-liquidation-orders-channel
626
+
627
+ :param str symbols:
411
628
  :param int [since]: the earliest time in ms to fetch liquidations for
412
629
  :param int [limit]: the maximum number of liquidation structures to retrieve
413
630
  :param dict [params]: exchange specific parameters for the okx api endpoint
@@ -416,8 +633,13 @@ class okx(ccxt.async_support.okx):
416
633
  await self.load_markets()
417
634
  symbols = self.market_symbols(symbols, None, True, True)
418
635
  messageHash = 'liquidations'
636
+ messageHashes = []
419
637
  if symbols is not None:
420
- messageHash += '::' + ','.join(symbols)
638
+ for i in range(0, len(symbols)):
639
+ symbol = symbols[i]
640
+ messageHashes.append(messageHash + '::' + symbol)
641
+ else:
642
+ messageHashes.append(messageHash)
421
643
  market = self.get_market_from_symbols(symbols)
422
644
  type = None
423
645
  type, params = self.handle_market_type_and_params('watchliquidationsForSymbols', market, params)
@@ -428,9 +650,16 @@ class okx(ccxt.async_support.okx):
428
650
  type = 'futures'
429
651
  uppercaseType = type.upper()
430
652
  request = {
431
- 'instType': uppercaseType,
653
+ 'op': 'subscribe',
654
+ 'args': [
655
+ {
656
+ 'channel': channel,
657
+ 'instType': uppercaseType,
658
+ },
659
+ ],
432
660
  }
433
- newLiquidations = await self.subscribe('public', messageHash, channel, None, self.extend(request, params))
661
+ url = self.get_url(channel, 'public')
662
+ newLiquidations = await self.watch_multiple(url, messageHashes, request, messageHashes)
434
663
  if self.newUpdates:
435
664
  return newLiquidations
436
665
  return self.filter_by_symbols_since_limit(self.liquidations, symbols, since, limit, True)
@@ -477,26 +706,42 @@ class okx(ccxt.async_support.okx):
477
706
  client.resolve([liquidation], 'liquidations')
478
707
  client.resolve([liquidation], 'liquidations::' + symbol)
479
708
 
480
- async def watch_my_liquidations_for_symbols(self, symbols: List[str] = None, since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
709
+ async def watch_my_liquidations_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Liquidation]:
481
710
  """
482
711
  watch the private liquidations of a trading pair
483
- :see: https://www.okx.com/docs-v5/en/#trading-account-websocket-balance-and-position-channel
484
- :param str symbol: unified CCXT market symbol
712
+
713
+ https://www.okx.com/docs-v5/en/#trading-account-websocket-balance-and-position-channel
714
+
715
+ :param str[] symbols:
485
716
  :param int [since]: the earliest time in ms to fetch liquidations for
486
717
  :param int [limit]: the maximum number of liquidation structures to retrieve
487
718
  :param dict [params]: exchange specific parameters for the okx api endpoint
488
719
  :returns dict: an array of `liquidation structures <https://github.com/ccxt/ccxt/wiki/Manual#liquidation-structure>`
489
720
  """
490
721
  await self.load_markets()
491
- isStop = self.safe_value_2(params, 'stop', 'trigger', False)
722
+ isTrigger = self.safe_value_2(params, 'stop', 'trigger', False)
492
723
  params = self.omit(params, ['stop', 'trigger'])
493
- await self.authenticate({'access': 'business' if isStop else 'private'})
724
+ await self.authenticate({'access': 'business' if isTrigger else 'private'})
494
725
  symbols = self.market_symbols(symbols, None, True, True)
495
726
  messageHash = 'myLiquidations'
727
+ messageHashes = []
496
728
  if symbols is not None:
497
- messageHash += '::' + ','.join(symbols)
729
+ for i in range(0, len(symbols)):
730
+ symbol = symbols[i]
731
+ messageHashes.append(messageHash + '::' + symbol)
732
+ else:
733
+ messageHashes.append(messageHash)
498
734
  channel = 'balance_and_position'
499
- newLiquidations = await self.subscribe('private', messageHash, channel, None, params)
735
+ request: dict = {
736
+ 'op': 'subscribe',
737
+ 'args': [
738
+ {
739
+ 'channel': channel,
740
+ },
741
+ ],
742
+ }
743
+ url = self.get_url(channel, 'private')
744
+ newLiquidations = await self.watch_multiple(url, messageHashes, self.deep_extend(request, params), messageHashes)
500
745
  if self.newUpdates:
501
746
  return newLiquidations
502
747
  return self.filter_by_symbols_since_limit(self.liquidations, symbols, since, limit, True)
@@ -631,6 +876,7 @@ class okx(ccxt.async_support.okx):
631
876
  'contracts': self.safe_number(liquidationDetails, 'sz'),
632
877
  'contractSize': self.safe_number(market, 'contractSize'),
633
878
  'price': self.safe_number(liquidationDetails, 'bkPx'),
879
+ 'side': self.safe_string(liquidationDetails, 'side'),
634
880
  'baseValue': None,
635
881
  'quoteValue': None,
636
882
  'timestamp': timestamp,
@@ -656,6 +902,17 @@ class okx(ccxt.async_support.okx):
656
902
  limit = ohlcv.getLimit(symbol, limit)
657
903
  return self.filter_by_since_limit(ohlcv, since, limit, 0, True)
658
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
+
659
916
  async def watch_ohlcv_for_symbols(self, symbolsAndTimeframes: List[List[str]], since: Int = None, limit: Int = None, params={}):
660
917
  """
661
918
  watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
@@ -695,6 +952,39 @@ class okx(ccxt.async_support.okx):
695
952
  filtered = self.filter_by_since_limit(candles, since, limit, 0, True)
696
953
  return self.create_ohlcv_object(symbol, timeframe, filtered)
697
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
+
698
988
  def handle_ohlcv(self, client: Client, message):
699
989
  #
700
990
  # {
@@ -740,7 +1030,9 @@ class okx(ccxt.async_support.okx):
740
1030
 
741
1031
  async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
742
1032
  """
743
- :see: https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-order-book-channel
1033
+
1034
+ https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-order-book-channel
1035
+
744
1036
  watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
745
1037
  :param str symbol: unified symbol of the market to fetch the order book for
746
1038
  :param int [limit]: the maximum amount of order book entries to return
@@ -775,7 +1067,9 @@ class okx(ccxt.async_support.okx):
775
1067
 
776
1068
  async def watch_order_book_for_symbols(self, symbols: List[str], limit: Int = None, params={}) -> OrderBook:
777
1069
  """
778
- :see: https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-order-book-channel
1070
+
1071
+ https://www.okx.com/docs-v5/en/#order-book-trading-market-data-ws-order-book-channel
1072
+
779
1073
  watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
780
1074
  :param str[] symbols: unified array of symbols
781
1075
  :param int [limit]: 1,5, 400, 50(l2-tbt, vip4+) or 40000(vip5+) the maximum amount of order book entries to return
@@ -819,6 +1113,66 @@ class okx(ccxt.async_support.okx):
819
1113
  orderbook = await self.watch_multiple(url, messageHashes, request, messageHashes)
820
1114
  return orderbook.limit()
821
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
+
822
1176
  def handle_delta(self, bookside, delta):
823
1177
  #
824
1178
  # [
@@ -836,7 +1190,7 @@ class okx(ccxt.async_support.okx):
836
1190
  for i in range(0, len(deltas)):
837
1191
  self.handle_delta(bookside, deltas[i])
838
1192
 
839
- 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):
840
1194
  #
841
1195
  # {
842
1196
  # "asks": [
@@ -851,6 +1205,9 @@ class okx(ccxt.async_support.okx):
851
1205
  # ],
852
1206
  # "instId": "BTC-USDT",
853
1207
  # "ts": "1626537446491"
1208
+ # "checksum": -855196043,
1209
+ # "prevSeqId": 123456,
1210
+ # "seqId": 123457
854
1211
  # }
855
1212
  #
856
1213
  asks = self.safe_value(message, 'asks', [])
@@ -860,9 +1217,12 @@ class okx(ccxt.async_support.okx):
860
1217
  self.handle_deltas(storedAsks, asks)
861
1218
  self.handle_deltas(storedBids, bids)
862
1219
  marketId = self.safe_string(message, 'instId')
863
- symbol = self.safe_symbol(marketId)
864
- checksum = self.safe_bool(self.options, 'checksum', True)
1220
+ symbol = self.safe_symbol(marketId, market)
1221
+ checksum = self.handle_option('watchOrderBook', 'checksum', True)
1222
+ seqId = self.safe_integer(message, 'seqId')
865
1223
  if checksum:
1224
+ prevSeqId = self.safe_integer(message, 'prevSeqId')
1225
+ nonce = orderbook['nonce']
866
1226
  asksLength = len(storedAsks)
867
1227
  bidsLength = len(storedBids)
868
1228
  payloadArray = []
@@ -876,12 +1236,17 @@ class okx(ccxt.async_support.okx):
876
1236
  payload = ':'.join(payloadArray)
877
1237
  responseChecksum = self.safe_integer(message, 'checksum')
878
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')
879
1242
  if responseChecksum != localChecksum:
880
- error = InvalidNonce(self.id + ' invalid checksum')
1243
+ error = ChecksumError(self.id + ' ' + self.orderbook_checksum_message(symbol))
1244
+ if error is not None:
881
1245
  del client.subscriptions[messageHash]
882
1246
  del self.orderbooks[symbol]
883
1247
  client.reject(error, messageHash)
884
1248
  timestamp = self.safe_integer(message, 'ts')
1249
+ orderbook['nonce'] = seqId
885
1250
  orderbook['timestamp'] = timestamp
886
1251
  orderbook['datetime'] = self.iso8601(timestamp)
887
1252
  return orderbook
@@ -1001,7 +1366,7 @@ class okx(ccxt.async_support.okx):
1001
1366
  orderbook = self.orderbooks[symbol]
1002
1367
  for i in range(0, len(data)):
1003
1368
  update = data[i]
1004
- self.handle_order_book_message(client, update, orderbook, messageHash)
1369
+ self.handle_order_book_message(client, update, orderbook, messageHash, market)
1005
1370
  client.resolve(orderbook, messageHash)
1006
1371
  elif (channel == 'books5') or (channel == 'bbo-tbt'):
1007
1372
  if not (symbol in self.orderbooks):
@@ -1042,8 +1407,10 @@ class okx(ccxt.async_support.okx):
1042
1407
  },
1043
1408
  ],
1044
1409
  }
1045
- message = self.extend(request, params)
1046
- 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)
1047
1414
  return await future
1048
1415
 
1049
1416
  async def watch_balance(self, params={}) -> Balances:
@@ -1139,24 +1506,26 @@ class okx(ccxt.async_support.okx):
1139
1506
  async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
1140
1507
  """
1141
1508
  watches information on multiple trades made by the user
1142
- :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
+
1143
1512
  :param str [symbol]: unified market symbol of the market trades were made in
1144
1513
  :param int [since]: the earliest time in ms to fetch trades for
1145
1514
  :param int [limit]: the maximum number of trade structures to retrieve
1146
1515
  :param dict [params]: extra parameters specific to the exchange API endpoint
1147
- :param bool [params.stop]: True if fetching trigger or conditional trades
1516
+ :param bool [params.trigger]: True if fetching trigger or conditional trades
1148
1517
  :param str [params.type]: 'spot', 'swap', 'future', 'option', 'ANY', 'SPOT', 'MARGIN', 'SWAP', 'FUTURES' or 'OPTION'
1149
1518
  :param str [params.marginMode]: 'cross' or 'isolated', for automatically setting the type to spot margin
1150
- :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>`
1151
1520
  """
1152
1521
  # By default, receive order updates from any instrument type
1153
1522
  type = None
1154
1523
  type, params = self.handle_option_and_params(params, 'watchMyTrades', 'type', 'ANY')
1155
- isStop = self.safe_bool(params, 'stop', False)
1156
- params = self.omit(params, ['stop'])
1524
+ isTrigger = self.safe_bool_2(params, 'trigger', 'stop', False)
1525
+ params = self.omit(params, ['trigger', 'stop'])
1157
1526
  await self.load_markets()
1158
- await self.authenticate({'access': 'business' if isStop else 'private'})
1159
- 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'
1160
1529
  messageHash = channel + '::myTrades'
1161
1530
  market = None
1162
1531
  if symbol is not None:
@@ -1182,9 +1551,13 @@ class okx(ccxt.async_support.okx):
1182
1551
 
1183
1552
  async def watch_positions(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]:
1184
1553
  """
1185
- :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
+
1186
1557
  watch all open positions
1187
1558
  :param str[]|None symbols: list of unified market symbols
1559
+ @param since
1560
+ @param limit
1188
1561
  :param dict params: extra parameters specific to the exchange API endpoint
1189
1562
  :returns dict[]: a list of `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
1190
1563
  """
@@ -1201,7 +1574,7 @@ class okx(ccxt.async_support.okx):
1201
1574
  'channel': 'positions',
1202
1575
  'instType': 'ANY',
1203
1576
  }
1204
- args = [arg]
1577
+ args = [self.extend(arg, params)]
1205
1578
  nonSymbolRequest: dict = {
1206
1579
  'op': 'subscribe',
1207
1580
  'args': args,
@@ -1282,6 +1655,9 @@ class okx(ccxt.async_support.okx):
1282
1655
  # }
1283
1656
  #
1284
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']
1285
1661
  channel = self.safe_string(arg, 'channel', '')
1286
1662
  data = self.safe_value(message, 'data', [])
1287
1663
  if self.positions is None:
@@ -1291,28 +1667,30 @@ class okx(ccxt.async_support.okx):
1291
1667
  for i in range(0, len(data)):
1292
1668
  rawPosition = data[i]
1293
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)
1294
1676
  newPositions.append(position)
1295
1677
  cache.append(position)
1296
- messageHashes = self.find_message_hashes(client, channel + '::')
1297
- for i in range(0, len(messageHashes)):
1298
- messageHash = messageHashes[i]
1299
- parts = messageHash.split('::')
1300
- symbolsString = parts[1]
1301
- symbols = symbolsString.split(',')
1302
- positions = self.filter_by_array(newPositions, 'symbol', symbols, False)
1303
- if not self.is_empty(positions):
1304
- client.resolve(positions, messageHash)
1305
- client.resolve(newPositions, channel)
1678
+ messageHash = channel
1679
+ if symbol is not None:
1680
+ messageHash = channel + '::' + symbol
1681
+ client.resolve(newPositions, messageHash)
1306
1682
 
1307
1683
  async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1308
1684
  """
1309
1685
  watches information on multiple orders made by the user
1310
- :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
+
1311
1689
  :param str [symbol]: unified market symbol of the market the orders were made in
1312
1690
  :param int [since]: the earliest time in ms to fetch orders for
1313
1691
  :param int [limit]: the maximum number of order structures to retrieve
1314
1692
  :param dict [params]: extra parameters specific to the exchange API endpoint
1315
- :param bool [params.stop]: True if fetching trigger or conditional orders
1693
+ :param bool [params.trigger]: True if fetching trigger or conditional orders
1316
1694
  :param str [params.type]: 'spot', 'swap', 'future', 'option', 'ANY', 'SPOT', 'MARGIN', 'SWAP', 'FUTURES' or 'OPTION'
1317
1695
  :param str [params.marginMode]: 'cross' or 'isolated', for automatically setting the type to spot margin
1318
1696
  :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
@@ -1320,10 +1698,10 @@ class okx(ccxt.async_support.okx):
1320
1698
  type = None
1321
1699
  # By default, receive order updates from any instrument type
1322
1700
  type, params = self.handle_option_and_params(params, 'watchOrders', 'type', 'ANY')
1323
- isStop = self.safe_value_2(params, 'stop', 'trigger', False)
1701
+ isTrigger = self.safe_value_2(params, 'stop', 'trigger', False)
1324
1702
  params = self.omit(params, ['stop', 'trigger'])
1325
1703
  await self.load_markets()
1326
- await self.authenticate({'access': 'business' if isStop else 'private'})
1704
+ await self.authenticate({'access': 'business' if isTrigger else 'private'})
1327
1705
  market = None
1328
1706
  if symbol is not None:
1329
1707
  market = self.market(symbol)
@@ -1340,7 +1718,7 @@ class okx(ccxt.async_support.okx):
1340
1718
  request: dict = {
1341
1719
  'instType': uppercaseType,
1342
1720
  }
1343
- channel = 'orders-algo' if isStop else 'orders'
1721
+ channel = 'orders-algo' if isTrigger else 'orders'
1344
1722
  orders = await self.subscribe('private', channel, channel, symbol, self.extend(request, params))
1345
1723
  if self.newUpdates:
1346
1724
  limit = orders.getLimit(symbol, limit)
@@ -1510,17 +1888,25 @@ class okx(ccxt.async_support.okx):
1510
1888
  tradeSymbols = list(symbols.keys())
1511
1889
  for i in range(0, len(tradeSymbols)):
1512
1890
  symbolMessageHash = messageHash + '::' + tradeSymbols[i]
1513
- 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
1514
1898
 
1515
1899
  async def create_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}) -> Order:
1516
1900
  """
1517
- :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
+
1518
1904
  create a trade order
1519
1905
  :param str symbol: unified symbol of the market to create an order in
1520
1906
  :param str type: 'market' or 'limit'
1521
1907
  :param str side: 'buy' or 'sell'
1522
1908
  :param float amount: how much of currency you want to trade in units of base currency
1523
- :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
1524
1910
  :param dict [params]: extra parameters specific to the exchange API endpoint
1525
1911
  :param boolean params['test']: test order, default False
1526
1912
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
@@ -1528,7 +1914,7 @@ class okx(ccxt.async_support.okx):
1528
1914
  await self.load_markets()
1529
1915
  await self.authenticate()
1530
1916
  url = self.get_url('private', 'private')
1531
- messageHash = str(self.nonce())
1917
+ messageHash = self.request_id()
1532
1918
  op = None
1533
1919
  op, params = self.handle_option_and_params(params, 'createOrderWs', 'op', 'batch-orders')
1534
1920
  args = self.create_order_request(symbol, type, side, amount, price, params)
@@ -1571,7 +1957,7 @@ class okx(ccxt.async_support.okx):
1571
1957
  if self.is_empty(args):
1572
1958
  method = self.safe_string(message, 'op')
1573
1959
  stringMsg = self.json(message)
1574
- self.handle_errors(None, None, client.url, method, None, stringMsg, stringMsg, None, None)
1960
+ self.handle_errors(1, '', client.url, method, {}, stringMsg, message, {}, {})
1575
1961
  orders = self.parse_orders(args, None, None, None)
1576
1962
  first = self.safe_dict(orders, 0, {})
1577
1963
  client.resolve(first, messageHash)
@@ -1579,21 +1965,23 @@ class okx(ccxt.async_support.okx):
1579
1965
  async def edit_order_ws(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}) -> Order:
1580
1966
  """
1581
1967
  edit a trade order
1582
- :see: https://www.okx.com/docs-v5/en/#order-book-trading-trade-ws-amend-order
1583
- :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
+
1584
1972
  :param str id: order id
1585
1973
  :param str symbol: unified symbol of the market to create an order in
1586
1974
  :param str type: 'market' or 'limit'
1587
1975
  :param str side: 'buy' or 'sell'
1588
1976
  :param float amount: how much of the currency you want to trade in units of the base currency
1589
- :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
1590
1978
  :param dict [params]: extra parameters specific to the exchange API endpoint
1591
1979
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1592
1980
  """
1593
1981
  await self.load_markets()
1594
1982
  await self.authenticate()
1595
1983
  url = self.get_url('private', 'private')
1596
- messageHash = str(self.nonce())
1984
+ messageHash = self.request_id()
1597
1985
  op = None
1598
1986
  op, params = self.handle_option_and_params(params, 'editOrderWs', 'op', 'amend-order')
1599
1987
  args = self.edit_order_request(id, symbol, type, side, amount, price, params)
@@ -1606,7 +1994,9 @@ class okx(ccxt.async_support.okx):
1606
1994
 
1607
1995
  async def cancel_order_ws(self, id: str, symbol: Str = None, params={}) -> Order:
1608
1996
  """
1609
- :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
+
1610
2000
  cancel multiple orders
1611
2001
  :param str id: order id
1612
2002
  :param str symbol: unified market symbol, default is None
@@ -1619,7 +2009,7 @@ class okx(ccxt.async_support.okx):
1619
2009
  await self.load_markets()
1620
2010
  await self.authenticate()
1621
2011
  url = self.get_url('private', 'private')
1622
- messageHash = str(self.nonce())
2012
+ messageHash = self.request_id()
1623
2013
  clientOrderId = self.safe_string_2(params, 'clOrdId', 'clientOrderId')
1624
2014
  params = self.omit(params, ['clientOrderId', 'clOrdId'])
1625
2015
  arg: dict = {
@@ -1638,14 +2028,16 @@ class okx(ccxt.async_support.okx):
1638
2028
 
1639
2029
  async def cancel_orders_ws(self, ids: List[str], symbol: Str = None, params={}):
1640
2030
  """
1641
- :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
+
1642
2034
  cancel multiple orders
1643
2035
  :param str[] ids: order ids
1644
2036
  :param str symbol: unified market symbol, default is None
1645
2037
  :param dict [params]: extra parameters specific to the exchange API endpoint
1646
2038
  :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1647
2039
  """
1648
- idsLength = len(ids)
2040
+ idsLength: number = len(ids)
1649
2041
  if idsLength > 20:
1650
2042
  raise BadRequest(self.id + ' cancelOrdersWs() accepts up to 20 ids at a time')
1651
2043
  if symbol is None:
@@ -1653,7 +2045,7 @@ class okx(ccxt.async_support.okx):
1653
2045
  await self.load_markets()
1654
2046
  await self.authenticate()
1655
2047
  url = self.get_url('private', 'private')
1656
- messageHash = str(self.nonce())
2048
+ messageHash = self.request_id()
1657
2049
  args = []
1658
2050
  for i in range(0, idsLength):
1659
2051
  arg: dict = {
@@ -1670,7 +2062,9 @@ class okx(ccxt.async_support.okx):
1670
2062
 
1671
2063
  async def cancel_all_orders_ws(self, symbol: Str = None, params={}):
1672
2064
  """
1673
- :see: https://docs.okx.com/websockets/#message-cancelAll
2065
+
2066
+ https://docs.okx.com/websockets/#message-cancelAll
2067
+
1674
2068
  cancel all open orders of a type. Only applicable to Option in Portfolio Margin mode, and MMP privilege is required.
1675
2069
  :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
1676
2070
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -1682,9 +2076,9 @@ class okx(ccxt.async_support.okx):
1682
2076
  await self.authenticate()
1683
2077
  market = self.market(symbol)
1684
2078
  if market['type'] != 'option':
1685
- raise BadRequest(self.id + 'cancelAllOrdersWs is only applicable to Option in Portfolio Margin mode, and MMP privilege is required.')
2079
+ raise BadRequest(self.id + ' cancelAllOrdersWs is only applicable to Option in Portfolio Margin mode, and MMP privilege is required.')
1686
2080
  url = self.get_url('private', 'private')
1687
- messageHash = str(self.nonce())
2081
+ messageHash = self.request_id()
1688
2082
  request: dict = {
1689
2083
  'id': messageHash,
1690
2084
  'op': 'mass-cancel',
@@ -1728,33 +2122,58 @@ class okx(ccxt.async_support.okx):
1728
2122
  future = self.safe_value(client.futures, 'authenticated')
1729
2123
  future.resolve(True)
1730
2124
 
1731
- def ping(self, client):
1732
- # okex does not support built-in ws protocol-level ping-pong
1733
- # 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.
1734
2128
  return 'ping'
1735
2129
 
1736
2130
  def handle_pong(self, client: Client, message):
1737
2131
  client.lastPong = self.milliseconds()
1738
2132
  return message
1739
2133
 
1740
- def handle_error_message(self, client: Client, message):
2134
+ def handle_error_message(self, client: Client, message) -> Bool:
1741
2135
  #
1742
2136
  # {event: 'error', msg: "Illegal request: {"op":"subscribe","args":["spot/ticker:BTC-USDT"]}", code: "60012"}
1743
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"}
1744
2139
  #
1745
2140
  errorCode = self.safe_string(message, 'code')
1746
2141
  try:
1747
2142
  if errorCode and errorCode != '0':
1748
2143
  feedback = self.id + ' ' + self.json(message)
1749
- 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)
1750
2146
  messageString = self.safe_value(message, 'msg')
1751
2147
  if messageString is not None:
1752
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)
1753
2159
  raise ExchangeError(feedback)
1754
2160
  except Exception as e:
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)
2173
+ return False
1755
2174
  client.reject(e)
1756
2175
  return False
1757
- return message
2176
+ return True
1758
2177
 
1759
2178
  def handle_message(self, client: Client, message):
1760
2179
  if not self.handle_error_message(client, message):
@@ -1810,6 +2229,7 @@ class okx(ccxt.async_support.okx):
1810
2229
  # 'book': 'handleOrderBook',
1811
2230
  'login': self.handle_authenticate,
1812
2231
  'subscribe': self.handle_subscription_status,
2232
+ 'unsubscribe': self.handle_unsubscription,
1813
2233
  'order': self.handle_place_orders,
1814
2234
  'batch-orders': self.handle_place_orders,
1815
2235
  'amend-order': self.handle_place_orders,
@@ -1830,6 +2250,7 @@ class okx(ccxt.async_support.okx):
1830
2250
  'books50-l2-tbt': self.handle_order_book, # only users who're VIP4 and above can subscribe, identity verification required before subscription
1831
2251
  'books-l2-tbt': self.handle_order_book, # only users who're VIP5 and above can subscribe, identity verification required before subscription
1832
2252
  'tickers': self.handle_ticker,
2253
+ 'mark-price': self.handle_ticker,
1833
2254
  'positions': self.handle_positions,
1834
2255
  'index-tickers': self.handle_ticker,
1835
2256
  'sprd-tickers': self.handle_ticker,
@@ -1849,3 +2270,57 @@ class okx(ccxt.async_support.okx):
1849
2270
  self.handle_ohlcv(client, message)
1850
2271
  else:
1851
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)