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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (772) hide show
  1. ccxt/__init__.py +358 -0
  2. ccxt/abantether.py +316 -0
  3. ccxt/abstract/__init__.py +0 -0
  4. ccxt/abstract/abantether.py +5 -0
  5. ccxt/abstract/ace.py +15 -0
  6. ccxt/abstract/afratether.py +6 -0
  7. ccxt/abstract/alpaca.py +70 -0
  8. ccxt/abstract/arzinja.py +5 -0
  9. ccxt/abstract/arzplus.py +7 -0
  10. ccxt/abstract/ascendex.py +77 -0
  11. ccxt/abstract/bequant.py +115 -0
  12. ccxt/abstract/bigone.py +45 -0
  13. ccxt/abstract/binance.py +712 -0
  14. ccxt/abstract/binancecoinm.py +712 -0
  15. ccxt/abstract/binanceus.py +764 -0
  16. ccxt/abstract/binanceusdm.py +712 -0
  17. ccxt/abstract/bingx.py +113 -0
  18. ccxt/abstract/bit2c.py +27 -0
  19. ccxt/abstract/bitbank.py +27 -0
  20. ccxt/abstract/bitbay.py +53 -0
  21. ccxt/abstract/bitbns.py +40 -0
  22. ccxt/abstract/bitcoincom.py +115 -0
  23. ccxt/abstract/bitfinex.py +69 -0
  24. ccxt/abstract/bitfinex2.py +139 -0
  25. ccxt/abstract/bitflyer.py +38 -0
  26. ccxt/abstract/bitget.py +508 -0
  27. ccxt/abstract/bithumb.py +32 -0
  28. ccxt/abstract/bitimen.py +7 -0
  29. ccxt/abstract/bitir.py +7 -0
  30. ccxt/abstract/bitmart.py +99 -0
  31. ccxt/abstract/bitmex.py +97 -0
  32. ccxt/abstract/bitopro.py +29 -0
  33. ccxt/abstract/bitpanda.py +35 -0
  34. ccxt/abstract/bitpin.py +7 -0
  35. ccxt/abstract/bitrue.py +72 -0
  36. ccxt/abstract/bitso.py +43 -0
  37. ccxt/abstract/bitstamp.py +258 -0
  38. ccxt/abstract/bitteam.py +29 -0
  39. ccxt/abstract/bitvavo.py +27 -0
  40. ccxt/abstract/bl3p.py +19 -0
  41. ccxt/abstract/blockchaincom.py +28 -0
  42. ccxt/abstract/blofin.py +37 -0
  43. ccxt/abstract/btcalpha.py +18 -0
  44. ccxt/abstract/btcbox.py +13 -0
  45. ccxt/abstract/btcmarkets.py +39 -0
  46. ccxt/abstract/btcturk.py +20 -0
  47. ccxt/abstract/bybit.py +298 -0
  48. ccxt/abstract/cex.py +33 -0
  49. ccxt/abstract/coinbase.py +94 -0
  50. ccxt/abstract/coinbaseadvanced.py +94 -0
  51. ccxt/abstract/coinbaseexchange.py +67 -0
  52. ccxt/abstract/coinbaseinternational.py +39 -0
  53. ccxt/abstract/coincatch.py +94 -0
  54. ccxt/abstract/coincheck.py +33 -0
  55. ccxt/abstract/coinex.py +237 -0
  56. ccxt/abstract/coinlist.py +54 -0
  57. ccxt/abstract/coinmate.py +62 -0
  58. ccxt/abstract/coinmetro.py +34 -0
  59. ccxt/abstract/coinone.py +67 -0
  60. ccxt/abstract/coinsph.py +54 -0
  61. ccxt/abstract/coinspot.py +28 -0
  62. ccxt/abstract/cryptocom.py +107 -0
  63. ccxt/abstract/currencycom.py +68 -0
  64. ccxt/abstract/delta.py +50 -0
  65. ccxt/abstract/deribit.py +125 -0
  66. ccxt/abstract/digifinex.py +91 -0
  67. ccxt/abstract/eterex.py +5 -0
  68. ccxt/abstract/excoino.py +7 -0
  69. ccxt/abstract/exir.py +8 -0
  70. ccxt/abstract/exmo.py +55 -0
  71. ccxt/abstract/exnovin.py +6 -0
  72. ccxt/abstract/farhadexchange.py +5 -0
  73. ccxt/abstract/fmfwio.py +115 -0
  74. ccxt/abstract/gate.py +265 -0
  75. ccxt/abstract/gateio.py +265 -0
  76. ccxt/abstract/gemini.py +58 -0
  77. ccxt/abstract/hashkey.py +67 -0
  78. ccxt/abstract/hitbtc.py +115 -0
  79. ccxt/abstract/hitbtc3.py +115 -0
  80. ccxt/abstract/hitobit.py +8 -0
  81. ccxt/abstract/hollaex.py +33 -0
  82. ccxt/abstract/htx.py +548 -0
  83. ccxt/abstract/huobi.py +548 -0
  84. ccxt/abstract/huobijp.py +114 -0
  85. ccxt/abstract/hyperliquid.py +6 -0
  86. ccxt/abstract/idex.py +26 -0
  87. ccxt/abstract/independentreserve.py +37 -0
  88. ccxt/abstract/indodax.py +26 -0
  89. ccxt/abstract/jibitex.py +7 -0
  90. ccxt/abstract/kraken.py +57 -0
  91. ccxt/abstract/krakenfutures.py +38 -0
  92. ccxt/abstract/kucoin.py +214 -0
  93. ccxt/abstract/kucoinfutures.py +233 -0
  94. ccxt/abstract/kuna.py +182 -0
  95. ccxt/abstract/latoken.py +56 -0
  96. ccxt/abstract/lbank.py +61 -0
  97. ccxt/abstract/luno.py +37 -0
  98. ccxt/abstract/lykke.py +29 -0
  99. ccxt/abstract/mercado.py +25 -0
  100. ccxt/abstract/mexc.py +178 -0
  101. ccxt/abstract/ndax.py +97 -0
  102. ccxt/abstract/nobitex.py +7 -0
  103. ccxt/abstract/novadax.py +29 -0
  104. ccxt/abstract/oceanex.py +22 -0
  105. ccxt/abstract/okcoin.py +74 -0
  106. ccxt/abstract/okexchange.py +8 -0
  107. ccxt/abstract/okx.py +324 -0
  108. ccxt/abstract/ompfinex.py +7 -0
  109. ccxt/abstract/onetrading.py +35 -0
  110. ccxt/abstract/oxfun.py +34 -0
  111. ccxt/abstract/p2b.py +22 -0
  112. ccxt/abstract/paradex.py +40 -0
  113. ccxt/abstract/paymium.py +28 -0
  114. ccxt/abstract/phemex.py +115 -0
  115. ccxt/abstract/poloniex.py +69 -0
  116. ccxt/abstract/poloniexfutures.py +48 -0
  117. ccxt/abstract/probit.py +23 -0
  118. ccxt/abstract/ramzinex.py +7 -0
  119. ccxt/abstract/sarmayex.py +5 -0
  120. ccxt/abstract/sarrafex.py +7 -0
  121. ccxt/abstract/tabdeal.py +7 -0
  122. ccxt/abstract/tetherland.py +5 -0
  123. ccxt/abstract/timex.py +62 -0
  124. ccxt/abstract/tokocrypto.py +37 -0
  125. ccxt/abstract/tradeogre.py +16 -0
  126. ccxt/abstract/twox.py +5 -0
  127. ccxt/abstract/ubitex.py +7 -0
  128. ccxt/abstract/upbit.py +38 -0
  129. ccxt/abstract/vertex.py +19 -0
  130. ccxt/abstract/wallex.py +8 -0
  131. ccxt/abstract/wavesexchange.py +154 -0
  132. ccxt/abstract/wazirx.py +30 -0
  133. ccxt/abstract/whitebit.py +98 -0
  134. ccxt/abstract/woo.py +83 -0
  135. ccxt/abstract/woofipro.py +119 -0
  136. ccxt/abstract/xt.py +152 -0
  137. ccxt/abstract/yobit.py +16 -0
  138. ccxt/abstract/zaif.py +38 -0
  139. ccxt/abstract/zonda.py +53 -0
  140. ccxt/ace.py +1012 -0
  141. ccxt/afratether.py +293 -0
  142. ccxt/alpaca.py +1083 -0
  143. ccxt/arzinja.py +285 -0
  144. ccxt/arzplus.py +412 -0
  145. ccxt/ascendex.py +3330 -0
  146. ccxt/async_support/__init__.py +337 -0
  147. ccxt/async_support/abantether.py +316 -0
  148. ccxt/async_support/ace.py +1012 -0
  149. ccxt/async_support/afratether.py +293 -0
  150. ccxt/async_support/alpaca.py +1083 -0
  151. ccxt/async_support/arzinja.py +285 -0
  152. ccxt/async_support/arzplus.py +412 -0
  153. ccxt/async_support/ascendex.py +3330 -0
  154. ccxt/async_support/base/__init__.py +1 -0
  155. ccxt/async_support/base/exchange.py +1966 -0
  156. ccxt/async_support/base/throttler.py +50 -0
  157. ccxt/async_support/base/ws/__init__.py +38 -0
  158. ccxt/async_support/base/ws/aiohttp_client.py +125 -0
  159. ccxt/async_support/base/ws/cache.py +212 -0
  160. ccxt/async_support/base/ws/client.py +193 -0
  161. ccxt/async_support/base/ws/fast_client.py +96 -0
  162. ccxt/async_support/base/ws/functions.py +59 -0
  163. ccxt/async_support/base/ws/future.py +58 -0
  164. ccxt/async_support/base/ws/order_book.py +78 -0
  165. ccxt/async_support/base/ws/order_book_side.py +174 -0
  166. ccxt/async_support/bequant.py +33 -0
  167. ccxt/async_support/bigone.py +2113 -0
  168. ccxt/async_support/binance.py +12234 -0
  169. ccxt/async_support/binancecoinm.py +45 -0
  170. ccxt/async_support/binanceus.py +211 -0
  171. ccxt/async_support/binanceusdm.py +58 -0
  172. ccxt/async_support/bingx.py +4325 -0
  173. ccxt/async_support/bit2c.py +866 -0
  174. ccxt/async_support/bitbank.py +1001 -0
  175. ccxt/async_support/bitbay.py +17 -0
  176. ccxt/async_support/bitbns.py +1154 -0
  177. ccxt/async_support/bitcoincom.py +17 -0
  178. ccxt/async_support/bitfinex.py +1617 -0
  179. ccxt/async_support/bitfinex2.py +3552 -0
  180. ccxt/async_support/bitflyer.py +995 -0
  181. ccxt/async_support/bitget.py +8273 -0
  182. ccxt/async_support/bithumb.py +1061 -0
  183. ccxt/async_support/bitimen.py +401 -0
  184. ccxt/async_support/bitir.py +490 -0
  185. ccxt/async_support/bitmart.py +4415 -0
  186. ccxt/async_support/bitmex.py +2756 -0
  187. ccxt/async_support/bitopro.py +1630 -0
  188. ccxt/async_support/bitpanda.py +16 -0
  189. ccxt/async_support/bitpin.py +454 -0
  190. ccxt/async_support/bitrue.py +3027 -0
  191. ccxt/async_support/bitso.py +1670 -0
  192. ccxt/async_support/bitstamp.py +2203 -0
  193. ccxt/async_support/bitteam.py +2239 -0
  194. ccxt/async_support/bitvavo.py +1968 -0
  195. ccxt/async_support/bl3p.py +485 -0
  196. ccxt/async_support/blockchaincom.py +1104 -0
  197. ccxt/async_support/blofin.py +2066 -0
  198. ccxt/async_support/btcalpha.py +891 -0
  199. ccxt/async_support/btcbox.py +544 -0
  200. ccxt/async_support/btcmarkets.py +1221 -0
  201. ccxt/async_support/btcturk.py +911 -0
  202. ccxt/async_support/bybit.py +8159 -0
  203. ccxt/async_support/cex.py +1605 -0
  204. ccxt/async_support/coinbase.py +4475 -0
  205. ccxt/async_support/coinbaseadvanced.py +17 -0
  206. ccxt/async_support/coinbaseexchange.py +1734 -0
  207. ccxt/async_support/coinbaseinternational.py +1899 -0
  208. ccxt/async_support/coincatch.py +5069 -0
  209. ccxt/async_support/coincheck.py +815 -0
  210. ccxt/async_support/coinex.py +5526 -0
  211. ccxt/async_support/coinlist.py +2243 -0
  212. ccxt/async_support/coinmate.py +1067 -0
  213. ccxt/async_support/coinmetro.py +1797 -0
  214. ccxt/async_support/coinone.py +1127 -0
  215. ccxt/async_support/coinsph.py +1850 -0
  216. ccxt/async_support/coinspot.py +534 -0
  217. ccxt/async_support/cryptocom.py +2822 -0
  218. ccxt/async_support/currencycom.py +1950 -0
  219. ccxt/async_support/delta.py +3376 -0
  220. ccxt/async_support/deribit.py +3437 -0
  221. ccxt/async_support/digifinex.py +3960 -0
  222. ccxt/async_support/eterex.py +286 -0
  223. ccxt/async_support/excoino.py +399 -0
  224. ccxt/async_support/exir.py +375 -0
  225. ccxt/async_support/exmo.py +2462 -0
  226. ccxt/async_support/exnovin.py +360 -0
  227. ccxt/async_support/farhadexchange.py +266 -0
  228. ccxt/async_support/fmfwio.py +34 -0
  229. ccxt/async_support/gate.py +6976 -0
  230. ccxt/async_support/gateio.py +16 -0
  231. ccxt/async_support/gemini.py +1825 -0
  232. ccxt/async_support/hashkey.py +4150 -0
  233. ccxt/async_support/hitbtc.py +3423 -0
  234. ccxt/async_support/hitbtc3.py +16 -0
  235. ccxt/async_support/hitobit.py +391 -0
  236. ccxt/async_support/hollaex.py +1813 -0
  237. ccxt/async_support/htx.py +8506 -0
  238. ccxt/async_support/huobi.py +16 -0
  239. ccxt/async_support/huobijp.py +1801 -0
  240. ccxt/async_support/hyperliquid.py +2431 -0
  241. ccxt/async_support/idex.py +1766 -0
  242. ccxt/async_support/independentreserve.py +784 -0
  243. ccxt/async_support/indodax.py +1247 -0
  244. ccxt/async_support/jibitex.py +395 -0
  245. ccxt/async_support/kraken.py +2894 -0
  246. ccxt/async_support/krakenfutures.py +2601 -0
  247. ccxt/async_support/kucoin.py +4602 -0
  248. ccxt/async_support/kucoinfutures.py +2698 -0
  249. ccxt/async_support/kuna.py +1841 -0
  250. ccxt/async_support/latoken.py +1664 -0
  251. ccxt/async_support/lbank.py +2683 -0
  252. ccxt/async_support/luno.py +1067 -0
  253. ccxt/async_support/lykke.py +1270 -0
  254. ccxt/async_support/mercado.py +842 -0
  255. ccxt/async_support/mexc.py +5369 -0
  256. ccxt/async_support/ndax.py +2354 -0
  257. ccxt/async_support/nobitex.py +419 -0
  258. ccxt/async_support/novadax.py +1484 -0
  259. ccxt/async_support/oceanex.py +903 -0
  260. ccxt/async_support/okcoin.py +2936 -0
  261. ccxt/async_support/okexchange.py +349 -0
  262. ccxt/async_support/okx.py +7827 -0
  263. ccxt/async_support/ompfinex.py +472 -0
  264. ccxt/async_support/onetrading.py +1911 -0
  265. ccxt/async_support/oxfun.py +2773 -0
  266. ccxt/async_support/p2b.py +1194 -0
  267. ccxt/async_support/paradex.py +2015 -0
  268. ccxt/async_support/paymium.py +564 -0
  269. ccxt/async_support/phemex.py +4473 -0
  270. ccxt/async_support/poloniex.py +2232 -0
  271. ccxt/async_support/poloniexfutures.py +1717 -0
  272. ccxt/async_support/probit.py +1734 -0
  273. ccxt/async_support/ramzinex.py +476 -0
  274. ccxt/async_support/sarmayex.py +357 -0
  275. ccxt/async_support/sarrafex.py +478 -0
  276. ccxt/async_support/tabdeal.py +364 -0
  277. ccxt/async_support/tetherland.py +349 -0
  278. ccxt/async_support/timex.py +1593 -0
  279. ccxt/async_support/tokocrypto.py +2405 -0
  280. ccxt/async_support/tradeogre.py +608 -0
  281. ccxt/async_support/twox.py +326 -0
  282. ccxt/async_support/ubitex.py +409 -0
  283. ccxt/async_support/upbit.py +1833 -0
  284. ccxt/async_support/vertex.py +2922 -0
  285. ccxt/async_support/wallex.py +445 -0
  286. ccxt/async_support/wavesexchange.py +2473 -0
  287. ccxt/async_support/wazirx.py +1224 -0
  288. ccxt/async_support/whitebit.py +2469 -0
  289. ccxt/async_support/woo.py +3114 -0
  290. ccxt/async_support/woofipro.py +2533 -0
  291. ccxt/async_support/xt.py +4454 -0
  292. ccxt/async_support/yobit.py +1283 -0
  293. ccxt/async_support/zaif.py +725 -0
  294. ccxt/async_support/zonda.py +1828 -0
  295. ccxt/base/__init__.py +27 -0
  296. ccxt/base/decimal_to_precision.py +174 -0
  297. ccxt/base/errors.py +242 -0
  298. ccxt/base/exchange.py +5941 -0
  299. ccxt/base/precise.py +287 -0
  300. ccxt/base/types.py +502 -0
  301. ccxt/bequant.py +33 -0
  302. ccxt/bigone.py +2112 -0
  303. ccxt/binance.py +12233 -0
  304. ccxt/binancecoinm.py +45 -0
  305. ccxt/binanceus.py +211 -0
  306. ccxt/binanceusdm.py +58 -0
  307. ccxt/bingx.py +4324 -0
  308. ccxt/bit2c.py +866 -0
  309. ccxt/bitbank.py +1001 -0
  310. ccxt/bitbay.py +17 -0
  311. ccxt/bitbns.py +1154 -0
  312. ccxt/bitcoincom.py +17 -0
  313. ccxt/bitfinex.py +1617 -0
  314. ccxt/bitfinex2.py +3552 -0
  315. ccxt/bitflyer.py +995 -0
  316. ccxt/bitget.py +8272 -0
  317. ccxt/bithumb.py +1061 -0
  318. ccxt/bitimen.py +401 -0
  319. ccxt/bitir.py +490 -0
  320. ccxt/bitmart.py +4415 -0
  321. ccxt/bitmex.py +2756 -0
  322. ccxt/bitopro.py +1630 -0
  323. ccxt/bitpanda.py +16 -0
  324. ccxt/bitpin.py +454 -0
  325. ccxt/bitrue.py +3026 -0
  326. ccxt/bitso.py +1670 -0
  327. ccxt/bitstamp.py +2203 -0
  328. ccxt/bitteam.py +2239 -0
  329. ccxt/bitvavo.py +1968 -0
  330. ccxt/bl3p.py +485 -0
  331. ccxt/blockchaincom.py +1104 -0
  332. ccxt/blofin.py +2066 -0
  333. ccxt/btcalpha.py +891 -0
  334. ccxt/btcbox.py +544 -0
  335. ccxt/btcmarkets.py +1221 -0
  336. ccxt/btcturk.py +911 -0
  337. ccxt/bybit.py +8158 -0
  338. ccxt/cex.py +1605 -0
  339. ccxt/coinbase.py +4474 -0
  340. ccxt/coinbaseadvanced.py +17 -0
  341. ccxt/coinbaseexchange.py +1734 -0
  342. ccxt/coinbaseinternational.py +1899 -0
  343. ccxt/coincatch.py +5069 -0
  344. ccxt/coincheck.py +815 -0
  345. ccxt/coinex.py +5525 -0
  346. ccxt/coinlist.py +2243 -0
  347. ccxt/coinmate.py +1067 -0
  348. ccxt/coinmetro.py +1797 -0
  349. ccxt/coinone.py +1127 -0
  350. ccxt/coinsph.py +1850 -0
  351. ccxt/coinspot.py +534 -0
  352. ccxt/cryptocom.py +2822 -0
  353. ccxt/currencycom.py +1950 -0
  354. ccxt/delta.py +3376 -0
  355. ccxt/deribit.py +3437 -0
  356. ccxt/digifinex.py +3959 -0
  357. ccxt/eterex.py +286 -0
  358. ccxt/excoino.py +399 -0
  359. ccxt/exir.py +375 -0
  360. ccxt/exmo.py +2462 -0
  361. ccxt/exnovin.py +360 -0
  362. ccxt/farhadexchange.py +266 -0
  363. ccxt/fmfwio.py +34 -0
  364. ccxt/gate.py +6975 -0
  365. ccxt/gateio.py +16 -0
  366. ccxt/gemini.py +1824 -0
  367. ccxt/hashkey.py +4150 -0
  368. ccxt/hitbtc.py +3423 -0
  369. ccxt/hitbtc3.py +16 -0
  370. ccxt/hitobit.py +391 -0
  371. ccxt/hollaex.py +1813 -0
  372. ccxt/htx.py +8505 -0
  373. ccxt/huobi.py +16 -0
  374. ccxt/huobijp.py +1801 -0
  375. ccxt/hyperliquid.py +2430 -0
  376. ccxt/idex.py +1766 -0
  377. ccxt/independentreserve.py +784 -0
  378. ccxt/indodax.py +1247 -0
  379. ccxt/jibitex.py +395 -0
  380. ccxt/kraken.py +2894 -0
  381. ccxt/krakenfutures.py +2601 -0
  382. ccxt/kucoin.py +4601 -0
  383. ccxt/kucoinfutures.py +2698 -0
  384. ccxt/kuna.py +1841 -0
  385. ccxt/latoken.py +1664 -0
  386. ccxt/lbank.py +2682 -0
  387. ccxt/luno.py +1067 -0
  388. ccxt/lykke.py +1270 -0
  389. ccxt/mercado.py +842 -0
  390. ccxt/mexc.py +5369 -0
  391. ccxt/ndax.py +2354 -0
  392. ccxt/nobitex.py +419 -0
  393. ccxt/novadax.py +1484 -0
  394. ccxt/oceanex.py +903 -0
  395. ccxt/okcoin.py +2936 -0
  396. ccxt/okexchange.py +349 -0
  397. ccxt/okx.py +7826 -0
  398. ccxt/ompfinex.py +472 -0
  399. ccxt/onetrading.py +1911 -0
  400. ccxt/oxfun.py +2772 -0
  401. ccxt/p2b.py +1194 -0
  402. ccxt/paradex.py +2015 -0
  403. ccxt/paymium.py +564 -0
  404. ccxt/phemex.py +4473 -0
  405. ccxt/poloniex.py +2232 -0
  406. ccxt/poloniexfutures.py +1717 -0
  407. ccxt/pro/__init__.py +149 -0
  408. ccxt/pro/alpaca.py +685 -0
  409. ccxt/pro/ascendex.py +916 -0
  410. ccxt/pro/bequant.py +38 -0
  411. ccxt/pro/binance.py +3488 -0
  412. ccxt/pro/binancecoinm.py +28 -0
  413. ccxt/pro/binanceus.py +48 -0
  414. ccxt/pro/binanceusdm.py +31 -0
  415. ccxt/pro/bingx.py +1264 -0
  416. ccxt/pro/bitcoincom.py +34 -0
  417. ccxt/pro/bitfinex.py +621 -0
  418. ccxt/pro/bitfinex2.py +1083 -0
  419. ccxt/pro/bitget.py +1692 -0
  420. ccxt/pro/bithumb.py +368 -0
  421. ccxt/pro/bitmart.py +1449 -0
  422. ccxt/pro/bitmex.py +1656 -0
  423. ccxt/pro/bitopro.py +445 -0
  424. ccxt/pro/bitpanda.py +15 -0
  425. ccxt/pro/bitrue.py +447 -0
  426. ccxt/pro/bitstamp.py +522 -0
  427. ccxt/pro/bitvavo.py +1270 -0
  428. ccxt/pro/blockchaincom.py +738 -0
  429. ccxt/pro/blofin.py +692 -0
  430. ccxt/pro/bybit.py +2000 -0
  431. ccxt/pro/cex.py +1440 -0
  432. ccxt/pro/coinbase.py +678 -0
  433. ccxt/pro/coinbaseadvanced.py +16 -0
  434. ccxt/pro/coinbaseexchange.py +895 -0
  435. ccxt/pro/coinbaseinternational.py +620 -0
  436. ccxt/pro/coincatch.py +1464 -0
  437. ccxt/pro/coincheck.py +199 -0
  438. ccxt/pro/coinex.py +1061 -0
  439. ccxt/pro/coinone.py +395 -0
  440. ccxt/pro/cryptocom.py +947 -0
  441. ccxt/pro/currencycom.py +536 -0
  442. ccxt/pro/deribit.py +892 -0
  443. ccxt/pro/exmo.py +629 -0
  444. ccxt/pro/gate.py +1416 -0
  445. ccxt/pro/gateio.py +15 -0
  446. ccxt/pro/gemini.py +865 -0
  447. ccxt/pro/hashkey.py +802 -0
  448. ccxt/pro/hitbtc.py +1216 -0
  449. ccxt/pro/hollaex.py +563 -0
  450. ccxt/pro/htx.py +2215 -0
  451. ccxt/pro/huobi.py +15 -0
  452. ccxt/pro/huobijp.py +570 -0
  453. ccxt/pro/hyperliquid.py +525 -0
  454. ccxt/pro/idex.py +672 -0
  455. ccxt/pro/independentreserve.py +270 -0
  456. ccxt/pro/kraken.py +1356 -0
  457. ccxt/pro/krakenfutures.py +1492 -0
  458. ccxt/pro/kucoin.py +1133 -0
  459. ccxt/pro/kucoinfutures.py +1081 -0
  460. ccxt/pro/lbank.py +843 -0
  461. ccxt/pro/luno.py +303 -0
  462. ccxt/pro/mexc.py +1122 -0
  463. ccxt/pro/ndax.py +506 -0
  464. ccxt/pro/okcoin.py +698 -0
  465. ccxt/pro/okx.py +1851 -0
  466. ccxt/pro/onetrading.py +1275 -0
  467. ccxt/pro/oxfun.py +950 -0
  468. ccxt/pro/p2b.py +419 -0
  469. ccxt/pro/paradex.py +352 -0
  470. ccxt/pro/phemex.py +1441 -0
  471. ccxt/pro/poloniex.py +1166 -0
  472. ccxt/pro/poloniexfutures.py +990 -0
  473. ccxt/pro/probit.py +551 -0
  474. ccxt/pro/upbit.py +520 -0
  475. ccxt/pro/vertex.py +943 -0
  476. ccxt/pro/wazirx.py +749 -0
  477. ccxt/pro/whitebit.py +864 -0
  478. ccxt/pro/woo.py +1078 -0
  479. ccxt/pro/woofipro.py +1183 -0
  480. ccxt/pro/xt.py +1067 -0
  481. ccxt/probit.py +1734 -0
  482. ccxt/ramzinex.py +476 -0
  483. ccxt/sarmayex.py +357 -0
  484. ccxt/sarrafex.py +478 -0
  485. ccxt/static_dependencies/__init__.py +1 -0
  486. ccxt/static_dependencies/ecdsa/__init__.py +14 -0
  487. ccxt/static_dependencies/ecdsa/_version.py +520 -0
  488. ccxt/static_dependencies/ecdsa/curves.py +56 -0
  489. ccxt/static_dependencies/ecdsa/der.py +221 -0
  490. ccxt/static_dependencies/ecdsa/ecdsa.py +310 -0
  491. ccxt/static_dependencies/ecdsa/ellipticcurve.py +197 -0
  492. ccxt/static_dependencies/ecdsa/keys.py +332 -0
  493. ccxt/static_dependencies/ecdsa/numbertheory.py +531 -0
  494. ccxt/static_dependencies/ecdsa/rfc6979.py +100 -0
  495. ccxt/static_dependencies/ecdsa/util.py +266 -0
  496. ccxt/static_dependencies/ethereum/__init__.py +7 -0
  497. ccxt/static_dependencies/ethereum/abi/__init__.py +16 -0
  498. ccxt/static_dependencies/ethereum/abi/abi.py +19 -0
  499. ccxt/static_dependencies/ethereum/abi/base.py +152 -0
  500. ccxt/static_dependencies/ethereum/abi/codec.py +217 -0
  501. ccxt/static_dependencies/ethereum/abi/constants.py +3 -0
  502. ccxt/static_dependencies/ethereum/abi/decoding.py +565 -0
  503. ccxt/static_dependencies/ethereum/abi/encoding.py +720 -0
  504. ccxt/static_dependencies/ethereum/abi/exceptions.py +139 -0
  505. ccxt/static_dependencies/ethereum/abi/grammar.py +443 -0
  506. ccxt/static_dependencies/ethereum/abi/packed.py +13 -0
  507. ccxt/static_dependencies/ethereum/abi/py.typed +0 -0
  508. ccxt/static_dependencies/ethereum/abi/registry.py +643 -0
  509. ccxt/static_dependencies/ethereum/abi/tools/__init__.py +3 -0
  510. ccxt/static_dependencies/ethereum/abi/tools/_strategies.py +230 -0
  511. ccxt/static_dependencies/ethereum/abi/utils/__init__.py +0 -0
  512. ccxt/static_dependencies/ethereum/abi/utils/numeric.py +83 -0
  513. ccxt/static_dependencies/ethereum/abi/utils/padding.py +27 -0
  514. ccxt/static_dependencies/ethereum/abi/utils/string.py +19 -0
  515. ccxt/static_dependencies/ethereum/account/__init__.py +3 -0
  516. ccxt/static_dependencies/ethereum/account/encode_typed_data/__init__.py +4 -0
  517. ccxt/static_dependencies/ethereum/account/encode_typed_data/encoding_and_hashing.py +239 -0
  518. ccxt/static_dependencies/ethereum/account/encode_typed_data/helpers.py +40 -0
  519. ccxt/static_dependencies/ethereum/account/messages.py +263 -0
  520. ccxt/static_dependencies/ethereum/account/py.typed +0 -0
  521. ccxt/static_dependencies/ethereum/hexbytes/__init__.py +5 -0
  522. ccxt/static_dependencies/ethereum/hexbytes/_utils.py +54 -0
  523. ccxt/static_dependencies/ethereum/hexbytes/main.py +65 -0
  524. ccxt/static_dependencies/ethereum/hexbytes/py.typed +0 -0
  525. ccxt/static_dependencies/ethereum/typing/__init__.py +63 -0
  526. ccxt/static_dependencies/ethereum/typing/abi.py +6 -0
  527. ccxt/static_dependencies/ethereum/typing/bls.py +7 -0
  528. ccxt/static_dependencies/ethereum/typing/discovery.py +5 -0
  529. ccxt/static_dependencies/ethereum/typing/encoding.py +7 -0
  530. ccxt/static_dependencies/ethereum/typing/enums.py +17 -0
  531. ccxt/static_dependencies/ethereum/typing/ethpm.py +9 -0
  532. ccxt/static_dependencies/ethereum/typing/evm.py +20 -0
  533. ccxt/static_dependencies/ethereum/typing/networks.py +1122 -0
  534. ccxt/static_dependencies/ethereum/typing/py.typed +0 -0
  535. ccxt/static_dependencies/ethereum/utils/__init__.py +115 -0
  536. ccxt/static_dependencies/ethereum/utils/abi.py +72 -0
  537. ccxt/static_dependencies/ethereum/utils/address.py +171 -0
  538. ccxt/static_dependencies/ethereum/utils/applicators.py +151 -0
  539. ccxt/static_dependencies/ethereum/utils/conversions.py +190 -0
  540. ccxt/static_dependencies/ethereum/utils/currency.py +107 -0
  541. ccxt/static_dependencies/ethereum/utils/curried/__init__.py +269 -0
  542. ccxt/static_dependencies/ethereum/utils/debug.py +20 -0
  543. ccxt/static_dependencies/ethereum/utils/decorators.py +132 -0
  544. ccxt/static_dependencies/ethereum/utils/encoding.py +6 -0
  545. ccxt/static_dependencies/ethereum/utils/exceptions.py +4 -0
  546. ccxt/static_dependencies/ethereum/utils/functional.py +75 -0
  547. ccxt/static_dependencies/ethereum/utils/hexadecimal.py +74 -0
  548. ccxt/static_dependencies/ethereum/utils/humanize.py +188 -0
  549. ccxt/static_dependencies/ethereum/utils/logging.py +159 -0
  550. ccxt/static_dependencies/ethereum/utils/module_loading.py +31 -0
  551. ccxt/static_dependencies/ethereum/utils/numeric.py +43 -0
  552. ccxt/static_dependencies/ethereum/utils/py.typed +0 -0
  553. ccxt/static_dependencies/ethereum/utils/toolz.py +76 -0
  554. ccxt/static_dependencies/ethereum/utils/types.py +54 -0
  555. ccxt/static_dependencies/ethereum/utils/typing/__init__.py +18 -0
  556. ccxt/static_dependencies/ethereum/utils/typing/misc.py +14 -0
  557. ccxt/static_dependencies/ethereum/utils/units.py +31 -0
  558. ccxt/static_dependencies/keccak/__init__.py +3 -0
  559. ccxt/static_dependencies/keccak/keccak.py +197 -0
  560. ccxt/static_dependencies/lark/__init__.py +38 -0
  561. ccxt/static_dependencies/lark/__pyinstaller/__init__.py +6 -0
  562. ccxt/static_dependencies/lark/__pyinstaller/hook-lark.py +14 -0
  563. ccxt/static_dependencies/lark/ast_utils.py +59 -0
  564. ccxt/static_dependencies/lark/common.py +86 -0
  565. ccxt/static_dependencies/lark/exceptions.py +292 -0
  566. ccxt/static_dependencies/lark/grammar.py +130 -0
  567. ccxt/static_dependencies/lark/grammars/__init__.py +0 -0
  568. ccxt/static_dependencies/lark/indenter.py +143 -0
  569. ccxt/static_dependencies/lark/lark.py +658 -0
  570. ccxt/static_dependencies/lark/lexer.py +678 -0
  571. ccxt/static_dependencies/lark/load_grammar.py +1428 -0
  572. ccxt/static_dependencies/lark/parse_tree_builder.py +391 -0
  573. ccxt/static_dependencies/lark/parser_frontends.py +257 -0
  574. ccxt/static_dependencies/lark/parsers/__init__.py +0 -0
  575. ccxt/static_dependencies/lark/parsers/cyk.py +340 -0
  576. ccxt/static_dependencies/lark/parsers/earley.py +314 -0
  577. ccxt/static_dependencies/lark/parsers/earley_common.py +42 -0
  578. ccxt/static_dependencies/lark/parsers/earley_forest.py +801 -0
  579. ccxt/static_dependencies/lark/parsers/grammar_analysis.py +203 -0
  580. ccxt/static_dependencies/lark/parsers/lalr_analysis.py +332 -0
  581. ccxt/static_dependencies/lark/parsers/lalr_interactive_parser.py +158 -0
  582. ccxt/static_dependencies/lark/parsers/lalr_parser.py +122 -0
  583. ccxt/static_dependencies/lark/parsers/lalr_parser_state.py +110 -0
  584. ccxt/static_dependencies/lark/parsers/xearley.py +165 -0
  585. ccxt/static_dependencies/lark/reconstruct.py +107 -0
  586. ccxt/static_dependencies/lark/tools/__init__.py +70 -0
  587. ccxt/static_dependencies/lark/tools/nearley.py +202 -0
  588. ccxt/static_dependencies/lark/tools/serialize.py +32 -0
  589. ccxt/static_dependencies/lark/tools/standalone.py +196 -0
  590. ccxt/static_dependencies/lark/tree.py +267 -0
  591. ccxt/static_dependencies/lark/tree_matcher.py +186 -0
  592. ccxt/static_dependencies/lark/tree_templates.py +180 -0
  593. ccxt/static_dependencies/lark/utils.py +343 -0
  594. ccxt/static_dependencies/lark/visitors.py +596 -0
  595. ccxt/static_dependencies/marshmallow/__init__.py +81 -0
  596. ccxt/static_dependencies/marshmallow/base.py +65 -0
  597. ccxt/static_dependencies/marshmallow/class_registry.py +94 -0
  598. ccxt/static_dependencies/marshmallow/decorators.py +231 -0
  599. ccxt/static_dependencies/marshmallow/error_store.py +60 -0
  600. ccxt/static_dependencies/marshmallow/exceptions.py +71 -0
  601. ccxt/static_dependencies/marshmallow/fields.py +2114 -0
  602. ccxt/static_dependencies/marshmallow/orderedset.py +89 -0
  603. ccxt/static_dependencies/marshmallow/schema.py +1228 -0
  604. ccxt/static_dependencies/marshmallow/types.py +12 -0
  605. ccxt/static_dependencies/marshmallow/utils.py +378 -0
  606. ccxt/static_dependencies/marshmallow/validate.py +678 -0
  607. ccxt/static_dependencies/marshmallow/warnings.py +2 -0
  608. ccxt/static_dependencies/marshmallow_dataclass/__init__.py +1047 -0
  609. ccxt/static_dependencies/marshmallow_dataclass/collection_field.py +51 -0
  610. ccxt/static_dependencies/marshmallow_dataclass/lazy_class_attribute.py +45 -0
  611. ccxt/static_dependencies/marshmallow_dataclass/mypy.py +71 -0
  612. ccxt/static_dependencies/marshmallow_dataclass/typing.py +14 -0
  613. ccxt/static_dependencies/marshmallow_dataclass/union_field.py +82 -0
  614. ccxt/static_dependencies/marshmallow_oneofschema/__init__.py +1 -0
  615. ccxt/static_dependencies/marshmallow_oneofschema/one_of_schema.py +193 -0
  616. ccxt/static_dependencies/msgpack/__init__.py +55 -0
  617. ccxt/static_dependencies/msgpack/exceptions.py +48 -0
  618. ccxt/static_dependencies/msgpack/ext.py +168 -0
  619. ccxt/static_dependencies/msgpack/fallback.py +951 -0
  620. ccxt/static_dependencies/parsimonious/__init__.py +10 -0
  621. ccxt/static_dependencies/parsimonious/exceptions.py +105 -0
  622. ccxt/static_dependencies/parsimonious/expressions.py +479 -0
  623. ccxt/static_dependencies/parsimonious/grammar.py +487 -0
  624. ccxt/static_dependencies/parsimonious/nodes.py +325 -0
  625. ccxt/static_dependencies/parsimonious/utils.py +40 -0
  626. ccxt/static_dependencies/starknet/__init__.py +0 -0
  627. ccxt/static_dependencies/starknet/cairo/__init__.py +0 -0
  628. ccxt/static_dependencies/starknet/cairo/data_types.py +123 -0
  629. ccxt/static_dependencies/starknet/cairo/deprecated_parse/__init__.py +0 -0
  630. ccxt/static_dependencies/starknet/cairo/deprecated_parse/cairo_types.py +77 -0
  631. ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser.py +46 -0
  632. ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser_transformer.py +138 -0
  633. ccxt/static_dependencies/starknet/cairo/felt.py +64 -0
  634. ccxt/static_dependencies/starknet/cairo/type_parser.py +121 -0
  635. ccxt/static_dependencies/starknet/cairo/v1/__init__.py +0 -0
  636. ccxt/static_dependencies/starknet/cairo/v1/type_parser.py +59 -0
  637. ccxt/static_dependencies/starknet/cairo/v2/__init__.py +0 -0
  638. ccxt/static_dependencies/starknet/cairo/v2/type_parser.py +77 -0
  639. ccxt/static_dependencies/starknet/ccxt_utils.py +7 -0
  640. ccxt/static_dependencies/starknet/common.py +15 -0
  641. ccxt/static_dependencies/starknet/constants.py +39 -0
  642. ccxt/static_dependencies/starknet/hash/__init__.py +0 -0
  643. ccxt/static_dependencies/starknet/hash/address.py +79 -0
  644. ccxt/static_dependencies/starknet/hash/compiled_class_hash_objects.py +111 -0
  645. ccxt/static_dependencies/starknet/hash/selector.py +16 -0
  646. ccxt/static_dependencies/starknet/hash/storage.py +12 -0
  647. ccxt/static_dependencies/starknet/hash/utils.py +78 -0
  648. ccxt/static_dependencies/starknet/models/__init__.py +0 -0
  649. ccxt/static_dependencies/starknet/models/typed_data.py +45 -0
  650. ccxt/static_dependencies/starknet/serialization/__init__.py +24 -0
  651. ccxt/static_dependencies/starknet/serialization/_calldata_reader.py +40 -0
  652. ccxt/static_dependencies/starknet/serialization/_context.py +142 -0
  653. ccxt/static_dependencies/starknet/serialization/data_serializers/__init__.py +10 -0
  654. ccxt/static_dependencies/starknet/serialization/data_serializers/_common.py +82 -0
  655. ccxt/static_dependencies/starknet/serialization/data_serializers/array_serializer.py +43 -0
  656. ccxt/static_dependencies/starknet/serialization/data_serializers/bool_serializer.py +37 -0
  657. ccxt/static_dependencies/starknet/serialization/data_serializers/byte_array_serializer.py +66 -0
  658. ccxt/static_dependencies/starknet/serialization/data_serializers/cairo_data_serializer.py +71 -0
  659. ccxt/static_dependencies/starknet/serialization/data_serializers/enum_serializer.py +71 -0
  660. ccxt/static_dependencies/starknet/serialization/data_serializers/felt_serializer.py +50 -0
  661. ccxt/static_dependencies/starknet/serialization/data_serializers/named_tuple_serializer.py +58 -0
  662. ccxt/static_dependencies/starknet/serialization/data_serializers/option_serializer.py +43 -0
  663. ccxt/static_dependencies/starknet/serialization/data_serializers/output_serializer.py +40 -0
  664. ccxt/static_dependencies/starknet/serialization/data_serializers/payload_serializer.py +72 -0
  665. ccxt/static_dependencies/starknet/serialization/data_serializers/struct_serializer.py +36 -0
  666. ccxt/static_dependencies/starknet/serialization/data_serializers/tuple_serializer.py +36 -0
  667. ccxt/static_dependencies/starknet/serialization/data_serializers/uint256_serializer.py +76 -0
  668. ccxt/static_dependencies/starknet/serialization/data_serializers/uint_serializer.py +100 -0
  669. ccxt/static_dependencies/starknet/serialization/data_serializers/unit_serializer.py +32 -0
  670. ccxt/static_dependencies/starknet/serialization/errors.py +10 -0
  671. ccxt/static_dependencies/starknet/serialization/factory.py +229 -0
  672. ccxt/static_dependencies/starknet/serialization/function_serialization_adapter.py +110 -0
  673. ccxt/static_dependencies/starknet/serialization/tuple_dataclass.py +59 -0
  674. ccxt/static_dependencies/starknet/utils/__init__.py +0 -0
  675. ccxt/static_dependencies/starknet/utils/constructor_args_translator.py +86 -0
  676. ccxt/static_dependencies/starknet/utils/iterable.py +13 -0
  677. ccxt/static_dependencies/starknet/utils/schema.py +13 -0
  678. ccxt/static_dependencies/starknet/utils/typed_data.py +182 -0
  679. ccxt/static_dependencies/starkware/__init__.py +0 -0
  680. ccxt/static_dependencies/starkware/crypto/__init__.py +0 -0
  681. ccxt/static_dependencies/starkware/crypto/fast_pedersen_hash.py +50 -0
  682. ccxt/static_dependencies/starkware/crypto/math_utils.py +78 -0
  683. ccxt/static_dependencies/starkware/crypto/signature.py +2344 -0
  684. ccxt/static_dependencies/starkware/crypto/utils.py +63 -0
  685. ccxt/static_dependencies/sympy/__init__.py +0 -0
  686. ccxt/static_dependencies/sympy/core/__init__.py +0 -0
  687. ccxt/static_dependencies/sympy/core/intfunc.py +35 -0
  688. ccxt/static_dependencies/sympy/external/__init__.py +0 -0
  689. ccxt/static_dependencies/sympy/external/gmpy.py +345 -0
  690. ccxt/static_dependencies/sympy/external/importtools.py +187 -0
  691. ccxt/static_dependencies/sympy/external/ntheory.py +637 -0
  692. ccxt/static_dependencies/sympy/external/pythonmpq.py +341 -0
  693. ccxt/static_dependencies/toolz/__init__.py +26 -0
  694. ccxt/static_dependencies/toolz/_signatures.py +784 -0
  695. ccxt/static_dependencies/toolz/_version.py +520 -0
  696. ccxt/static_dependencies/toolz/compatibility.py +30 -0
  697. ccxt/static_dependencies/toolz/curried/__init__.py +101 -0
  698. ccxt/static_dependencies/toolz/curried/exceptions.py +22 -0
  699. ccxt/static_dependencies/toolz/curried/operator.py +22 -0
  700. ccxt/static_dependencies/toolz/dicttoolz.py +339 -0
  701. ccxt/static_dependencies/toolz/functoolz.py +1049 -0
  702. ccxt/static_dependencies/toolz/itertoolz.py +1057 -0
  703. ccxt/static_dependencies/toolz/recipes.py +46 -0
  704. ccxt/static_dependencies/toolz/utils.py +9 -0
  705. ccxt/static_dependencies/typing_inspect/__init__.py +0 -0
  706. ccxt/static_dependencies/typing_inspect/typing_inspect.py +851 -0
  707. ccxt/tabdeal.py +364 -0
  708. ccxt/test/__init__.py +3 -0
  709. ccxt/test/base/__init__.py +29 -0
  710. ccxt/test/base/test_account.py +26 -0
  711. ccxt/test/base/test_balance.py +56 -0
  712. ccxt/test/base/test_borrow_interest.py +35 -0
  713. ccxt/test/base/test_borrow_rate.py +32 -0
  714. ccxt/test/base/test_calculate_fee.py +51 -0
  715. ccxt/test/base/test_crypto.py +127 -0
  716. ccxt/test/base/test_currency.py +76 -0
  717. ccxt/test/base/test_datetime.py +109 -0
  718. ccxt/test/base/test_decimal_to_precision.py +392 -0
  719. ccxt/test/base/test_deep_extend.py +68 -0
  720. ccxt/test/base/test_deposit_withdrawal.py +50 -0
  721. ccxt/test/base/test_exchange_datetime_functions.py +76 -0
  722. ccxt/test/base/test_funding_rate_history.py +29 -0
  723. ccxt/test/base/test_last_price.py +31 -0
  724. ccxt/test/base/test_ledger_entry.py +45 -0
  725. ccxt/test/base/test_ledger_item.py +48 -0
  726. ccxt/test/base/test_leverage_tier.py +33 -0
  727. ccxt/test/base/test_liquidation.py +50 -0
  728. ccxt/test/base/test_margin_mode.py +24 -0
  729. ccxt/test/base/test_margin_modification.py +35 -0
  730. ccxt/test/base/test_market.py +193 -0
  731. ccxt/test/base/test_number.py +411 -0
  732. ccxt/test/base/test_ohlcv.py +33 -0
  733. ccxt/test/base/test_open_interest.py +32 -0
  734. ccxt/test/base/test_order.py +64 -0
  735. ccxt/test/base/test_order_book.py +69 -0
  736. ccxt/test/base/test_position.py +60 -0
  737. ccxt/test/base/test_shared_methods.py +353 -0
  738. ccxt/test/base/test_status.py +24 -0
  739. ccxt/test/base/test_throttle.py +126 -0
  740. ccxt/test/base/test_ticker.py +92 -0
  741. ccxt/test/base/test_trade.py +47 -0
  742. ccxt/test/base/test_trading_fee.py +26 -0
  743. ccxt/test/base/test_transaction.py +39 -0
  744. ccxt/test/test_async.py +1649 -0
  745. ccxt/test/test_sync.py +1648 -0
  746. ccxt/test/tests_async.py +1558 -0
  747. ccxt/test/tests_helpers.py +287 -0
  748. ccxt/test/tests_init.py +39 -0
  749. ccxt/test/tests_sync.py +1555 -0
  750. ccxt/tetherland.py +349 -0
  751. ccxt/timex.py +1593 -0
  752. ccxt/tokocrypto.py +2405 -0
  753. ccxt/tradeogre.py +608 -0
  754. ccxt/twox.py +326 -0
  755. ccxt/ubitex.py +409 -0
  756. ccxt/upbit.py +1833 -0
  757. ccxt/vertex.py +2922 -0
  758. ccxt/wallex.py +445 -0
  759. ccxt/wavesexchange.py +2472 -0
  760. ccxt/wazirx.py +1224 -0
  761. ccxt/whitebit.py +2469 -0
  762. ccxt/woo.py +3114 -0
  763. ccxt/woofipro.py +2533 -0
  764. ccxt/xt.py +4453 -0
  765. ccxt/yobit.py +1283 -0
  766. ccxt/zaif.py +725 -0
  767. ccxt/zonda.py +1828 -0
  768. ccxt_ir-4.3.46.0.1.dist-info/LICENSE.txt +21 -0
  769. ccxt_ir-4.3.46.0.1.dist-info/METADATA +655 -0
  770. ccxt_ir-4.3.46.0.1.dist-info/RECORD +772 -0
  771. ccxt_ir-4.3.46.0.1.dist-info/WHEEL +6 -0
  772. ccxt_ir-4.3.46.0.1.dist-info/top_level.txt +1 -0
ccxt/pro/htx.py ADDED
@@ -0,0 +1,2215 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
4
+ # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
5
+
6
+ import ccxt.async_support
7
+ from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide, ArrayCacheByTimestamp
8
+ import hashlib
9
+ from ccxt.base.types import Balances, Int, Order, OrderBook, Position, Str, Strings, Ticker, Trade
10
+ from ccxt.async_support.base.ws.client import Client
11
+ from typing import List
12
+ from ccxt.base.errors import ExchangeError
13
+ from ccxt.base.errors import AuthenticationError
14
+ from ccxt.base.errors import ArgumentsRequired
15
+ from ccxt.base.errors import BadRequest
16
+ from ccxt.base.errors import BadSymbol
17
+ from ccxt.base.errors import NetworkError
18
+ from ccxt.base.errors import InvalidNonce
19
+
20
+
21
+ class htx(ccxt.async_support.htx):
22
+
23
+ def describe(self):
24
+ return self.deep_extend(super(htx, self).describe(), {
25
+ 'has': {
26
+ 'ws': True,
27
+ 'createOrderWs': False,
28
+ 'editOrderWs': False,
29
+ 'fetchOpenOrdersWs': False,
30
+ 'fetchOrderWs': False,
31
+ 'cancelOrderWs': False,
32
+ 'cancelOrdersWs': False,
33
+ 'cancelAllOrdersWs': False,
34
+ 'fetchTradesWs': False,
35
+ 'fetchBalanceWs': False,
36
+ 'watchOrderBook': True,
37
+ 'watchOrders': True,
38
+ 'watchTickers': False,
39
+ 'watchTicker': True,
40
+ 'watchTrades': True,
41
+ 'watchMyTrades': True,
42
+ 'watchBalance': True,
43
+ 'watchOHLCV': True,
44
+ },
45
+ 'urls': {
46
+ 'api': {
47
+ 'ws': {
48
+ 'api': {
49
+ 'spot': {
50
+ 'public': 'wss://{hostname}/ws',
51
+ 'private': 'wss://{hostname}/ws/v2',
52
+ 'feed': 'wss://{hostname}/feed',
53
+ },
54
+ 'future': {
55
+ 'linear': {
56
+ 'public': 'wss://api.hbdm.com/linear-swap-ws',
57
+ 'private': 'wss://api.hbdm.com/linear-swap-notification',
58
+ },
59
+ 'inverse': {
60
+ 'public': 'wss://api.hbdm.com/ws',
61
+ 'private': 'wss://api.hbdm.com/notification',
62
+ },
63
+ },
64
+ 'swap': {
65
+ 'inverse': {
66
+ 'public': 'wss://api.hbdm.com/swap-ws',
67
+ 'private': 'wss://api.hbdm.com/swap-notification',
68
+ },
69
+ 'linear': {
70
+ 'public': 'wss://api.hbdm.com/linear-swap-ws',
71
+ 'private': 'wss://api.hbdm.com/linear-swap-notification',
72
+ },
73
+ },
74
+ },
75
+ # these settings work faster for clients hosted on AWS
76
+ 'api-aws': {
77
+ 'spot': {
78
+ 'public': 'wss://api-aws.huobi.pro/ws',
79
+ 'private': 'wss://api-aws.huobi.pro/ws/v2',
80
+ 'feed': 'wss://{hostname}/feed',
81
+ },
82
+ 'future': {
83
+ 'linear': {
84
+ 'public': 'wss://api.hbdm.vn/linear-swap-ws',
85
+ 'private': 'wss://api.hbdm.vn/linear-swap-notification',
86
+ },
87
+ 'inverse': {
88
+ 'public': 'wss://api.hbdm.vn/ws',
89
+ 'private': 'wss://api.hbdm.vn/notification',
90
+ },
91
+ },
92
+ 'swap': {
93
+ 'linear': {
94
+ 'public': 'wss://api.hbdm.vn/linear-swap-ws',
95
+ 'private': 'wss://api.hbdm.vn/linear-swap-notification',
96
+ },
97
+ 'inverse': {
98
+ 'public': 'wss://api.hbdm.vn/swap-ws',
99
+ 'private': 'wss://api.hbdm.vn/swap-notification',
100
+ },
101
+ },
102
+ },
103
+ },
104
+ },
105
+ },
106
+ 'options': {
107
+ 'tradesLimit': 1000,
108
+ 'OHLCVLimit': 1000,
109
+ 'api': 'api', # or api-aws for clients hosted on AWS
110
+ 'watchOrderBook': {
111
+ 'maxRetries': 3,
112
+ },
113
+ 'ws': {
114
+ 'gunzip': True,
115
+ },
116
+ 'watchTicker': {
117
+ 'name': 'market.{marketId}.detail', # 'market.{marketId}.bbo' or 'market.{marketId}.ticker'
118
+ },
119
+ },
120
+ 'exceptions': {
121
+ 'ws': {
122
+ 'exact': {
123
+ 'bad-request': BadRequest, # { ts: 1586323747018, status: 'error', 'err-code': 'bad-request', err-msg': 'invalid mbp.150.symbol linkusdt', id: '2'}
124
+ '2002': AuthenticationError, # {action: 'sub', code: 2002, ch: 'accounts.update#2', message: 'invalid.auth.state'}
125
+ '2021': BadRequest,
126
+ '2001': BadSymbol, # {action: 'sub', code: 2001, ch: 'orders#2ltcusdt', message: 'invalid.symbol'}
127
+ '2011': BadSymbol, # {op: 'sub', cid: '1649149285', topic: 'orders_cross.hereltc-usdt', 'err-code': 2011, 'err-msg': "Contract doesn't exist.", ts: 1649149287637}
128
+ '2040': BadRequest, # {op: 'sub', cid: '1649152947', 'err-code': 2040, 'err-msg': 'Missing required parameter.', ts: 1649152948684}
129
+ '4007': BadRequest, # {op: 'sub', cid: '1', topic: 'accounts_unify.USDT', 'err-code': 4007, 'err-msg': 'Non - single account user is not available, please check through the cross and isolated account asset interface', ts: 1698419318540}
130
+ },
131
+ },
132
+ },
133
+ })
134
+
135
+ def request_id(self):
136
+ requestId = self.sum(self.safe_integer(self.options, 'requestId', 0), 1)
137
+ self.options['requestId'] = requestId
138
+ return str(requestId)
139
+
140
+ async def watch_ticker(self, symbol: str, params={}) -> Ticker:
141
+ """
142
+ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
143
+ :param str symbol: unified symbol of the market to fetch the ticker for
144
+ :param dict [params]: extra parameters specific to the exchange API endpoint
145
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
146
+ """
147
+ await self.load_markets()
148
+ market = self.market(symbol)
149
+ symbol = market['symbol']
150
+ options = self.safe_value(self.options, 'watchTicker', {})
151
+ topic = self.safe_string(options, 'name', 'market.{marketId}.detail')
152
+ if topic == 'market.{marketId}.ticker' and market['type'] != 'spot':
153
+ raise BadRequest(self.id + ' watchTicker() with name market.{marketId}.ticker is only allowed for spot markets, use market.{marketId}.detail instead')
154
+ messageHash = self.implode_params(topic, {'marketId': market['id']})
155
+ url = self.get_url_by_market_type(market['type'], market['linear'])
156
+ return await self.subscribe_public(url, symbol, messageHash, None, params)
157
+
158
+ def handle_ticker(self, client: Client, message):
159
+ #
160
+ # "market.btcusdt.detail"
161
+ # {
162
+ # "ch": "market.btcusdt.detail",
163
+ # "ts": 1583494163784,
164
+ # "tick": {
165
+ # "id": 209988464418,
166
+ # "low": 8988,
167
+ # "high": 9155.41,
168
+ # "open": 9078.91,
169
+ # "close": 9136.46,
170
+ # "vol": 237813910.5928412,
171
+ # "amount": 26184.202558551195,
172
+ # "version": 209988464418,
173
+ # "count": 265673
174
+ # }
175
+ # }
176
+ # "market.btcusdt.bbo"
177
+ # {
178
+ # "ch": "market.btcusdt.bbo",
179
+ # "ts": 1671941599613,
180
+ # "tick": {
181
+ # "seqId": 161499562790,
182
+ # "ask": 16829.51,
183
+ # "askSize": 0.707776,
184
+ # "bid": 16829.5,
185
+ # "bidSize": 1.685945,
186
+ # "quoteTime": 1671941599612,
187
+ # "symbol": "btcusdt"
188
+ # }
189
+ # }
190
+ #
191
+ tick = self.safe_value(message, 'tick', {})
192
+ ch = self.safe_string(message, 'ch')
193
+ parts = ch.split('.')
194
+ marketId = self.safe_string(parts, 1)
195
+ market = self.safe_market(marketId)
196
+ ticker = self.parse_ticker(tick, market)
197
+ timestamp = self.safe_value(message, 'ts')
198
+ ticker['timestamp'] = timestamp
199
+ ticker['datetime'] = self.iso8601(timestamp)
200
+ symbol = ticker['symbol']
201
+ self.tickers[symbol] = ticker
202
+ client.resolve(ticker, ch)
203
+ return message
204
+
205
+ async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
206
+ """
207
+ get the list of most recent trades for a particular symbol
208
+ :param str symbol: unified symbol of the market to fetch trades for
209
+ :param int [since]: timestamp in ms of the earliest trade to fetch
210
+ :param int [limit]: the maximum amount of trades to fetch
211
+ :param dict [params]: extra parameters specific to the exchange API endpoint
212
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
213
+ """
214
+ await self.load_markets()
215
+ market = self.market(symbol)
216
+ symbol = market['symbol']
217
+ messageHash = 'market.' + market['id'] + '.trade.detail'
218
+ url = self.get_url_by_market_type(market['type'], market['linear'])
219
+ trades = await self.subscribe_public(url, symbol, messageHash, None, params)
220
+ if self.newUpdates:
221
+ limit = trades.getLimit(symbol, limit)
222
+ return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
223
+
224
+ def handle_trades(self, client: Client, message):
225
+ #
226
+ # {
227
+ # "ch": "market.btcusdt.trade.detail",
228
+ # "ts": 1583495834011,
229
+ # "tick": {
230
+ # "id": 105004645372,
231
+ # "ts": 1583495833751,
232
+ # "data": [
233
+ # {
234
+ # "id": 1.050046453727319e+22,
235
+ # "ts": 1583495833751,
236
+ # "tradeId": 102090727790,
237
+ # "amount": 0.003893,
238
+ # "price": 9150.01,
239
+ # "direction": "sell"
240
+ # }
241
+ # ]
242
+ # }
243
+ # }
244
+ #
245
+ tick = self.safe_value(message, 'tick', {})
246
+ data = self.safe_value(tick, 'data', {})
247
+ ch = self.safe_string(message, 'ch')
248
+ parts = ch.split('.')
249
+ marketId = self.safe_string(parts, 1)
250
+ market = self.safe_market(marketId)
251
+ symbol = market['symbol']
252
+ tradesCache = self.safe_value(self.trades, symbol)
253
+ if tradesCache is None:
254
+ limit = self.safe_integer(self.options, 'tradesLimit', 1000)
255
+ tradesCache = ArrayCache(limit)
256
+ self.trades[symbol] = tradesCache
257
+ for i in range(0, len(data)):
258
+ trade = self.parse_trade(data[i], market)
259
+ tradesCache.append(trade)
260
+ client.resolve(tradesCache, ch)
261
+ return message
262
+
263
+ async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
264
+ """
265
+ watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
266
+ :param str symbol: unified symbol of the market to fetch OHLCV data for
267
+ :param str timeframe: the length of time each candle represents
268
+ :param int [since]: timestamp in ms of the earliest candle to fetch
269
+ :param int [limit]: the maximum amount of candles to fetch
270
+ :param dict [params]: extra parameters specific to the exchange API endpoint
271
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
272
+ """
273
+ await self.load_markets()
274
+ market = self.market(symbol)
275
+ symbol = market['symbol']
276
+ interval = self.safe_string(self.timeframes, timeframe, timeframe)
277
+ messageHash = 'market.' + market['id'] + '.kline.' + interval
278
+ url = self.get_url_by_market_type(market['type'], market['linear'])
279
+ ohlcv = await self.subscribe_public(url, symbol, messageHash, None, params)
280
+ if self.newUpdates:
281
+ limit = ohlcv.getLimit(symbol, limit)
282
+ return self.filter_by_since_limit(ohlcv, since, limit, 0, True)
283
+
284
+ def handle_ohlcv(self, client: Client, message):
285
+ #
286
+ # {
287
+ # "ch": "market.btcusdt.kline.1min",
288
+ # "ts": 1583501786794,
289
+ # "tick": {
290
+ # "id": 1583501760,
291
+ # "open": 9094.5,
292
+ # "close": 9094.51,
293
+ # "low": 9094.5,
294
+ # "high": 9094.51,
295
+ # "amount": 0.44639786263800907,
296
+ # "vol": 4059.76919054,
297
+ # "count": 16
298
+ # }
299
+ # }
300
+ #
301
+ ch = self.safe_string(message, 'ch')
302
+ parts = ch.split('.')
303
+ marketId = self.safe_string(parts, 1)
304
+ market = self.safe_market(marketId)
305
+ symbol = market['symbol']
306
+ interval = self.safe_string(parts, 3)
307
+ timeframe = self.find_timeframe(interval)
308
+ self.ohlcvs[symbol] = self.safe_value(self.ohlcvs, symbol, {})
309
+ stored = self.safe_value(self.ohlcvs[symbol], timeframe)
310
+ if stored is None:
311
+ limit = self.safe_integer(self.options, 'OHLCVLimit', 1000)
312
+ stored = ArrayCacheByTimestamp(limit)
313
+ self.ohlcvs[symbol][timeframe] = stored
314
+ tick = self.safe_value(message, 'tick')
315
+ parsed = self.parse_ohlcv(tick, market)
316
+ stored.append(parsed)
317
+ client.resolve(stored, ch)
318
+
319
+ async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
320
+ """
321
+ :see: https://huobiapi.github.io/docs/dm/v1/en/#subscribe-market-depth-data
322
+ :see: https://huobiapi.github.io/docs/coin_margined_swap/v1/en/#subscribe-incremental-market-depth-data
323
+ :see: https://huobiapi.github.io/docs/usdt_swap/v1/en/#general-subscribe-incremental-market-depth-data
324
+ watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
325
+ :param str symbol: unified symbol of the market to fetch the order book for
326
+ :param int [limit]: the maximum amount of order book entries to return
327
+ :param dict [params]: extra parameters specific to the exchange API endpoint
328
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
329
+ """
330
+ await self.load_markets()
331
+ market = self.market(symbol)
332
+ symbol = market['symbol']
333
+ allowedLimits = [20, 150]
334
+ # 2) 5-level/20-level incremental MBP is a tick by tick feed,
335
+ # which means whenever there is an order book change at that level, it pushes an update
336
+ # 150-levels/400-level incremental MBP feed is based on the gap
337
+ # between two snapshots at 100ms interval.
338
+ if limit is None:
339
+ limit = 150 if market['spot'] else 20
340
+ if not self.in_array(limit, allowedLimits):
341
+ raise ExchangeError(self.id + ' watchOrderBook market accepts limits of 20 and 150 only')
342
+ messageHash = None
343
+ if market['spot']:
344
+ messageHash = 'market.' + market['id'] + '.mbp.' + str(limit)
345
+ else:
346
+ messageHash = 'market.' + market['id'] + '.depth.size_' + str(limit) + '.high_freq'
347
+ url = self.get_url_by_market_type(market['type'], market['linear'], False, True)
348
+ method = self.handle_order_book_subscription
349
+ if not market['spot']:
350
+ params = self.extend(params)
351
+ params['data_type'] = 'incremental'
352
+ method = None
353
+ orderbook = await self.subscribe_public(url, symbol, messageHash, method, params)
354
+ return orderbook.limit()
355
+
356
+ def handle_order_book_snapshot(self, client: Client, message, subscription):
357
+ #
358
+ # {
359
+ # "id": 1583473663565,
360
+ # "rep": "market.btcusdt.mbp.150",
361
+ # "status": "ok",
362
+ # "ts": 1698359289261,
363
+ # "data": {
364
+ # "seqNum": 104999417756,
365
+ # "bids": [
366
+ # [9058.27, 0],
367
+ # [9058.43, 0],
368
+ # [9058.99, 0],
369
+ # ],
370
+ # "asks": [
371
+ # [9084.27, 0.2],
372
+ # [9085.69, 0],
373
+ # [9085.81, 0],
374
+ # ]
375
+ # }
376
+ # }
377
+ #
378
+ symbol = self.safe_string(subscription, 'symbol')
379
+ messageHash = self.safe_string(subscription, 'messageHash')
380
+ id = self.safe_string(message, 'id')
381
+ lastTimestamp = self.safe_integer(subscription, 'lastTimestamp')
382
+ try:
383
+ orderbook = self.orderbooks[symbol]
384
+ data = self.safe_value(message, 'data')
385
+ messages = orderbook.cache
386
+ firstMessage = self.safe_value(messages, 0, {})
387
+ snapshot = self.parse_order_book(data, symbol)
388
+ tick = self.safe_value(firstMessage, 'tick')
389
+ sequence = self.safe_integer(tick, 'prevSeqNum')
390
+ nonce = self.safe_integer(data, 'seqNum')
391
+ snapshot['nonce'] = nonce
392
+ snapshotTimestamp = self.safe_integer(message, 'ts')
393
+ subscription['lastTimestamp'] = snapshotTimestamp
394
+ snapshotLimit = self.safe_integer(subscription, 'limit')
395
+ snapshotOrderBook = self.order_book(snapshot, snapshotLimit)
396
+ client.resolve(snapshotOrderBook, id)
397
+ if (sequence is None) or (nonce < sequence):
398
+ maxAttempts = self.handle_option('watchOrderBook', 'maxRetries', 3)
399
+ numAttempts = self.safe_integer(subscription, 'numAttempts', 0)
400
+ # retry to synchronize if we have not reached maxAttempts yet
401
+ if numAttempts < maxAttempts:
402
+ # safety guard
403
+ if messageHash in client.subscriptions:
404
+ numAttempts = self.sum(numAttempts, 1)
405
+ delayTime = self.sum(1000, lastTimestamp - snapshotTimestamp)
406
+ subscription['numAttempts'] = numAttempts
407
+ client.subscriptions[messageHash] = subscription
408
+ self.delay(delayTime, self.watch_order_book_snapshot, client, message, subscription)
409
+ else:
410
+ # raise upon failing to synchronize in maxAttempts
411
+ raise InvalidNonce(self.id + ' failed to synchronize WebSocket feed with the snapshot for symbol ' + symbol + ' in ' + str(maxAttempts) + ' attempts')
412
+ else:
413
+ orderbook.reset(snapshot)
414
+ # unroll the accumulated deltas
415
+ for i in range(0, len(messages)):
416
+ self.handle_order_book_message(client, messages[i])
417
+ orderbook.cache = []
418
+ self.orderbooks[symbol] = orderbook
419
+ client.resolve(orderbook, messageHash)
420
+ except Exception as e:
421
+ del client.subscriptions[messageHash]
422
+ del self.orderbooks[symbol]
423
+ client.reject(e, messageHash)
424
+
425
+ async def watch_order_book_snapshot(self, client, message, subscription):
426
+ messageHash = self.safe_string(subscription, 'messageHash')
427
+ symbol = self.safe_string(subscription, 'symbol')
428
+ limit = self.safe_integer(subscription, 'limit')
429
+ timestamp = self.safe_integer(message, 'ts')
430
+ params = self.safe_value(subscription, 'params')
431
+ attempts = self.safe_integer(subscription, 'numAttempts', 0)
432
+ market = self.market(symbol)
433
+ url = self.get_url_by_market_type(market['type'], market['linear'], False, True)
434
+ requestId = self.request_id()
435
+ request: dict = {
436
+ 'req': messageHash,
437
+ 'id': requestId,
438
+ }
439
+ # self is a temporary subscription by a specific requestId
440
+ # it has a very short lifetime until the snapshot is received over ws
441
+ snapshotSubscription: dict = {
442
+ 'id': requestId,
443
+ 'messageHash': messageHash,
444
+ 'symbol': symbol,
445
+ 'limit': limit,
446
+ 'params': params,
447
+ 'numAttempts': attempts,
448
+ 'lastTimestamp': timestamp,
449
+ 'method': self.handle_order_book_snapshot,
450
+ }
451
+ try:
452
+ orderbook = await self.watch(url, requestId, request, requestId, snapshotSubscription)
453
+ return orderbook.limit()
454
+ except Exception as e:
455
+ del client.subscriptions[messageHash]
456
+ client.reject(e, messageHash)
457
+ return None
458
+
459
+ def handle_delta(self, bookside, delta):
460
+ price = self.safe_float(delta, 0)
461
+ amount = self.safe_float(delta, 1)
462
+ bookside.store(price, amount)
463
+
464
+ def handle_deltas(self, bookside, deltas):
465
+ for i in range(0, len(deltas)):
466
+ self.handle_delta(bookside, deltas[i])
467
+
468
+ def handle_order_book_message(self, client: Client, message):
469
+ # spot markets
470
+ #
471
+ # {
472
+ # "ch": "market.btcusdt.mbp.150",
473
+ # "ts": 1583472025885,
474
+ # "tick": {
475
+ # "seqNum": 104998984994,
476
+ # "prevSeqNum": 104998984977,
477
+ # "bids": [
478
+ # [9058.27, 0],
479
+ # [9058.43, 0],
480
+ # [9058.99, 0],
481
+ # ],
482
+ # "asks": [
483
+ # [9084.27, 0.2],
484
+ # [9085.69, 0],
485
+ # [9085.81, 0],
486
+ # ]
487
+ # }
488
+ # }
489
+ #
490
+ # non-spot market update
491
+ #
492
+ # {
493
+ # "ch":"market.BTC220218.depth.size_150.high_freq",
494
+ # "tick":{
495
+ # "asks":[],
496
+ # "bids":[
497
+ # [43445.74,1],
498
+ # [43444.48,0],
499
+ # [40593.92,9]
500
+ # ],
501
+ # "ch":"market.BTC220218.depth.size_150.high_freq",
502
+ # "event":"update",
503
+ # "id":152727500274,
504
+ # "mrid":152727500274,
505
+ # "ts":1645023376098,
506
+ # "version":37536690
507
+ # },
508
+ # "ts":1645023376098
509
+ # }
510
+ # non-spot market snapshot
511
+ #
512
+ # {
513
+ # "ch":"market.BTC220218.depth.size_150.high_freq",
514
+ # "tick":{
515
+ # "asks":[
516
+ # [43445.74,1],
517
+ # [43444.48,0],
518
+ # [40593.92,9]
519
+ # ],
520
+ # "bids":[
521
+ # [43445.74,1],
522
+ # [43444.48,0],
523
+ # [40593.92,9]
524
+ # ],
525
+ # "ch":"market.BTC220218.depth.size_150.high_freq",
526
+ # "event":"snapshot",
527
+ # "id":152727500274,
528
+ # "mrid":152727500274,
529
+ # "ts":1645023376098,
530
+ # "version":37536690
531
+ # },
532
+ # "ts":1645023376098
533
+ # }
534
+ #
535
+ ch = self.safe_value(message, 'ch')
536
+ parts = ch.split('.')
537
+ marketId = self.safe_string(parts, 1)
538
+ market = self.safe_market(marketId)
539
+ symbol = market['symbol']
540
+ orderbook = self.orderbooks[symbol]
541
+ tick = self.safe_value(message, 'tick', {})
542
+ seqNum = self.safe_integer(tick, 'seqNum')
543
+ prevSeqNum = self.safe_integer(tick, 'prevSeqNum')
544
+ event = self.safe_string(tick, 'event')
545
+ version = self.safe_integer(tick, 'version')
546
+ timestamp = self.safe_integer(message, 'ts')
547
+ if event == 'snapshot':
548
+ snapshot = self.parse_order_book(tick, symbol, timestamp)
549
+ orderbook.reset(snapshot)
550
+ orderbook['nonce'] = version
551
+ if (prevSeqNum is not None) and prevSeqNum > orderbook['nonce']:
552
+ raise InvalidNonce(self.id + ' watchOrderBook() received a mesage out of order')
553
+ spotConditon = market['spot'] and (prevSeqNum == orderbook['nonce'])
554
+ nonSpotCondition = market['contract'] and (version - 1 == orderbook['nonce'])
555
+ if spotConditon or nonSpotCondition:
556
+ asks = self.safe_value(tick, 'asks', [])
557
+ bids = self.safe_value(tick, 'bids', [])
558
+ self.handle_deltas(orderbook['asks'], asks)
559
+ self.handle_deltas(orderbook['bids'], bids)
560
+ orderbook['nonce'] = seqNum if spotConditon else version
561
+ orderbook['timestamp'] = timestamp
562
+ orderbook['datetime'] = self.iso8601(timestamp)
563
+
564
+ def handle_order_book(self, client: Client, message):
565
+ #
566
+ # deltas
567
+ #
568
+ # spot markets
569
+ #
570
+ # {
571
+ # "ch": "market.btcusdt.mbp.150",
572
+ # "ts": 1583472025885,
573
+ # "tick": {
574
+ # "seqNum": 104998984994,
575
+ # "prevSeqNum": 104998984977,
576
+ # "bids": [
577
+ # [9058.27, 0],
578
+ # [9058.43, 0],
579
+ # [9058.99, 0],
580
+ # ],
581
+ # "asks": [
582
+ # [9084.27, 0.2],
583
+ # [9085.69, 0],
584
+ # [9085.81, 0],
585
+ # ]
586
+ # }
587
+ # }
588
+ #
589
+ # non spot markets
590
+ #
591
+ # {
592
+ # "ch":"market.BTC220218.depth.size_150.high_freq",
593
+ # "tick":{
594
+ # "asks":[],
595
+ # "bids":[
596
+ # [43445.74,1],
597
+ # [43444.48,0],
598
+ # [40593.92,9]
599
+ # ],
600
+ # "ch":"market.BTC220218.depth.size_150.high_freq",
601
+ # "event":"update",
602
+ # "id":152727500274,
603
+ # "mrid":152727500274,
604
+ # "ts":1645023376098,
605
+ # "version":37536690
606
+ # },
607
+ # "ts":1645023376098
608
+ # }
609
+ #
610
+ messageHash = self.safe_string(message, 'ch')
611
+ tick = self.safe_dict(message, 'tick')
612
+ event = self.safe_string(tick, 'event')
613
+ ch = self.safe_string(message, 'ch')
614
+ parts = ch.split('.')
615
+ marketId = self.safe_string(parts, 1)
616
+ symbol = self.safe_symbol(marketId)
617
+ if not (symbol in self.orderbooks):
618
+ size = self.safe_string(parts, 3)
619
+ sizeParts = size.split('_')
620
+ limit = self.safe_integer(sizeParts, 1)
621
+ self.orderbooks[symbol] = self.order_book({}, limit)
622
+ orderbook = self.orderbooks[symbol]
623
+ if (event is None) and (orderbook['nonce'] is None):
624
+ orderbook.cache.append(message)
625
+ else:
626
+ self.handle_order_book_message(client, message)
627
+ client.resolve(orderbook, messageHash)
628
+
629
+ def handle_order_book_subscription(self, client: Client, message, subscription):
630
+ symbol = self.safe_string(subscription, 'symbol')
631
+ market = self.market(symbol)
632
+ limit = self.safe_integer(subscription, 'limit')
633
+ self.orderbooks[symbol] = self.order_book({}, limit)
634
+ if market['spot']:
635
+ self.spawn(self.watch_order_book_snapshot, client, message, subscription)
636
+
637
+ async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
638
+ """
639
+ watches information on multiple trades made by the user
640
+ :param str symbol: unified market symbol of the market trades were made in
641
+ :param int [since]: the earliest time in ms to fetch trades for
642
+ :param int [limit]: the maximum number of trade structures to retrieve
643
+ :param dict [params]: extra parameters specific to the exchange API endpoint
644
+ :returns dict[]: a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure
645
+ """
646
+ self.check_required_credentials()
647
+ await self.load_markets()
648
+ type = None
649
+ marketId = '*' # wildcard
650
+ market = None
651
+ messageHash = None
652
+ channel = None
653
+ trades = None
654
+ subType = None
655
+ if symbol is not None:
656
+ market = self.market(symbol)
657
+ symbol = market['symbol']
658
+ type = market['type']
659
+ subType = 'linear' if market['linear'] else 'inverse'
660
+ marketId = market['lowercaseId']
661
+ else:
662
+ type = self.safe_string(self.options, 'defaultType', 'spot')
663
+ type = self.safe_string(params, 'type', type)
664
+ subType = self.safe_string_2(self.options, 'subType', 'defaultSubType', 'linear')
665
+ subType = self.safe_string(params, 'subType', subType)
666
+ params = self.omit(params, ['type', 'subType'])
667
+ if type == 'spot':
668
+ mode = None
669
+ if mode is None:
670
+ mode = self.safe_string_2(self.options, 'watchMyTrades', 'mode', '0')
671
+ mode = self.safe_string(params, 'mode', mode)
672
+ params = self.omit(params, 'mode')
673
+ messageHash = 'trade.clearing' + '#' + marketId + '#' + mode
674
+ channel = messageHash
675
+ else:
676
+ channelAndMessageHash = self.get_order_channel_and_message_hash(type, subType, market, params)
677
+ channel = self.safe_string(channelAndMessageHash, 0)
678
+ orderMessageHash = self.safe_string(channelAndMessageHash, 1)
679
+ # we will take advantage of the order messageHash because already handles stuff
680
+ # like symbol/margin/subtype/type variations
681
+ messageHash = orderMessageHash + ':' + 'trade'
682
+ trades = await self.subscribe_private(channel, messageHash, type, subType, params)
683
+ if self.newUpdates:
684
+ limit = trades.getLimit(symbol, limit)
685
+ return self.filter_by_symbol_since_limit(trades, symbol, since, limit, True)
686
+
687
+ def get_order_channel_and_message_hash(self, type, subType, market=None, params={}):
688
+ messageHash = None
689
+ channel = None
690
+ orderType = self.safe_string(self.options, 'orderType', 'orders') # orders or matchOrders
691
+ orderType = self.safe_string(params, 'orderType', orderType)
692
+ params = self.omit(params, 'orderType')
693
+ marketCode = market['lowercaseId'].lower() if (market is not None) else None
694
+ baseId = market['baseId'] if (market is not None) else None
695
+ prefix = orderType
696
+ messageHash = prefix
697
+ if subType == 'linear':
698
+ # USDT Margined Contracts Example: LTC/USDT:USDT
699
+ marginMode = self.safe_string(params, 'margin', 'cross')
700
+ marginPrefix = prefix + '_cross' if (marginMode == 'cross') else prefix
701
+ messageHash = marginPrefix
702
+ if marketCode is not None:
703
+ messageHash += '.' + marketCode
704
+ channel = messageHash
705
+ else:
706
+ channel = marginPrefix + '.' + '*'
707
+ elif type == 'future':
708
+ # inverse futures Example: BCH/USD:BCH-220408
709
+ if baseId is not None:
710
+ channel = prefix + '.' + baseId.lower()
711
+ messageHash = channel
712
+ else:
713
+ channel = prefix + '.' + '*'
714
+ else:
715
+ # inverse swaps: Example: BTC/USD:BTC
716
+ if marketCode is not None:
717
+ channel = prefix + '.' + marketCode
718
+ messageHash = channel
719
+ else:
720
+ channel = prefix + '.' + '*'
721
+ return [channel, messageHash]
722
+
723
+ async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
724
+ """
725
+ watches information on multiple orders made by the user
726
+ :param str symbol: unified market symbol of the market orders were made in
727
+ :param int [since]: the earliest time in ms to fetch orders for
728
+ :param int [limit]: the maximum number of order structures to retrieve
729
+ :param dict [params]: extra parameters specific to the exchange API endpoint
730
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
731
+ """
732
+ await self.load_markets()
733
+ type = None
734
+ subType = None
735
+ market = None
736
+ suffix = '*' # wildcard
737
+ if symbol is not None:
738
+ market = self.market(symbol)
739
+ symbol = market['symbol']
740
+ type = market['type']
741
+ suffix = market['lowercaseId']
742
+ subType = 'linear' if market['linear'] else 'inverse'
743
+ else:
744
+ type = self.safe_string(self.options, 'defaultType', 'spot')
745
+ type = self.safe_string(params, 'type', type)
746
+ subType = self.safe_string_2(self.options, 'subType', 'defaultSubType', 'linear')
747
+ subType = self.safe_string(params, 'subType', subType)
748
+ params = self.omit(params, ['type', 'subType'])
749
+ messageHash = None
750
+ channel = None
751
+ if type == 'spot':
752
+ messageHash = 'orders' + '#' + suffix
753
+ channel = messageHash
754
+ else:
755
+ channelAndMessageHash = self.get_order_channel_and_message_hash(type, subType, market, params)
756
+ channel = self.safe_string(channelAndMessageHash, 0)
757
+ messageHash = self.safe_string(channelAndMessageHash, 1)
758
+ orders = await self.subscribe_private(channel, messageHash, type, subType, params)
759
+ if self.newUpdates:
760
+ limit = orders.getLimit(symbol, limit)
761
+ return self.filter_by_since_limit(orders, since, limit, 'timestamp', True)
762
+
763
+ def handle_order(self, client: Client, message):
764
+ #
765
+ # spot
766
+ #
767
+ # {
768
+ # "action":"push",
769
+ # "ch":"orders#btcusdt", # or "orders#*" for global subscriptions
770
+ # "data": {
771
+ # "orderSource": "spot-web",
772
+ # "orderCreateTime": 1645116048355,
773
+ # "accountId": 44234548,
774
+ # "orderPrice": "100",
775
+ # "orderSize": "0.05",
776
+ # "symbol": "ethusdt",
777
+ # "type": "buy-limit",
778
+ # "orderId": "478861479986886",
779
+ # "eventType": "creation",
780
+ # "clientOrderId": '',
781
+ # "orderStatus": "submitted"
782
+ # }
783
+ # }
784
+ #
785
+ # spot wrapped trade
786
+ #
787
+ # {
788
+ # "action": "push",
789
+ # "ch": "orders#ltcusdt",
790
+ # "data": {
791
+ # "tradePrice": "130.01",
792
+ # "tradeVolume": "0.0385",
793
+ # "tradeTime": 1648714741525,
794
+ # "aggressor": True,
795
+ # "execAmt": "0.0385",
796
+ # "orderSource": "spot-web",
797
+ # "orderSize": "0.0385",
798
+ # "remainAmt": "0",
799
+ # "tradeId": 101541578884,
800
+ # "symbol": "ltcusdt",
801
+ # "type": "sell-market",
802
+ # "eventType": "trade",
803
+ # "clientOrderId": '',
804
+ # "orderStatus": "filled",
805
+ # "orderId": 509835753860328
806
+ # }
807
+ # }
808
+ #
809
+ # non spot order
810
+ #
811
+ # {
812
+ # "contract_type": "swap",
813
+ # "pair": "LTC-USDT",
814
+ # "business_type": "swap",
815
+ # "op": "notify",
816
+ # "topic": "orders_cross.ltc-usdt",
817
+ # "ts": 1650354508696,
818
+ # "symbol": "LTC",
819
+ # "contract_code": "LTC-USDT",
820
+ # "volume": 1,
821
+ # "price": 110.34,
822
+ # "order_price_type": "lightning",
823
+ # "direction": "sell",
824
+ # "offset": "close",
825
+ # "status": 6,
826
+ # "lever_rate": 1,
827
+ # "order_id": "966002354015051776",
828
+ # "order_id_str": "966002354015051776",
829
+ # "client_order_id": null,
830
+ # "order_source": "web",
831
+ # "order_type": 1,
832
+ # "created_at": 1650354508649,
833
+ # "trade_volume": 1,
834
+ # "trade_turnover": 11.072,
835
+ # "fee": -0.005536,
836
+ # "trade_avg_price": 110.72,
837
+ # "margin_frozen": 0,
838
+ # "profit": -0.045,
839
+ # "trade": [
840
+ # {
841
+ # "trade_fee": -0.005536,
842
+ # "fee_asset": "USDT",
843
+ # "real_profit": 0.473,
844
+ # "profit": -0.045,
845
+ # "trade_id": 86678766507,
846
+ # "id": "86678766507-966002354015051776-1",
847
+ # "trade_volume": 1,
848
+ # "trade_price": 110.72,
849
+ # "trade_turnover": 11.072,
850
+ # "created_at": 1650354508656,
851
+ # "role": "taker"
852
+ # }
853
+ # ],
854
+ # "canceled_at": 0,
855
+ # "fee_asset": "USDT",
856
+ # "margin_asset": "USDT",
857
+ # "uid": "359305390",
858
+ # "liquidation_type": "0",
859
+ # "margin_mode": "cross",
860
+ # "margin_account": "USDT",
861
+ # "is_tpsl": 0,
862
+ # "real_profit": 0.473,
863
+ # "trade_partition": "USDT",
864
+ # "reduce_only": 1
865
+ # }
866
+ #
867
+ #
868
+ messageHash = self.safe_string_2(message, 'ch', 'topic')
869
+ data = self.safe_value(message, 'data')
870
+ marketId = self.safe_string(message, 'contract_code')
871
+ if marketId is None:
872
+ marketId = self.safe_string(data, 'symbol')
873
+ market = self.safe_market(marketId)
874
+ parsedOrder = None
875
+ if data is not None:
876
+ # spot updates
877
+ eventType = self.safe_string(data, 'eventType')
878
+ if eventType == 'trade':
879
+ # when a spot order is filled we get an update message
880
+ # with the trade info
881
+ parsedTrade = self.parse_order_trade(data, market)
882
+ # inject trade in existing order by faking an order object
883
+ orderId = self.safe_string(parsedTrade, 'order')
884
+ trades = [parsedTrade]
885
+ status = self.parse_order_status(self.safe_string_2(data, 'orderStatus', 'status', 'closed'))
886
+ filled = self.safe_string(data, 'execAmt')
887
+ remaining = self.safe_string(data, 'remainAmt')
888
+ order: dict = {
889
+ 'id': orderId,
890
+ 'trades': trades,
891
+ 'status': status,
892
+ 'symbol': market['symbol'],
893
+ 'filled': self.parse_number(filled),
894
+ 'remaining': self.parse_number(remaining),
895
+ }
896
+ parsedOrder = order
897
+ else:
898
+ parsedOrder = self.parse_ws_order(data, market)
899
+ else:
900
+ # contract branch
901
+ parsedOrder = self.parse_ws_order(message, market)
902
+ rawTrades = self.safe_value(message, 'trade', [])
903
+ tradesLength = len(rawTrades)
904
+ if tradesLength > 0:
905
+ tradesObject: dict = {
906
+ 'trades': rawTrades,
907
+ 'ch': messageHash,
908
+ 'symbol': marketId,
909
+ }
910
+ # inject order params in every trade
911
+ extendTradeParams: dict = {
912
+ 'order': self.safe_string(parsedOrder, 'id'),
913
+ 'type': self.safe_string(parsedOrder, 'type'),
914
+ 'side': self.safe_string(parsedOrder, 'side'),
915
+ }
916
+ # trades arrive inside an order update
917
+ # we're forwarding them to handleMyTrade
918
+ # so they can be properly resolved
919
+ self.handle_my_trade(client, tradesObject, extendTradeParams)
920
+ if self.orders is None:
921
+ limit = self.safe_integer(self.options, 'ordersLimit', 1000)
922
+ self.orders = ArrayCacheBySymbolById(limit)
923
+ cachedOrders = self.orders
924
+ cachedOrders.append(parsedOrder)
925
+ client.resolve(self.orders, messageHash)
926
+ # when we make a global subscription(for contracts only) our message hash can't have a symbol/currency attached
927
+ # so we're removing it here
928
+ genericMessageHash = messageHash.replace('.' + market['lowercaseId'], '')
929
+ lowerCaseBaseId = self.safe_string_lower(market, 'baseId')
930
+ genericMessageHash = genericMessageHash.replace('.' + lowerCaseBaseId, '')
931
+ client.resolve(self.orders, genericMessageHash)
932
+
933
+ def parse_ws_order(self, order, market=None):
934
+ #
935
+ # spot
936
+ #
937
+ # {
938
+ # "orderSource": "spot-web",
939
+ # "orderCreateTime": 1645116048355, # creating only
940
+ # "accountId": 44234548,
941
+ # "orderPrice": "100",
942
+ # "orderSize": "0.05",
943
+ # "orderValue": "3.71676361", # market-buy only
944
+ # "symbol": "ethusdt",
945
+ # "type": "buy-limit",
946
+ # "orderId": "478861479986886",
947
+ # "eventType": "creation",
948
+ # "clientOrderId": '',
949
+ # "orderStatus": "submitted"
950
+ # "lastActTime":1645118621810 # except creating
951
+ # "execAmt":"0"
952
+ # }
953
+ #
954
+ # swap order
955
+ #
956
+ # {
957
+ # "contract_type": "swap",
958
+ # "pair": "LTC-USDT",
959
+ # "business_type": "swap",
960
+ # "op": "notify",
961
+ # "topic": "orders_cross.ltc-usdt",
962
+ # "ts": 1648717911384,
963
+ # "symbol": "LTC",
964
+ # "contract_code": "LTC-USDT",
965
+ # "volume": 1,
966
+ # "price": 129.13,
967
+ # "order_price_type": "lightning",
968
+ # "direction": "sell",
969
+ # "offset": "close",
970
+ # "status": 6,
971
+ # "lever_rate": 5,
972
+ # "order_id": "959137967397068800",
973
+ # "order_id_str": "959137967397068800",
974
+ # "client_order_id": null,
975
+ # "order_source": "web",
976
+ # "order_type": 1,
977
+ # "created_at": 1648717911344,
978
+ # "trade_volume": 1,
979
+ # "trade_turnover": 12.952,
980
+ # "fee": -0.006476,
981
+ # "trade_avg_price": 129.52,
982
+ # "margin_frozen": 0,
983
+ # "profit": -0.005,
984
+ # "trade": [
985
+ # {
986
+ # "trade_fee": -0.006476,
987
+ # "fee_asset": "USDT",
988
+ # "real_profit": -0.005,
989
+ # "profit": -0.005,
990
+ # "trade_id": 83619995370,
991
+ # "id": "83619995370-959137967397068800-1",
992
+ # "trade_volume": 1,
993
+ # "trade_price": 129.52,
994
+ # "trade_turnover": 12.952,
995
+ # "created_at": 1648717911352,
996
+ # "role": "taker"
997
+ # }
998
+ # ],
999
+ # "canceled_at": 0,
1000
+ # "fee_asset": "USDT",
1001
+ # "margin_asset": "USDT",
1002
+ # "uid": "359305390",
1003
+ # "liquidation_type": "0",
1004
+ # "margin_mode": "cross",
1005
+ # "margin_account": "USDT",
1006
+ # "is_tpsl": 0,
1007
+ # "real_profit": -0.005,
1008
+ # "trade_partition": "USDT",
1009
+ # "reduce_only": 1
1010
+ # }
1011
+ #
1012
+ # {
1013
+ # "op":"notify",
1014
+ # "topic":"orders.ada",
1015
+ # "ts":1604388667226,
1016
+ # "symbol":"ADA",
1017
+ # "contract_type":"quarter",
1018
+ # "contract_code":"ADA201225",
1019
+ # "volume":1,
1020
+ # "price":0.0905,
1021
+ # "order_price_type":"post_only",
1022
+ # "direction":"sell",
1023
+ # "offset":"open",
1024
+ # "status":6,
1025
+ # "lever_rate":20,
1026
+ # "order_id":773207641127878656,
1027
+ # "order_id_str":"773207641127878656",
1028
+ # "client_order_id":null,
1029
+ # "order_source":"web",
1030
+ # "order_type":1,
1031
+ # "created_at":1604388667146,
1032
+ # "trade_volume":1,
1033
+ # "trade_turnover":10,
1034
+ # "fee":-0.022099447513812154,
1035
+ # "trade_avg_price":0.0905,
1036
+ # "margin_frozen":0,
1037
+ # "profit":0,
1038
+ # "trade":[],
1039
+ # "canceled_at":0,
1040
+ # "fee_asset":"ADA",
1041
+ # "uid":"123456789",
1042
+ # "liquidation_type":"0",
1043
+ # "is_tpsl": 0,
1044
+ # "real_profit": 0
1045
+ # }
1046
+ #
1047
+ lastTradeTimestamp = self.safe_integer_2(order, 'lastActTime', 'ts')
1048
+ created = self.safe_integer(order, 'orderCreateTime')
1049
+ marketId = self.safe_string_2(order, 'contract_code', 'symbol')
1050
+ market = self.safe_market(marketId, market)
1051
+ symbol = self.safe_symbol(marketId, market)
1052
+ amount = self.safe_string_2(order, 'orderSize', 'volume')
1053
+ status = self.parse_order_status(self.safe_string_2(order, 'orderStatus', 'status'))
1054
+ id = self.safe_string_2(order, 'orderId', 'order_id')
1055
+ clientOrderId = self.safe_string_2(order, 'clientOrderId', 'client_order_id')
1056
+ price = self.safe_string_2(order, 'orderPrice', 'price')
1057
+ filled = self.safe_string(order, 'execAmt')
1058
+ typeSide = self.safe_string(order, 'type')
1059
+ feeCost = self.safe_string(order, 'fee')
1060
+ fee = None
1061
+ if feeCost is not None:
1062
+ feeCurrencyId = self.safe_string(order, 'fee_asset')
1063
+ fee = {
1064
+ 'cost': feeCost,
1065
+ 'currency': self.safe_currency_code(feeCurrencyId),
1066
+ }
1067
+ avgPrice = self.safe_string(order, 'trade_avg_price')
1068
+ rawTrades = self.safe_value(order, 'trade')
1069
+ typeSideParts = []
1070
+ if typeSide is not None:
1071
+ typeSideParts = typeSide.split('-')
1072
+ type = self.safe_string_lower(typeSideParts, 1)
1073
+ if type is None:
1074
+ type = self.safe_string(order, 'order_price_type')
1075
+ side = self.safe_string_lower(typeSideParts, 0)
1076
+ if side is None:
1077
+ side = self.safe_string(order, 'direction')
1078
+ cost = self.safe_string(order, 'orderValue')
1079
+ return self.safe_order({
1080
+ 'info': order,
1081
+ 'id': id,
1082
+ 'clientOrderId': clientOrderId,
1083
+ 'timestamp': created,
1084
+ 'datetime': self.iso8601(created),
1085
+ 'lastTradeTimestamp': lastTradeTimestamp,
1086
+ 'status': status,
1087
+ 'symbol': symbol,
1088
+ 'type': type,
1089
+ 'timeInForce': None,
1090
+ 'postOnly': None,
1091
+ 'side': side,
1092
+ 'price': price,
1093
+ 'amount': amount,
1094
+ 'filled': filled,
1095
+ 'remaining': None,
1096
+ 'cost': cost,
1097
+ 'fee': fee,
1098
+ 'average': avgPrice,
1099
+ 'trades': rawTrades,
1100
+ }, market)
1101
+
1102
+ def parse_order_trade(self, trade, market=None):
1103
+ # spot private wrapped trade
1104
+ #
1105
+ # {
1106
+ # "tradePrice": "130.01",
1107
+ # "tradeVolume": "0.0385",
1108
+ # "tradeTime": 1648714741525,
1109
+ # "aggressor": True,
1110
+ # "execAmt": "0.0385",
1111
+ # "orderSource": "spot-web",
1112
+ # "orderSize": "0.0385",
1113
+ # "remainAmt": "0",
1114
+ # "tradeId": 101541578884,
1115
+ # "symbol": "ltcusdt",
1116
+ # "type": "sell-market",
1117
+ # "eventType": "trade",
1118
+ # "clientOrderId": '',
1119
+ # "orderStatus": "filled",
1120
+ # "orderId": 509835753860328
1121
+ # }
1122
+ #
1123
+ market = self.safe_market(None, market)
1124
+ symbol = market['symbol']
1125
+ tradeId = self.safe_string(trade, 'tradeId')
1126
+ price = self.safe_string(trade, 'tradePrice')
1127
+ amount = self.safe_string(trade, 'tradeVolume')
1128
+ order = self.safe_string(trade, 'orderId')
1129
+ timestamp = self.safe_integer(trade, 'tradeTime')
1130
+ type = self.safe_string(trade, 'type')
1131
+ side = None
1132
+ if type is not None:
1133
+ typeParts = type.split('-')
1134
+ side = typeParts[0]
1135
+ type = typeParts[1]
1136
+ aggressor = self.safe_value(trade, 'aggressor')
1137
+ takerOrMaker = None
1138
+ if aggressor is not None:
1139
+ takerOrMaker = 'taker' if aggressor else 'maker'
1140
+ return self.safe_trade({
1141
+ 'info': trade,
1142
+ 'timestamp': timestamp,
1143
+ 'datetime': self.iso8601(timestamp),
1144
+ 'symbol': symbol,
1145
+ 'id': tradeId,
1146
+ 'order': order,
1147
+ 'type': type,
1148
+ 'takerOrMaker': takerOrMaker,
1149
+ 'side': side,
1150
+ 'price': price,
1151
+ 'amount': amount,
1152
+ 'cost': None,
1153
+ 'fee': None,
1154
+ }, market)
1155
+
1156
+ async def watch_positions(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]:
1157
+ """
1158
+ :see: https://www.huobi.com/en-in/opend/newApiPages/?id=8cb7de1c-77b5-11ed-9966-0242ac110003
1159
+ :see: https://www.huobi.com/en-in/opend/newApiPages/?id=8cb7df0f-77b5-11ed-9966-0242ac110003
1160
+ :see: https://www.huobi.com/en-in/opend/newApiPages/?id=28c34a7d-77ae-11ed-9966-0242ac110003
1161
+ :see: https://www.huobi.com/en-in/opend/newApiPages/?id=5d5156b5-77b6-11ed-9966-0242ac110003
1162
+ watch all open positions. Note: huobi has one channel for each marginMode and type
1163
+ :param str[]|None symbols: list of unified market symbols
1164
+ :param dict params: extra parameters specific to the exchange API endpoint
1165
+ :returns dict[]: a list of `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
1166
+ """
1167
+ await self.load_markets()
1168
+ market = None
1169
+ messageHash = ''
1170
+ if not self.is_empty(symbols):
1171
+ market = self.get_market_from_symbols(symbols)
1172
+ messageHash = '::' + ','.join(symbols)
1173
+ type = None
1174
+ subType = None
1175
+ if market is not None:
1176
+ type = market['type']
1177
+ subType = 'linear' if market['linear'] else 'inverse'
1178
+ else:
1179
+ type, params = self.handle_market_type_and_params('watchPositions', market, params)
1180
+ if type == 'spot':
1181
+ type = 'future'
1182
+ subType, params = self.handle_option_and_params(params, 'watchPositions', 'subType', subType)
1183
+ symbols = self.market_symbols(symbols)
1184
+ marginMode = None
1185
+ marginMode, params = self.handle_margin_mode_and_params('watchPositions', params, 'cross')
1186
+ isLinear = (subType == 'linear')
1187
+ url = self.get_url_by_market_type(type, isLinear, True)
1188
+ messageHash = marginMode + ':positions' + messageHash
1189
+ channel = 'positions_cross.*' if (marginMode == 'cross') else 'positions.*'
1190
+ newPositions = await self.subscribe_private(channel, messageHash, type, subType, params)
1191
+ if self.newUpdates:
1192
+ return newPositions
1193
+ return self.filter_by_symbols_since_limit(self.positions[url][marginMode], symbols, since, limit, False)
1194
+
1195
+ def handle_positions(self, client, message):
1196
+ #
1197
+ # {
1198
+ # op: 'notify',
1199
+ # topic: 'positions_cross',
1200
+ # ts: 1696767149650,
1201
+ # event: 'snapshot',
1202
+ # data: [
1203
+ # {
1204
+ # contract_type: 'swap',
1205
+ # pair: 'BTC-USDT',
1206
+ # business_type: 'swap',
1207
+ # liquidation_price: null,
1208
+ # symbol: 'BTC',
1209
+ # contract_code: 'BTC-USDT',
1210
+ # volume: 1,
1211
+ # available: 1,
1212
+ # frozen: 0,
1213
+ # cost_open: 27802.2,
1214
+ # cost_hold: 27802.2,
1215
+ # profit_unreal: 0.0175,
1216
+ # profit_rate: 0.000629446590557581,
1217
+ # profit: 0.0175,
1218
+ # margin_asset: 'USDT',
1219
+ # position_margin: 27.8197,
1220
+ # lever_rate: 1,
1221
+ # direction: 'buy',
1222
+ # last_price: 27819.7,
1223
+ # margin_mode: 'cross',
1224
+ # margin_account: 'USDT',
1225
+ # trade_partition: 'USDT',
1226
+ # position_mode: 'dual_side'
1227
+ # },
1228
+ # ]
1229
+ # }
1230
+ #
1231
+ url = client.url
1232
+ topic = self.safe_string(message, 'topic', '')
1233
+ marginMode = 'cross' if (topic == 'positions_cross') else 'isolated'
1234
+ if self.positions is None:
1235
+ self.positions = {}
1236
+ clientPositions = self.safe_value(self.positions, url)
1237
+ if clientPositions is None:
1238
+ self.positions[url] = {}
1239
+ clientMarginModePositions = self.safe_value(clientPositions, marginMode)
1240
+ if clientMarginModePositions is None:
1241
+ self.positions[url][marginMode] = ArrayCacheBySymbolBySide()
1242
+ cache = self.positions[url][marginMode]
1243
+ rawPositions = self.safe_value(message, 'data', [])
1244
+ newPositions = []
1245
+ timestamp = self.safe_integer(message, 'ts')
1246
+ for i in range(0, len(rawPositions)):
1247
+ rawPosition = rawPositions[i]
1248
+ position = self.parse_position(rawPosition)
1249
+ position['timestamp'] = timestamp
1250
+ position['datetime'] = self.iso8601(timestamp)
1251
+ newPositions.append(position)
1252
+ cache.append(position)
1253
+ messageHashes = self.find_message_hashes(client, marginMode + ':positions::')
1254
+ for i in range(0, len(messageHashes)):
1255
+ messageHash = messageHashes[i]
1256
+ parts = messageHash.split('::')
1257
+ symbolsString = parts[1]
1258
+ symbols = symbolsString.split(',')
1259
+ positions = self.filter_by_array(newPositions, 'symbol', symbols, False)
1260
+ if not self.is_empty(positions):
1261
+ client.resolve(positions, messageHash)
1262
+ client.resolve(newPositions, marginMode + ':positions')
1263
+
1264
+ async def watch_balance(self, params={}) -> Balances:
1265
+ """
1266
+ watch balance and get the amount of funds available for trading or funds locked in orders
1267
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1268
+ :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
1269
+ """
1270
+ type = None
1271
+ type, params = self.handle_market_type_and_params('watchBalance', None, params)
1272
+ subType = None
1273
+ subType, params = self.handle_sub_type_and_params('watchBalance', None, params, 'linear')
1274
+ isUnifiedAccount = self.safe_value_2(params, 'isUnifiedAccount', 'unified', False)
1275
+ params = self.omit(params, ['isUnifiedAccount', 'unified'])
1276
+ await self.load_markets()
1277
+ messageHash = None
1278
+ channel = None
1279
+ marginMode = None
1280
+ if type == 'spot':
1281
+ mode = self.safe_string_2(self.options, 'watchBalance', 'mode', '2')
1282
+ mode = self.safe_string(params, 'mode', mode)
1283
+ messageHash = 'accounts.update' + '#' + mode
1284
+ channel = messageHash
1285
+ else:
1286
+ symbol = self.safe_string(params, 'symbol')
1287
+ currency = self.safe_string(params, 'currency')
1288
+ market = self.market(symbol) if (symbol is not None) else None
1289
+ currencyCode = self.currency(currency) if (currency is not None) else None
1290
+ marginMode = self.safe_string(params, 'margin', 'cross')
1291
+ params = self.omit(params, ['currency', 'symbol', 'margin'])
1292
+ prefix = 'accounts'
1293
+ messageHash = prefix
1294
+ if subType == 'linear':
1295
+ if isUnifiedAccount:
1296
+ # usdt contracts account
1297
+ prefix = 'accounts_unify'
1298
+ messageHash = prefix
1299
+ channel = prefix + '.' + 'usdt'
1300
+ else:
1301
+ # usdt contracts account
1302
+ prefix = prefix + '_cross' if (marginMode == 'cross') else prefix
1303
+ messageHash = prefix
1304
+ if marginMode == 'isolated':
1305
+ # isolated margin only allows filtering by symbol3
1306
+ if symbol is not None:
1307
+ messageHash += '.' + market['id']
1308
+ channel = messageHash
1309
+ else:
1310
+ # subscribe to all
1311
+ channel = prefix + '.' + '*'
1312
+ else:
1313
+ # cross margin
1314
+ if currencyCode is not None:
1315
+ channel = prefix + '.' + currencyCode['id']
1316
+ messageHash = channel
1317
+ else:
1318
+ # subscribe to all
1319
+ channel = prefix + '.' + '*'
1320
+ elif type == 'future':
1321
+ # inverse futures account
1322
+ if currencyCode is not None:
1323
+ messageHash += '.' + currencyCode['id']
1324
+ channel = messageHash
1325
+ else:
1326
+ # subscribe to all
1327
+ channel = prefix + '.' + '*'
1328
+ else:
1329
+ # inverse swaps account
1330
+ if market is not None:
1331
+ messageHash += '.' + market['id']
1332
+ channel = messageHash
1333
+ else:
1334
+ # subscribe to all
1335
+ channel = prefix + '.' + '*'
1336
+ subscriptionParams: dict = {
1337
+ 'type': type,
1338
+ 'subType': subType,
1339
+ 'margin': marginMode,
1340
+ }
1341
+ # we are differentiating the channel from the messageHash for global subscriptions(*)
1342
+ # because huobi returns a different topic than the topic sent. Example: we send
1343
+ # "accounts.*" and "accounts" is returned so we're setting channel = "accounts.*" and
1344
+ # messageHash = "accounts" allowing handleBalance to freely resolve the topic in the message
1345
+ return await self.subscribe_private(channel, messageHash, type, subType, params, subscriptionParams)
1346
+
1347
+ def handle_balance(self, client: Client, message):
1348
+ # spot
1349
+ #
1350
+ # {
1351
+ # "action": "push",
1352
+ # "ch": "accounts.update#0",
1353
+ # "data": {
1354
+ # "currency": "btc",
1355
+ # "accountId": 123456,
1356
+ # "balance": "23.111",
1357
+ # "available": "2028.699426619837209087",
1358
+ # "changeType": "transfer",
1359
+ # "accountType":"trade",
1360
+ # "seqNum": "86872993928",
1361
+ # "changeTime": 1568601800000
1362
+ # }
1363
+ # }
1364
+ #
1365
+ # inverse future
1366
+ #
1367
+ # {
1368
+ # "op":"notify",
1369
+ # "topic":"accounts.ada",
1370
+ # "ts":1604388667226,
1371
+ # "event":"order.match",
1372
+ # "data":[
1373
+ # {
1374
+ # "symbol":"ADA",
1375
+ # "margin_balance":446.417641681222726716,
1376
+ # "margin_static":445.554085945257745136,
1377
+ # "margin_position":11.049723756906077348,
1378
+ # "margin_frozen":0,
1379
+ # "margin_available":435.367917924316649368,
1380
+ # "profit_real":21.627049781983019459,
1381
+ # "profit_unreal":0.86355573596498158,
1382
+ # "risk_rate":40.000796572150656768,
1383
+ # "liquidation_price":0.018674308027108984,
1384
+ # "withdraw_available":423.927036163274725677,
1385
+ # "lever_rate":20,
1386
+ # "adjust_factor":0.4
1387
+ # }
1388
+ # ],
1389
+ # "uid":"123456789"
1390
+ # }
1391
+ #
1392
+ # usdt / linear future, swap
1393
+ #
1394
+ # {
1395
+ # "op":"notify",
1396
+ # "topic":"accounts.btc-usdt", # or "accounts" for global subscriptions
1397
+ # "ts":1603711370689,
1398
+ # "event":"order.open",
1399
+ # "data":[
1400
+ # {
1401
+ # "margin_mode":"cross",
1402
+ # "margin_account":"USDT",
1403
+ # "margin_asset":"USDT",
1404
+ # "margin_balance":30.959342395,
1405
+ # "margin_static":30.959342395,
1406
+ # "margin_position":0,
1407
+ # "margin_frozen":10,
1408
+ # "profit_real":0,
1409
+ # "profit_unreal":0,
1410
+ # "withdraw_available":20.959342395,
1411
+ # "risk_rate":153.796711975,
1412
+ # "position_mode":"dual_side",
1413
+ # "contract_detail":[
1414
+ # {
1415
+ # "symbol":"LTC",
1416
+ # "contract_code":"LTC-USDT",
1417
+ # "margin_position":0,
1418
+ # "margin_frozen":0,
1419
+ # "margin_available":20.959342395,
1420
+ # "profit_unreal":0,
1421
+ # "liquidation_price":null,
1422
+ # "lever_rate":1,
1423
+ # "adjust_factor":0.01,
1424
+ # "contract_type":"swap",
1425
+ # "pair":"LTC-USDT",
1426
+ # "business_type":"swap",
1427
+ # "trade_partition":"USDT"
1428
+ # },
1429
+ # ],
1430
+ # "futures_contract_detail":[],
1431
+ # }
1432
+ # ]
1433
+ # }
1434
+ #
1435
+ # inverse future
1436
+ #
1437
+ # {
1438
+ # "op":"notify",
1439
+ # "topic":"accounts.ada",
1440
+ # "ts":1604388667226,
1441
+ # "event":"order.match",
1442
+ # "data":[
1443
+ # {
1444
+ # "symbol":"ADA",
1445
+ # "margin_balance":446.417641681222726716,
1446
+ # "margin_static":445.554085945257745136,
1447
+ # "margin_position":11.049723756906077348,
1448
+ # "margin_frozen":0,
1449
+ # "margin_available":435.367917924316649368,
1450
+ # "profit_real":21.627049781983019459,
1451
+ # "profit_unreal":0.86355573596498158,
1452
+ # "risk_rate":40.000796572150656768,
1453
+ # "liquidation_price":0.018674308027108984,
1454
+ # "withdraw_available":423.927036163274725677,
1455
+ # "lever_rate":20,
1456
+ # "adjust_factor":0.4
1457
+ # }
1458
+ # ],
1459
+ # "uid":"123456789"
1460
+ # }
1461
+ #
1462
+ channel = self.safe_string(message, 'ch')
1463
+ data = self.safe_value(message, 'data', [])
1464
+ timestamp = self.safe_integer(data, 'changeTime', self.safe_integer(message, 'ts'))
1465
+ self.balance['timestamp'] = timestamp
1466
+ self.balance['datetime'] = self.iso8601(timestamp)
1467
+ self.balance['info'] = data
1468
+ if channel is not None:
1469
+ # spot balance
1470
+ currencyId = self.safe_string(data, 'currency')
1471
+ code = self.safe_currency_code(currencyId)
1472
+ account = self.account()
1473
+ account['free'] = self.safe_string(data, 'available')
1474
+ account['total'] = self.safe_string(data, 'balance')
1475
+ self.balance[code] = account
1476
+ self.balance = self.safe_balance(self.balance)
1477
+ client.resolve(self.balance, channel)
1478
+ else:
1479
+ # contract balance
1480
+ dataLength = len(data)
1481
+ if dataLength == 0:
1482
+ return
1483
+ first = self.safe_value(data, 0, {})
1484
+ topic = self.safe_string(message, 'topic')
1485
+ splitTopic = topic.split('.')
1486
+ messageHash = self.safe_string(splitTopic, 0)
1487
+ subscription = self.safe_value_2(client.subscriptions, messageHash, messageHash + '.*')
1488
+ if subscription is None:
1489
+ # if subscription not found means that we subscribed to a specific currency/symbol
1490
+ # and we use the first data entry to find it
1491
+ # Example: topic = 'accounts'
1492
+ # client.subscription hash = 'accounts.usdt'
1493
+ # we do 'accounts' + '.' + data[0]]['margin_asset'] to get it
1494
+ currencyId = self.safe_string_2(first, 'margin_asset', 'symbol')
1495
+ messageHash += '.' + currencyId.lower()
1496
+ subscription = self.safe_value(client.subscriptions, messageHash)
1497
+ type = self.safe_string(subscription, 'type')
1498
+ subType = self.safe_string(subscription, 'subType')
1499
+ if topic == 'accounts_unify':
1500
+ # {
1501
+ # "margin_asset": "USDT",
1502
+ # "margin_static": 10,
1503
+ # "cross_margin_static": 10,
1504
+ # "margin_balance": 10,
1505
+ # "cross_profit_unreal": 0,
1506
+ # "margin_frozen": 0,
1507
+ # "withdraw_available": 10,
1508
+ # "cross_risk_rate": null,
1509
+ # "cross_swap": [],
1510
+ # "cross_future": [],
1511
+ # "isolated_swap": []
1512
+ # }
1513
+ marginAsset = self.safe_string(first, 'margin_asset')
1514
+ code = self.safe_currency_code(marginAsset)
1515
+ marginFrozen = self.safe_string(first, 'margin_frozen')
1516
+ unifiedAccount = self.account()
1517
+ unifiedAccount['free'] = self.safe_string(first, 'withdraw_available')
1518
+ unifiedAccount['used'] = marginFrozen
1519
+ self.balance[code] = unifiedAccount
1520
+ self.balance = self.safe_balance(self.balance)
1521
+ client.resolve(self.balance, 'accounts_unify')
1522
+ elif subType == 'linear':
1523
+ margin = self.safe_string(subscription, 'margin')
1524
+ if margin == 'cross':
1525
+ fieldName = 'futures_contract_detail' if (type == 'future') else 'contract_detail'
1526
+ balances = self.safe_value(first, fieldName, [])
1527
+ balancesLength = len(balances)
1528
+ if balancesLength > 0:
1529
+ for i in range(0, len(balances)):
1530
+ balance = balances[i]
1531
+ marketId = self.safe_string_2(balance, 'contract_code', 'margin_account')
1532
+ market = self.safe_market(marketId)
1533
+ currencyId = self.safe_string(balance, 'margin_asset')
1534
+ currency = self.safe_currency(currencyId)
1535
+ code = self.safe_string(market, 'settle', currency['code'])
1536
+ # the exchange outputs positions for delisted markets
1537
+ # https://www.huobi.com/support/en-us/detail/74882968522337
1538
+ # we skip it if the market was delisted
1539
+ if code is not None:
1540
+ account = self.account()
1541
+ account['free'] = self.safe_string_2(balance, 'margin_balance', 'margin_available')
1542
+ account['used'] = self.safe_string(balance, 'margin_frozen')
1543
+ accountsByCode: dict = {}
1544
+ accountsByCode[code] = account
1545
+ symbol = market['symbol']
1546
+ self.balance[symbol] = self.safe_balance(accountsByCode)
1547
+ else:
1548
+ # isolated margin
1549
+ for i in range(0, len(data)):
1550
+ isolatedBalance = data[i]
1551
+ account = self.account()
1552
+ account['free'] = self.safe_string(isolatedBalance, 'margin_balance', 'margin_available')
1553
+ account['used'] = self.safe_string(isolatedBalance, 'margin_frozen')
1554
+ currencyId = self.safe_string_2(isolatedBalance, 'margin_asset', 'symbol')
1555
+ code = self.safe_currency_code(currencyId)
1556
+ self.balance[code] = account
1557
+ self.balance = self.safe_balance(self.balance)
1558
+ else:
1559
+ # inverse branch
1560
+ for i in range(0, len(data)):
1561
+ balance = data[i]
1562
+ currencyId = self.safe_string(balance, 'symbol')
1563
+ code = self.safe_currency_code(currencyId)
1564
+ account = self.account()
1565
+ account['free'] = self.safe_string(balance, 'margin_available')
1566
+ account['used'] = self.safe_string(balance, 'margin_frozen')
1567
+ self.balance[code] = account
1568
+ self.balance = self.safe_balance(self.balance)
1569
+ client.resolve(self.balance, messageHash)
1570
+
1571
+ def handle_subscription_status(self, client: Client, message):
1572
+ #
1573
+ # {
1574
+ # "id": 1583414227,
1575
+ # "status": "ok",
1576
+ # "subbed": "market.btcusdt.mbp.150",
1577
+ # "ts": 1583414229143
1578
+ # }
1579
+ #
1580
+ id = self.safe_string(message, 'id')
1581
+ subscriptionsById = self.index_by(client.subscriptions, 'id')
1582
+ subscription = self.safe_value(subscriptionsById, id)
1583
+ if subscription is not None:
1584
+ method = self.safe_value(subscription, 'method')
1585
+ if method is not None:
1586
+ method(client, message, subscription)
1587
+ return
1588
+ # clean up
1589
+ if id in client.subscriptions:
1590
+ del client.subscriptions[id]
1591
+
1592
+ def handle_system_status(self, client: Client, message):
1593
+ #
1594
+ # todo: answer the question whether handleSystemStatus should be renamed
1595
+ # and unified for any usage pattern that
1596
+ # involves system status and maintenance updates
1597
+ #
1598
+ # {
1599
+ # "id": "1578090234088", # connectId
1600
+ # "type": "welcome",
1601
+ # }
1602
+ #
1603
+ return message
1604
+
1605
+ def handle_subject(self, client: Client, message):
1606
+ # spot
1607
+ # {
1608
+ # "ch": "market.btcusdt.mbp.150",
1609
+ # "ts": 1583472025885,
1610
+ # "tick": {
1611
+ # "seqNum": 104998984994,
1612
+ # "prevSeqNum": 104998984977,
1613
+ # "bids": [
1614
+ # [9058.27, 0],
1615
+ # [9058.43, 0],
1616
+ # [9058.99, 0],
1617
+ # ],
1618
+ # "asks": [
1619
+ # [9084.27, 0.2],
1620
+ # [9085.69, 0],
1621
+ # [9085.81, 0],
1622
+ # ]
1623
+ # }
1624
+ # }
1625
+ # non spot
1626
+ #
1627
+ # {
1628
+ # "ch":"market.BTC220218.depth.size_150.high_freq",
1629
+ # "tick":{
1630
+ # "asks":[],
1631
+ # "bids":[
1632
+ # [43445.74,1],
1633
+ # [43444.48,0],
1634
+ # [40593.92,9]
1635
+ # ],
1636
+ # "ch":"market.BTC220218.depth.size_150.high_freq",
1637
+ # "event":"update",
1638
+ # "id":152727500274,
1639
+ # "mrid":152727500274,
1640
+ # "ts":1645023376098,
1641
+ # "version":37536690
1642
+ # },
1643
+ # "ts":1645023376098
1644
+ # }
1645
+ #
1646
+ # spot private trade
1647
+ #
1648
+ # {
1649
+ # "action":"push",
1650
+ # "ch":"trade.clearing#ltcusdt#1",
1651
+ # "data":{
1652
+ # "eventType":"trade",
1653
+ # "symbol":"ltcusdt",
1654
+ # # ...
1655
+ # },
1656
+ # }
1657
+ #
1658
+ # spot order
1659
+ #
1660
+ # {
1661
+ # "action":"push",
1662
+ # "ch":"orders#btcusdt",
1663
+ # "data": {
1664
+ # "orderSide":"buy",
1665
+ # "lastActTime":1583853365586,
1666
+ # "clientOrderId":"abc123",
1667
+ # "orderStatus":"rejected",
1668
+ # "symbol":"btcusdt",
1669
+ # "eventType":"trigger",
1670
+ # "errCode": 2002,
1671
+ # "errMessage":"invalid.client.order.id(NT)"
1672
+ # }
1673
+ # }
1674
+ #
1675
+ # contract order
1676
+ #
1677
+ # {
1678
+ # "op":"notify",
1679
+ # "topic":"orders.ada",
1680
+ # "ts":1604388667226,
1681
+ # # ?
1682
+ # }
1683
+ #
1684
+ ch = self.safe_value(message, 'ch', '')
1685
+ parts = ch.split('.')
1686
+ type = self.safe_string(parts, 0)
1687
+ if type == 'market':
1688
+ methodName = self.safe_string(parts, 2)
1689
+ methods: dict = {
1690
+ 'depth': self.handle_order_book,
1691
+ 'mbp': self.handle_order_book,
1692
+ 'detail': self.handle_ticker,
1693
+ 'bbo': self.handle_ticker,
1694
+ 'ticker': self.handle_ticker,
1695
+ 'trade': self.handle_trades,
1696
+ 'kline': self.handle_ohlcv,
1697
+ }
1698
+ method = self.safe_value(methods, methodName)
1699
+ if method is not None:
1700
+ method(client, message)
1701
+ return
1702
+ # private spot subjects
1703
+ privateParts = ch.split('#')
1704
+ privateType = self.safe_string(privateParts, 0, '')
1705
+ if privateType == 'trade.clearing':
1706
+ self.handle_my_trade(client, message)
1707
+ return
1708
+ if privateType.find('accounts.update') >= 0:
1709
+ self.handle_balance(client, message)
1710
+ return
1711
+ if privateType == 'orders':
1712
+ self.handle_order(client, message)
1713
+ return
1714
+ # private contract subjects
1715
+ op = self.safe_string(message, 'op')
1716
+ if op == 'notify':
1717
+ topic = self.safe_string(message, 'topic', '')
1718
+ if topic.find('orders') >= 0:
1719
+ self.handle_order(client, message)
1720
+ if topic.find('account') >= 0:
1721
+ self.handle_balance(client, message)
1722
+ if topic.find('positions') >= 0:
1723
+ self.handle_positions(client, message)
1724
+
1725
+ async def pong(self, client, message):
1726
+ #
1727
+ # {ping: 1583491673714}
1728
+ # {action: "ping", data: {ts: 1645108204665}}
1729
+ # {op: "ping", ts: "1645202800015"}
1730
+ #
1731
+ try:
1732
+ ping = self.safe_integer(message, 'ping')
1733
+ if ping is not None:
1734
+ await client.send({'pong': ping})
1735
+ return
1736
+ action = self.safe_string(message, 'action')
1737
+ if action == 'ping':
1738
+ data = self.safe_value(message, 'data')
1739
+ pingTs = self.safe_integer(data, 'ts')
1740
+ await client.send({'action': 'pong', 'data': {'ts': pingTs}})
1741
+ return
1742
+ op = self.safe_string(message, 'op')
1743
+ if op == 'ping':
1744
+ pingTs = self.safe_integer(message, 'ts')
1745
+ await client.send({'op': 'pong', 'ts': pingTs})
1746
+ except Exception as e:
1747
+ error = NetworkError(self.id + ' pong failed ' + self.json(e))
1748
+ client.reset(error)
1749
+
1750
+ def handle_ping(self, client: Client, message):
1751
+ self.spawn(self.pong, client, message)
1752
+
1753
+ def handle_authenticate(self, client: Client, message):
1754
+ #
1755
+ # spot
1756
+ #
1757
+ # {
1758
+ # "action": "req",
1759
+ # "code": 200,
1760
+ # "ch": "auth",
1761
+ # "data": {}
1762
+ # }
1763
+ #
1764
+ # non spot
1765
+ #
1766
+ # {
1767
+ # "op": "auth",
1768
+ # "type": "api",
1769
+ # "err-code": 0,
1770
+ # "ts": 1645200307319,
1771
+ # "data": {"user-id": "35930539"}
1772
+ # }
1773
+ #
1774
+ promise = client.futures['auth']
1775
+ promise.resolve(message)
1776
+
1777
+ def handle_error_message(self, client: Client, message):
1778
+ #
1779
+ # {
1780
+ # "action": "sub",
1781
+ # "code": 2002,
1782
+ # "ch": "accounts.update#2",
1783
+ # "message": "invalid.auth.state"
1784
+ # }
1785
+ #
1786
+ # {
1787
+ # "ts": 1586323747018,
1788
+ # "status": "error",
1789
+ # 'err-code': "bad-request",
1790
+ # 'err-msg': "invalid mbp.150.symbol linkusdt",
1791
+ # "id": "2"
1792
+ # }
1793
+ #
1794
+ # {
1795
+ # "op": "sub",
1796
+ # "cid": "1",
1797
+ # "topic": "accounts_unify.USDT",
1798
+ # "err-code": 4007,
1799
+ # 'err-msg': "Non - single account user is not available, please check through the cross and isolated account asset interface",
1800
+ # "ts": 1698419490189
1801
+ # }
1802
+ # {
1803
+ # "action":"req",
1804
+ # "code":2002,
1805
+ # "ch":"auth",
1806
+ # "message":"auth.fail"
1807
+ # }
1808
+ #
1809
+ status = self.safe_string(message, 'status')
1810
+ if status == 'error':
1811
+ id = self.safe_string(message, 'id')
1812
+ subscriptionsById = self.index_by(client.subscriptions, 'id')
1813
+ subscription = self.safe_value(subscriptionsById, id)
1814
+ if subscription is not None:
1815
+ errorCode = self.safe_string(message, 'err-code')
1816
+ try:
1817
+ self.throw_exactly_matched_exception(self.exceptions['ws']['exact'], errorCode, self.json(message))
1818
+ raise ExchangeError(self.json(message))
1819
+ except Exception as e:
1820
+ messageHash = self.safe_string(subscription, 'messageHash')
1821
+ client.reject(e, messageHash)
1822
+ client.reject(e, id)
1823
+ if id in client.subscriptions:
1824
+ del client.subscriptions[id]
1825
+ return False
1826
+ code = self.safe_string_2(message, 'code', 'err-code')
1827
+ if code is not None and ((code != '200') and (code != '0')):
1828
+ feedback = self.id + ' ' + self.json(message)
1829
+ try:
1830
+ self.throw_exactly_matched_exception(self.exceptions['ws']['exact'], code, feedback)
1831
+ raise ExchangeError(feedback)
1832
+ except Exception as e:
1833
+ if isinstance(e, AuthenticationError):
1834
+ client.reject(e, 'auth')
1835
+ method = 'auth'
1836
+ if method in client.subscriptions:
1837
+ del client.subscriptions[method]
1838
+ return False
1839
+ else:
1840
+ client.reject(e)
1841
+ return message
1842
+
1843
+ def handle_message(self, client: Client, message):
1844
+ if self.handle_error_message(client, message):
1845
+ #
1846
+ # {"id":1583414227,"status":"ok","subbed":"market.btcusdt.mbp.150","ts":1583414229143}
1847
+ #
1848
+ # first ping format
1849
+ #
1850
+ # {"ping": 1645106821667}
1851
+ #
1852
+ # second ping format
1853
+ #
1854
+ # {"action":"ping","data":{"ts":1645106821667}}
1855
+ #
1856
+ # third pong format
1857
+ #
1858
+ #
1859
+ # auth spot
1860
+ #
1861
+ # {
1862
+ # "action": "req",
1863
+ # "code": 200,
1864
+ # "ch": "auth",
1865
+ # "data": {}
1866
+ # }
1867
+ #
1868
+ # auth non spot
1869
+ #
1870
+ # {
1871
+ # "op": "auth",
1872
+ # "type": "api",
1873
+ # "err-code": 0,
1874
+ # "ts": 1645200307319,
1875
+ # "data": {"user-id": "35930539"}
1876
+ # }
1877
+ #
1878
+ # trade
1879
+ #
1880
+ # {
1881
+ # "action":"push",
1882
+ # "ch":"trade.clearing#ltcusdt#1",
1883
+ # "data":{
1884
+ # "eventType":"trade",
1885
+ # # ?
1886
+ # }
1887
+ # }
1888
+ #
1889
+ if 'id' in message:
1890
+ self.handle_subscription_status(client, message)
1891
+ return
1892
+ if 'action' in message:
1893
+ action = self.safe_string(message, 'action')
1894
+ if action == 'ping':
1895
+ self.handle_ping(client, message)
1896
+ return
1897
+ if action == 'sub':
1898
+ self.handle_subscription_status(client, message)
1899
+ return
1900
+ if 'ch' in message:
1901
+ if message['ch'] == 'auth':
1902
+ self.handle_authenticate(client, message)
1903
+ return
1904
+ else:
1905
+ # route by channel aka topic aka subject
1906
+ self.handle_subject(client, message)
1907
+ return
1908
+ if 'op' in message:
1909
+ op = self.safe_string(message, 'op')
1910
+ if op == 'ping':
1911
+ self.handle_ping(client, message)
1912
+ return
1913
+ if op == 'auth':
1914
+ self.handle_authenticate(client, message)
1915
+ return
1916
+ if op == 'sub':
1917
+ self.handle_subscription_status(client, message)
1918
+ return
1919
+ if op == 'notify':
1920
+ self.handle_subject(client, message)
1921
+ return
1922
+ if 'ping' in message:
1923
+ self.handle_ping(client, message)
1924
+
1925
+ def handle_my_trade(self, client: Client, message, extendParams={}):
1926
+ #
1927
+ # spot
1928
+ #
1929
+ # {
1930
+ # "action":"push",
1931
+ # "ch":"trade.clearing#ltcusdt#1",
1932
+ # "data":{
1933
+ # "eventType":"trade",
1934
+ # "symbol":"ltcusdt",
1935
+ # "orderId":"478862728954426",
1936
+ # "orderSide":"buy",
1937
+ # "orderType":"buy-market",
1938
+ # "accountId":44234548,
1939
+ # "source":"spot-web",
1940
+ # "orderValue":"5.01724137",
1941
+ # "orderCreateTime":1645124660365,
1942
+ # "orderStatus":"filled",
1943
+ # "feeCurrency":"ltc",
1944
+ # "tradePrice":"118.89",
1945
+ # "tradeVolume":"0.042200701236437042",
1946
+ # "aggressor":true,
1947
+ # "tradeId":101539740584,
1948
+ # "tradeTime":1645124660368,
1949
+ # "transactFee":"0.000041778694224073",
1950
+ # "feeDeduct":"0",
1951
+ # "feeDeductType":""
1952
+ # }
1953
+ # }
1954
+ #
1955
+ # contract
1956
+ #
1957
+ # {
1958
+ # "symbol": "ADA/USDT:USDT"
1959
+ # "ch": "orders_cross.ada-usdt"
1960
+ # "trades": [
1961
+ # {
1962
+ # "trade_fee":-0.022099447513812154,
1963
+ # "fee_asset":"ADA",
1964
+ # "trade_id":113913755890,
1965
+ # "id":"113913755890-773207641127878656-1",
1966
+ # "trade_volume":1,
1967
+ # "trade_price":0.0905,
1968
+ # "trade_turnover":10,
1969
+ # "created_at":1604388667194,
1970
+ # "profit":0,
1971
+ # "real_profit": 0,
1972
+ # "role":"maker"
1973
+ # }
1974
+ # ],
1975
+ # }
1976
+ #
1977
+ if self.myTrades is None:
1978
+ limit = self.safe_integer(self.options, 'tradesLimit', 1000)
1979
+ self.myTrades = ArrayCacheBySymbolById(limit)
1980
+ cachedTrades = self.myTrades
1981
+ messageHash = self.safe_string(message, 'ch')
1982
+ if messageHash is not None:
1983
+ data = self.safe_value(message, 'data')
1984
+ if data is not None:
1985
+ parsed = self.parse_ws_trade(data)
1986
+ symbol = self.safe_string(parsed, 'symbol')
1987
+ if symbol is not None:
1988
+ cachedTrades.append(parsed)
1989
+ client.resolve(self.myTrades, messageHash)
1990
+ else:
1991
+ # self trades object is artificially created
1992
+ # in handleOrder
1993
+ rawTrades = self.safe_value(message, 'trades', [])
1994
+ marketId = self.safe_value(message, 'symbol')
1995
+ market = self.market(marketId)
1996
+ for i in range(0, len(rawTrades)):
1997
+ trade = rawTrades[i]
1998
+ parsedTrade = self.parse_trade(trade, market)
1999
+ # add extra params(side, type, ...) coming from the order
2000
+ parsedTrade = self.extend(parsedTrade, extendParams)
2001
+ cachedTrades.append(parsedTrade)
2002
+ # messageHash here is the orders one, so
2003
+ # we have to recreate the trades messageHash = orderMessageHash + ':' + 'trade'
2004
+ tradesHash = messageHash + ':' + 'trade'
2005
+ client.resolve(self.myTrades, tradesHash)
2006
+ # when we make an global order sub we have to send the channel like self
2007
+ # ch = orders_cross.* and we store messageHash = 'orders_cross'
2008
+ # however it is returned with the specific order update symbol: ch = orders_cross.btc-usd
2009
+ # since self is a global sub, our messageHash does not specify any symbol(ex: orders_cross:trade)
2010
+ # so we must remove it
2011
+ genericOrderHash = messageHash.replace('.' + market['lowercaseId'], '')
2012
+ lowerCaseBaseId = self.safe_string_lower(market, 'baseId')
2013
+ genericOrderHash = genericOrderHash.replace('.' + lowerCaseBaseId, '')
2014
+ genericTradesHash = genericOrderHash + ':' + 'trade'
2015
+ client.resolve(self.myTrades, genericTradesHash)
2016
+
2017
+ def parse_ws_trade(self, trade, market=None):
2018
+ # spot private
2019
+ #
2020
+ # {
2021
+ # "eventType":"trade",
2022
+ # "symbol":"ltcusdt",
2023
+ # "orderId":"478862728954426",
2024
+ # "orderSide":"buy",
2025
+ # "orderType":"buy-market",
2026
+ # "accountId":44234548,
2027
+ # "source":"spot-web",
2028
+ # "orderValue":"5.01724137",
2029
+ # "orderCreateTime":1645124660365,
2030
+ # "orderStatus":"filled",
2031
+ # "feeCurrency":"ltc",
2032
+ # "tradePrice":"118.89",
2033
+ # "tradeVolume":"0.042200701236437042",
2034
+ # "aggressor":true,
2035
+ # "tradeId":101539740584,
2036
+ # "tradeTime":1645124660368,
2037
+ # "transactFee":"0.000041778694224073",
2038
+ # "feeDeduct":"0",
2039
+ # "feeDeductType":""
2040
+ # }
2041
+ #
2042
+ symbol = self.safe_symbol(self.safe_string(trade, 'symbol'))
2043
+ side = self.safe_string_2(trade, 'side', 'orderSide')
2044
+ tradeId = self.safe_string(trade, 'tradeId')
2045
+ price = self.safe_string(trade, 'tradePrice')
2046
+ amount = self.safe_string(trade, 'tradeVolume')
2047
+ order = self.safe_string(trade, 'orderId')
2048
+ timestamp = self.safe_integer(trade, 'tradeTime')
2049
+ market = self.market(symbol)
2050
+ orderType = self.safe_string(trade, 'orderType')
2051
+ aggressor = self.safe_value(trade, 'aggressor')
2052
+ takerOrMaker = None
2053
+ if aggressor is not None:
2054
+ takerOrMaker = 'taker' if aggressor else 'maker'
2055
+ type = None
2056
+ orderTypeParts = []
2057
+ if orderType is not None:
2058
+ orderTypeParts = orderType.split('-')
2059
+ type = self.safe_string(orderTypeParts, 1)
2060
+ fee = None
2061
+ feeCurrency = self.safe_currency_code(self.safe_string(trade, 'feeCurrency'))
2062
+ if feeCurrency is not None:
2063
+ fee = {
2064
+ 'cost': self.safe_string(trade, 'transactFee'),
2065
+ 'currency': feeCurrency,
2066
+ }
2067
+ return self.safe_trade({
2068
+ 'info': trade,
2069
+ 'timestamp': timestamp,
2070
+ 'datetime': self.iso8601(timestamp),
2071
+ 'symbol': symbol,
2072
+ 'id': tradeId,
2073
+ 'order': order,
2074
+ 'type': type,
2075
+ 'takerOrMaker': takerOrMaker,
2076
+ 'side': side,
2077
+ 'price': price,
2078
+ 'amount': amount,
2079
+ 'cost': None,
2080
+ 'fee': fee,
2081
+ }, market)
2082
+
2083
+ def get_url_by_market_type(self, type, isLinear=True, isPrivate=False, isFeed=False):
2084
+ api = self.safe_string(self.options, 'api', 'api')
2085
+ hostname: dict = {'hostname': self.hostname}
2086
+ hostnameURL = None
2087
+ url = None
2088
+ if type == 'spot':
2089
+ if isPrivate:
2090
+ hostnameURL = self.urls['api']['ws'][api]['spot']['private']
2091
+ else:
2092
+ if isFeed:
2093
+ hostnameURL = self.urls['api']['ws'][api]['spot']['feed']
2094
+ else:
2095
+ hostnameURL = self.urls['api']['ws'][api]['spot']['public']
2096
+ url = self.implode_params(hostnameURL, hostname)
2097
+ else:
2098
+ baseUrl = self.urls['api']['ws'][api][type]
2099
+ subTypeUrl = baseUrl['linear'] if isLinear else baseUrl['inverse']
2100
+ url = subTypeUrl['private'] if isPrivate else subTypeUrl['public']
2101
+ return url
2102
+
2103
+ async def subscribe_public(self, url, symbol, messageHash, method=None, params={}):
2104
+ requestId = self.request_id()
2105
+ request: dict = {
2106
+ 'sub': messageHash,
2107
+ 'id': requestId,
2108
+ }
2109
+ subscription: dict = {
2110
+ 'id': requestId,
2111
+ 'messageHash': messageHash,
2112
+ 'symbol': symbol,
2113
+ 'params': params,
2114
+ }
2115
+ if method is not None:
2116
+ subscription['method'] = method
2117
+ return await self.watch(url, messageHash, self.extend(request, params), messageHash, subscription)
2118
+
2119
+ async def subscribe_private(self, channel, messageHash, type, subtype, params={}, subscriptionParams={}):
2120
+ requestId = self.request_id()
2121
+ subscription: dict = {
2122
+ 'id': requestId,
2123
+ 'messageHash': messageHash,
2124
+ 'params': params,
2125
+ }
2126
+ extendedSubsription = self.extend(subscription, subscriptionParams)
2127
+ request = None
2128
+ if type == 'spot':
2129
+ request = {
2130
+ 'action': 'sub',
2131
+ 'ch': channel,
2132
+ }
2133
+ else:
2134
+ request = {
2135
+ 'op': 'sub',
2136
+ 'topic': channel,
2137
+ 'cid': requestId,
2138
+ }
2139
+ isLinear = subtype == 'linear'
2140
+ url = self.get_url_by_market_type(type, isLinear, True)
2141
+ hostname = self.urls['hostnames']['spot'] if (type == 'spot') else self.urls['hostnames']['contract']
2142
+ authParams: dict = {
2143
+ 'type': type,
2144
+ 'url': url,
2145
+ 'hostname': hostname,
2146
+ }
2147
+ await self.authenticate(authParams)
2148
+ return await self.watch(url, messageHash, self.extend(request, params), channel, extendedSubsription)
2149
+
2150
+ async def authenticate(self, params={}):
2151
+ url = self.safe_string(params, 'url')
2152
+ hostname = self.safe_string(params, 'hostname')
2153
+ type = self.safe_string(params, 'type')
2154
+ if url is None or hostname is None or type is None:
2155
+ raise ArgumentsRequired(self.id + ' authenticate requires a url, hostname and type argument')
2156
+ self.check_required_credentials()
2157
+ messageHash = 'auth'
2158
+ relativePath = url.replace('wss://' + hostname, '')
2159
+ client = self.client(url)
2160
+ future = client.future(messageHash)
2161
+ authenticated = self.safe_value(client.subscriptions, messageHash)
2162
+ if authenticated is None:
2163
+ timestamp = self.ymdhms(self.milliseconds(), 'T')
2164
+ signatureParams = None
2165
+ if type == 'spot':
2166
+ signatureParams = {
2167
+ 'accessKey': self.apiKey,
2168
+ 'signatureMethod': 'HmacSHA256',
2169
+ 'signatureVersion': '2.1',
2170
+ 'timestamp': timestamp,
2171
+ }
2172
+ else:
2173
+ signatureParams = {
2174
+ 'AccessKeyId': self.apiKey,
2175
+ 'SignatureMethod': 'HmacSHA256',
2176
+ 'SignatureVersion': '2',
2177
+ 'Timestamp': timestamp,
2178
+ }
2179
+ signatureParams = self.keysort(signatureParams)
2180
+ auth = self.urlencode(signatureParams)
2181
+ payload = "\n".join(['GET', hostname, relativePath, auth]) # eslint-disable-line quotes
2182
+ signature = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha256, 'base64')
2183
+ request = None
2184
+ if type == 'spot':
2185
+ newParams: dict = {
2186
+ 'authType': 'api',
2187
+ 'accessKey': self.apiKey,
2188
+ 'signatureMethod': 'HmacSHA256',
2189
+ 'signatureVersion': '2.1',
2190
+ 'timestamp': timestamp,
2191
+ 'signature': signature,
2192
+ }
2193
+ request = {
2194
+ 'params': newParams,
2195
+ 'action': 'req',
2196
+ 'ch': 'auth',
2197
+ }
2198
+ else:
2199
+ request = {
2200
+ 'op': 'auth',
2201
+ 'type': 'api',
2202
+ 'AccessKeyId': self.apiKey,
2203
+ 'SignatureMethod': 'HmacSHA256',
2204
+ 'SignatureVersion': '2',
2205
+ 'Timestamp': timestamp,
2206
+ 'Signature': signature,
2207
+ }
2208
+ requestId = self.request_id()
2209
+ subscription: dict = {
2210
+ 'id': requestId,
2211
+ 'messageHash': messageHash,
2212
+ 'params': params,
2213
+ }
2214
+ self.watch(url, messageHash, request, messageHash, subscription)
2215
+ return await future