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
@@ -0,0 +1,2462 @@
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
+ from ccxt.async_support.base.exchange import Exchange
7
+ from ccxt.abstract.exmo import ImplicitAPI
8
+ import hashlib
9
+ from ccxt.base.types import Balances, Currencies, Currency, Int, MarginModification, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFees, Transaction
10
+ from typing import List
11
+ from ccxt.base.errors import ExchangeError
12
+ from ccxt.base.errors import AuthenticationError
13
+ from ccxt.base.errors import PermissionDenied
14
+ from ccxt.base.errors import ArgumentsRequired
15
+ from ccxt.base.errors import BadRequest
16
+ from ccxt.base.errors import InsufficientFunds
17
+ from ccxt.base.errors import InvalidOrder
18
+ from ccxt.base.errors import OrderNotFound
19
+ from ccxt.base.errors import RateLimitExceeded
20
+ from ccxt.base.errors import OnMaintenance
21
+ from ccxt.base.errors import InvalidNonce
22
+ from ccxt.base.decimal_to_precision import TICK_SIZE
23
+ from ccxt.base.precise import Precise
24
+
25
+
26
+ class exmo(Exchange, ImplicitAPI):
27
+
28
+ def describe(self):
29
+ return self.deep_extend(super(exmo, self).describe(), {
30
+ 'id': 'exmo',
31
+ 'name': 'EXMO',
32
+ 'countries': ['LT'], # Lithuania
33
+ 'rateLimit': 350, # once every 350 ms ≈ 180 requests per minute ≈ 3 requests per second
34
+ 'version': 'v1.1',
35
+ 'has': {
36
+ 'CORS': None,
37
+ 'spot': True,
38
+ 'margin': True,
39
+ 'swap': False,
40
+ 'future': False,
41
+ 'option': False,
42
+ 'addMargin': True,
43
+ 'cancelOrder': True,
44
+ 'cancelOrders': False,
45
+ 'createDepositAddress': False,
46
+ 'createOrder': True,
47
+ 'createStopLimitOrder': True,
48
+ 'createStopMarketOrder': True,
49
+ 'createStopOrder': True,
50
+ 'editOrder': True, # margin only
51
+ 'fetchAccounts': False,
52
+ 'fetchBalance': True,
53
+ 'fetchCanceledOrders': True,
54
+ 'fetchCurrencies': True,
55
+ 'fetchDeposit': True,
56
+ 'fetchDepositAddress': True,
57
+ 'fetchDeposits': True,
58
+ 'fetchDepositsWithdrawals': True,
59
+ 'fetchDepositWithdrawFee': 'emulated',
60
+ 'fetchDepositWithdrawFees': True,
61
+ 'fetchFundingHistory': False,
62
+ 'fetchFundingRate': False,
63
+ 'fetchFundingRateHistory': False,
64
+ 'fetchFundingRates': False,
65
+ 'fetchIndexOHLCV': False,
66
+ 'fetchMarginMode': False,
67
+ 'fetchMarkets': True,
68
+ 'fetchMarkOHLCV': False,
69
+ 'fetchMyTrades': True,
70
+ 'fetchOHLCV': True,
71
+ 'fetchOpenInterestHistory': False,
72
+ 'fetchOpenOrders': True,
73
+ 'fetchOrder': 'emulated',
74
+ 'fetchOrderBook': True,
75
+ 'fetchOrderBooks': True,
76
+ 'fetchOrderTrades': True,
77
+ 'fetchPosition': False,
78
+ 'fetchPositionHistory': False,
79
+ 'fetchPositionMode': False,
80
+ 'fetchPositions': False,
81
+ 'fetchPositionsHistory': False,
82
+ 'fetchPositionsRisk': False,
83
+ 'fetchPremiumIndexOHLCV': False,
84
+ 'fetchTicker': True,
85
+ 'fetchTickers': True,
86
+ 'fetchTrades': True,
87
+ 'fetchTradingFee': False,
88
+ 'fetchTradingFees': True,
89
+ 'fetchTransactionFees': True,
90
+ 'fetchTransactions': 'emulated',
91
+ 'fetchTransfer': False,
92
+ 'fetchTransfers': False,
93
+ 'fetchWithdrawal': True,
94
+ 'fetchWithdrawals': True,
95
+ 'reduceMargin': True,
96
+ 'setMargin': False,
97
+ 'transfer': False,
98
+ 'withdraw': True,
99
+ },
100
+ 'timeframes': {
101
+ '1m': '1',
102
+ '5m': '5',
103
+ '15m': '15',
104
+ '30m': '30',
105
+ '45m': '45',
106
+ '1h': '60',
107
+ '2h': '120',
108
+ '3h': '180',
109
+ '4h': '240',
110
+ '1d': 'D',
111
+ '1w': 'W',
112
+ '1M': 'M',
113
+ },
114
+ 'urls': {
115
+ 'logo': 'https://user-images.githubusercontent.com/1294454/27766491-1b0ea956-5eda-11e7-9225-40d67b481b8d.jpg',
116
+ 'api': {
117
+ 'public': 'https://api.exmo.com',
118
+ 'private': 'https://api.exmo.com',
119
+ 'web': 'https://exmo.me',
120
+ },
121
+ 'www': 'https://exmo.me',
122
+ 'referral': 'https://exmo.me/?ref=131685',
123
+ 'doc': [
124
+ 'https://exmo.me/en/api_doc?ref=131685',
125
+ ],
126
+ 'fees': 'https://exmo.com/en/docs/fees',
127
+ },
128
+ 'api': {
129
+ 'web': {
130
+ 'get': [
131
+ 'ctrl/feesAndLimits',
132
+ 'en/docs/fees',
133
+ ],
134
+ },
135
+ 'public': {
136
+ 'get': [
137
+ 'currency',
138
+ 'currency/list/extended',
139
+ 'order_book',
140
+ 'pair_settings',
141
+ 'ticker',
142
+ 'trades',
143
+ 'candles_history',
144
+ 'required_amount',
145
+ 'payments/providers/crypto/list',
146
+ ],
147
+ },
148
+ 'private': {
149
+ 'post': [
150
+ 'user_info',
151
+ 'order_create',
152
+ 'order_cancel',
153
+ 'stop_market_order_create',
154
+ 'stop_market_order_cancel',
155
+ 'user_open_orders',
156
+ 'user_trades',
157
+ 'user_cancelled_orders',
158
+ 'order_trades',
159
+ 'deposit_address',
160
+ 'withdraw_crypt',
161
+ 'withdraw_get_txid',
162
+ 'excode_create',
163
+ 'excode_load',
164
+ 'code_check',
165
+ 'wallet_history',
166
+ 'wallet_operations',
167
+ 'margin/user/order/create',
168
+ 'margin/user/order/update',
169
+ 'margin/user/order/cancel',
170
+ 'margin/user/position/close',
171
+ 'margin/user/position/margin_add',
172
+ 'margin/user/position/margin_remove',
173
+ 'margin/currency/list',
174
+ 'margin/pair/list',
175
+ 'margin/settings',
176
+ 'margin/funding/list',
177
+ 'margin/user/info',
178
+ 'margin/user/order/list',
179
+ 'margin/user/order/history',
180
+ 'margin/user/order/trades',
181
+ 'margin/user/order/max_quantity',
182
+ 'margin/user/position/list',
183
+ 'margin/user/position/margin_remove_info',
184
+ 'margin/user/position/margin_add_info',
185
+ 'margin/user/wallet/list',
186
+ 'margin/user/wallet/history',
187
+ 'margin/user/trade/list',
188
+ 'margin/trades',
189
+ 'margin/liquidation/feed',
190
+ ],
191
+ },
192
+ },
193
+ 'fees': {
194
+ 'trading': {
195
+ 'feeSide': 'get',
196
+ 'tierBased': True,
197
+ 'percentage': True,
198
+ 'maker': self.parse_number('0.004'),
199
+ 'taker': self.parse_number('0.004'),
200
+ },
201
+ 'transaction': {
202
+ 'tierBased': False,
203
+ 'percentage': False, # fixed transaction fees for crypto, see fetchDepositWithdrawFees below
204
+ },
205
+ },
206
+ 'options': {
207
+ 'networks': {
208
+ 'ETH': 'ERC20',
209
+ 'TRX': 'TRC20',
210
+ },
211
+ 'fetchTradingFees': {
212
+ 'method': 'fetchPrivateTradingFees', # or 'fetchPublicTradingFees'
213
+ },
214
+ 'margin': {
215
+ 'fillResponseFromRequest': True,
216
+ },
217
+ },
218
+ 'commonCurrencies': {
219
+ 'GMT': 'GMT Token',
220
+ },
221
+ 'precisionMode': TICK_SIZE,
222
+ 'exceptions': {
223
+ 'exact': {
224
+ '140434': BadRequest,
225
+ '40005': AuthenticationError, # Authorization error, incorrect signature
226
+ '40009': InvalidNonce, #
227
+ '40015': ExchangeError, # API function do not exist
228
+ '40016': OnMaintenance, # {"result":false,"error":"Error 40016: Maintenance work in progress"}
229
+ '40017': AuthenticationError, # Wrong API Key
230
+ '40032': PermissionDenied, # {"result":false,"error":"Error 40032: Access is denied for self API key"}
231
+ '40033': PermissionDenied, # {"result":false,"error":"Error 40033: Access is denied, self resources are temporarily blocked to user"}
232
+ '40034': RateLimitExceeded, # {"result":false,"error":"Error 40034: Access is denied, rate limit is exceeded"}
233
+ '50052': InsufficientFunds,
234
+ '50054': InsufficientFunds,
235
+ '50304': OrderNotFound, # "Order was not found '123456789'"(fetching order trades for an order that does not have trades yet)
236
+ '50173': OrderNotFound, # "Order with id X was not found."(cancelling non-existent, closed and cancelled order)
237
+ '50277': InvalidOrder,
238
+ '50319': InvalidOrder, # Price by order is less than permissible minimum for self pair
239
+ '50321': InvalidOrder, # Price by order is more than permissible maximum for self pair
240
+ '50381': InvalidOrder, # {"result":false,"error":"Error 50381: More than 2 decimal places are not permitted for pair BTC_USD"}
241
+ },
242
+ 'broad': {
243
+ 'range period is too long': BadRequest,
244
+ 'invalid syntax': BadRequest,
245
+ 'API rate limit exceeded': RateLimitExceeded, # {"result":false,"error":"API rate limit exceeded for x.x.x.x. Retry after 60 sec.","history":[],"begin":1579392000,"end":1579478400}
246
+ },
247
+ },
248
+ })
249
+
250
+ async def modify_margin_helper(self, symbol: str, amount, type, params={}):
251
+ await self.load_markets()
252
+ market = self.market(symbol)
253
+ request: dict = {
254
+ 'position_id': market['id'],
255
+ 'quantity': amount,
256
+ }
257
+ response = None
258
+ if type == 'add':
259
+ response = await self.privatePostMarginUserPositionMarginAdd(self.extend(request, params))
260
+ elif type == 'reduce':
261
+ response = await self.privatePostMarginUserPositionMarginRemove(self.extend(request, params))
262
+ #
263
+ # {}
264
+ #
265
+ margin = self.parse_margin_modification(response, market)
266
+ options = self.safe_value(self.options, 'margin', {})
267
+ fillResponseFromRequest = self.safe_bool(options, 'fillResponseFromRequest', True)
268
+ if fillResponseFromRequest:
269
+ margin['type'] = type
270
+ margin['amount'] = amount
271
+ return margin
272
+
273
+ def parse_margin_modification(self, data: dict, market: Market = None) -> MarginModification:
274
+ #
275
+ # {}
276
+ #
277
+ return {
278
+ 'info': data,
279
+ 'symbol': self.safe_symbol(None, market),
280
+ 'type': None,
281
+ 'marginMode': 'isolated',
282
+ 'amount': None,
283
+ 'total': None,
284
+ 'code': self.safe_value(market, 'quote'),
285
+ 'status': 'ok',
286
+ 'timestamp': None,
287
+ 'datetime': None,
288
+ }
289
+
290
+ async def reduce_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
291
+ """
292
+ remove margin from a position
293
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#eebf9f25-0289-4946-9482-89872c738449
294
+ :param str symbol: unified market symbol
295
+ :param float amount: the amount of margin to remove
296
+ :param dict [params]: extra parameters specific to the exchange API endpoint
297
+ :returns dict: a `margin structure <https://docs.ccxt.com/#/?id=reduce-margin-structure>`
298
+ """
299
+ return await self.modify_margin_helper(symbol, amount, 'reduce', params)
300
+
301
+ async def add_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
302
+ """
303
+ add margin
304
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#143ef808-79ca-4e49-9e79-a60ea4d8c0e3
305
+ :param str symbol: unified market symbol
306
+ :param float amount: amount of margin to add
307
+ :param dict [params]: extra parameters specific to the exchange API endpoint
308
+ :returns dict: a `margin structure <https://docs.ccxt.com/#/?id=add-margin-structure>`
309
+ """
310
+ return await self.modify_margin_helper(symbol, amount, 'add', params)
311
+
312
+ async def fetch_trading_fees(self, params={}) -> TradingFees:
313
+ """
314
+ fetch the trading fees for multiple markets
315
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#90927062-256c-4b03-900f-2b99131f9a54
316
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#7de7e75c-5833-45a8-b937-c2276d235aaa
317
+ :param dict [params]: extra parameters specific to the exchange API endpoint
318
+ :returns dict: a dictionary of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>` indexed by market symbols
319
+ """
320
+ options = self.safe_value(self.options, 'fetchTradingFees', {})
321
+ defaultMethod = self.safe_string(options, 'method', 'fetchPrivateTradingFees')
322
+ method = self.safe_string(params, 'method', defaultMethod)
323
+ params = self.omit(params, 'method')
324
+ if method == 'fetchPrivateTradingFees':
325
+ return await self.fetch_private_trading_fees(params)
326
+ else:
327
+ return await self.fetch_public_trading_fees(params)
328
+
329
+ async def fetch_private_trading_fees(self, params={}):
330
+ await self.load_markets()
331
+ response = await self.privatePostMarginPairList(params)
332
+ #
333
+ # {
334
+ # "pairs": [{
335
+ # "name": "EXM_USD",
336
+ # "buy_price": "0.02728391",
337
+ # "sell_price": "0.0276",
338
+ # "last_trade_price": "0.0276",
339
+ # "ticker_updated": "1646956050056696046",
340
+ # "is_fair_price": True,
341
+ # "max_price_precision": "8",
342
+ # "min_order_quantity": "1",
343
+ # "max_order_quantity": "50000",
344
+ # "min_order_price": "0.00000001",
345
+ # "max_order_price": "1000",
346
+ # "max_position_quantity": "50000",
347
+ # "trade_taker_fee": "0.05",
348
+ # "trade_maker_fee": "0",
349
+ # "liquidation_fee": "0.5",
350
+ # "max_leverage": "3",
351
+ # "default_leverage": "3",
352
+ # "liquidation_level": "5",
353
+ # "margin_call_level": "7.5",
354
+ # "position": "1",
355
+ # "updated": "1638976144797807397"
356
+ # }
357
+ # ...
358
+ # ]
359
+ # }
360
+ #
361
+ pairs = self.safe_value(response, 'pairs', [])
362
+ result: dict = {}
363
+ for i in range(0, len(pairs)):
364
+ pair = pairs[i]
365
+ marketId = self.safe_string(pair, 'name')
366
+ symbol = self.safe_symbol(marketId, None, '_')
367
+ makerString = self.safe_string(pair, 'trade_maker_fee')
368
+ takerString = self.safe_string(pair, 'trade_taker_fee')
369
+ maker = self.parse_number(Precise.string_div(makerString, '100'))
370
+ taker = self.parse_number(Precise.string_div(takerString, '100'))
371
+ result[symbol] = {
372
+ 'info': pair,
373
+ 'symbol': symbol,
374
+ 'maker': maker,
375
+ 'taker': taker,
376
+ 'percentage': True,
377
+ 'tierBased': True,
378
+ }
379
+ return result
380
+
381
+ async def fetch_public_trading_fees(self, params={}):
382
+ await self.load_markets()
383
+ response = await self.publicGetPairSettings(params)
384
+ #
385
+ # {
386
+ # "BTC_USD": {
387
+ # "min_quantity": "0.00002",
388
+ # "max_quantity": "1000",
389
+ # "min_price": "1",
390
+ # "max_price": "150000",
391
+ # "max_amount": "500000",
392
+ # "min_amount": "1",
393
+ # "price_precision": "2",
394
+ # "commission_taker_percent": "0.3",
395
+ # "commission_maker_percent": "0.3"
396
+ # },
397
+ # }
398
+ #
399
+ result: dict = {}
400
+ for i in range(0, len(self.symbols)):
401
+ symbol = self.symbols[i]
402
+ market = self.market(symbol)
403
+ fee = self.safe_value(response, market['id'], {})
404
+ makerString = self.safe_string(fee, 'commission_maker_percent')
405
+ takerString = self.safe_string(fee, 'commission_taker_percent')
406
+ maker = self.parse_number(Precise.string_div(makerString, '100'))
407
+ taker = self.parse_number(Precise.string_div(takerString, '100'))
408
+ result[symbol] = {
409
+ 'info': fee,
410
+ 'symbol': symbol,
411
+ 'maker': maker,
412
+ 'taker': taker,
413
+ 'percentage': True,
414
+ 'tierBased': True,
415
+ }
416
+ return result
417
+
418
+ def parse_fixed_float_value(self, input):
419
+ if (input is None) or (input == '-'):
420
+ return None
421
+ if input == '':
422
+ return 0
423
+ isPercentage = (input.find('%') >= 0)
424
+ parts = input.split(' ')
425
+ value = parts[0].replace('%', '')
426
+ result = float(value)
427
+ if (result > 0) and isPercentage:
428
+ raise ExchangeError(self.id + ' parseFixedFloatValue() detected an unsupported non-zero percentage-based fee ' + input)
429
+ return result
430
+
431
+ async def fetch_transaction_fees(self, codes: Strings = None, params={}):
432
+ """
433
+ * @deprecated
434
+ please use fetchDepositWithdrawFees instead
435
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#4190035d-24b1-453d-833b-37e0a52f88e2
436
+ :param str[]|None codes: list of unified currency codes
437
+ :param dict [params]: extra parameters specific to the exchange API endpoint
438
+ :returns dict: a list of `transaction fees structures <https://docs.ccxt.com/#/?id=fees-structure>`
439
+ """
440
+ await self.load_markets()
441
+ cryptoList = await self.publicGetPaymentsProvidersCryptoList(params)
442
+ #
443
+ # {
444
+ # "BTC":[
445
+ # {"type":"deposit", "name":"BTC", "currency_name":"BTC", "min":"0.001", "max":"0", "enabled":true,"comment":"Minimum deposit amount is 0.001 BTC. We do not support BSC and BEP20 network, please consider self when sending funds", "commission_desc":"0%", "currency_confirmations":1},
446
+ # {"type":"withdraw", "name":"BTC", "currency_name":"BTC", "min":"0.001", "max":"350", "enabled":true,"comment":"Do not withdraw directly to the Crowdfunding or ICO address account will not be credited with tokens from such sales.", "commission_desc":"0.0005 BTC", "currency_confirmations":6}
447
+ # ],
448
+ # "ETH":[
449
+ # {"type":"withdraw", "name":"ETH", "currency_name":"ETH", "min":"0.01", "max":"500", "enabled":true,"comment":"Do not withdraw directly to the Crowdfunding or ICO address account will not be credited with tokens from such sales.", "commission_desc":"0.004 ETH", "currency_confirmations":4},
450
+ # {"type":"deposit", "name":"ETH", "currency_name":"ETH", "min":"0.01", "max":"0", "enabled":true,"comment":"Minimum deposit amount is 0.01 ETH. We do not support BSC and BEP20 network, please consider self when sending funds", "commission_desc":"0%", "currency_confirmations":1}
451
+ # ],
452
+ # "USDT":[
453
+ # {"type":"deposit", "name":"USDT(OMNI)", "currency_name":"USDT", "min":"10", "max":"0", "enabled":false,"comment":"Minimum deposit amount is 10 USDT", "commission_desc":"0%", "currency_confirmations":2},
454
+ # {"type":"withdraw", "name":"USDT(OMNI)", "currency_name":"USDT", "min":"10", "max":"100000", "enabled":false,"comment":"Do not withdraw directly to the Crowdfunding or ICO address account will not be credited with tokens from such sales.", "commission_desc":"5 USDT", "currency_confirmations":6},
455
+ # {"type":"deposit", "name":"USDT(ERC20)", "currency_name":"USDT", "min":"10", "max":"0", "enabled":true,"comment":"Minimum deposit amount is 10 USDT", "commission_desc":"0%", "currency_confirmations":2},
456
+ # {
457
+ # "type":"withdraw",
458
+ # "name":"USDT(ERC20)",
459
+ # "currency_name":"USDT",
460
+ # "min":"55",
461
+ # "max":"200000",
462
+ # "enabled":true,
463
+ # "comment":"Caution! Do not withdraw directly to a crowdfund or ICO address, account will not be credited with tokens from such sales. Recommendation: Due to the high load of ERC20 network, using TRC20 address for withdrawal is recommended.",
464
+ # "commission_desc":"10 USDT",
465
+ # "currency_confirmations":6
466
+ # },
467
+ # {"type":"deposit", "name":"USDT(TRC20)", "currency_name":"USDT", "min":"10", "max":"100000", "enabled":true,"comment":"Minimum deposit amount is 10 USDT. Only TRON main network supported", "commission_desc":"0%", "currency_confirmations":2},
468
+ # {"type":"withdraw", "name":"USDT(TRC20)", "currency_name":"USDT", "min":"10", "max":"150000", "enabled":true,"comment":"Caution! Do not withdraw directly to a crowdfund or ICO address, account will not be credited with tokens from such sales. Only TRON main network supported.", "commission_desc":"1 USDT", "currency_confirmations":6}
469
+ # ],
470
+ # "XLM":[
471
+ # {"type":"deposit", "name":"XLM", "currency_name":"XLM", "min":"1", "max":"1000000", "enabled":true,"comment":"Attention! A deposit without memo(invoice) will not be credited. Minimum deposit amount is 1 XLM. We do not support BSC and BEP20 network, please consider self when sending funds", "commission_desc":"0%", "currency_confirmations":1},
472
+ # {"type":"withdraw", "name":"XLM", "currency_name":"XLM", "min":"21", "max":"1000000", "enabled":true,"comment":"Caution! Do not withdraw directly to a crowdfund or ICO address, account will not be credited with tokens from such sales.", "commission_desc":"0.01 XLM", "currency_confirmations":1}
473
+ # ],
474
+ # }
475
+ #
476
+ result: dict = {}
477
+ cryptoListKeys = list(cryptoList.keys())
478
+ for i in range(0, len(cryptoListKeys)):
479
+ code = cryptoListKeys[i]
480
+ if codes is not None and not self.in_array(code, codes):
481
+ continue
482
+ result[code] = {
483
+ 'deposit': None,
484
+ 'withdraw': None,
485
+ }
486
+ currency = self.currency(code)
487
+ currencyId = self.safe_string(currency, 'id')
488
+ providers = self.safe_value(cryptoList, currencyId, [])
489
+ for j in range(0, len(providers)):
490
+ provider = providers[j]
491
+ typeInner = self.safe_string(provider, 'type')
492
+ commissionDesc = self.safe_string(provider, 'commission_desc')
493
+ fee = self.parse_fixed_float_value(commissionDesc)
494
+ result[code][typeInner] = fee
495
+ result[code]['info'] = providers
496
+ # cache them for later use
497
+ self.options['transactionFees'] = result
498
+ return result
499
+
500
+ async def fetch_deposit_withdraw_fees(self, codes: Strings = None, params={}):
501
+ """
502
+ fetch deposit and withdraw fees
503
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#4190035d-24b1-453d-833b-37e0a52f88e2
504
+ :param str[]|None codes: list of unified currency codes
505
+ :param dict [params]: extra parameters specific to the exchange API endpoint
506
+ :returns dict: a list of `transaction fees structures <https://docs.ccxt.com/#/?id=fees-structure>`
507
+ """
508
+ await self.load_markets()
509
+ response = await self.publicGetPaymentsProvidersCryptoList(params)
510
+ #
511
+ # {
512
+ # "USDT": [
513
+ # {
514
+ # "type": "deposit", # or "withdraw"
515
+ # "name": "USDT(ERC20)",
516
+ # "currency_name": "USDT",
517
+ # "min": "10",
518
+ # "max": "0",
519
+ # "enabled": True,
520
+ # "comment": "Minimum deposit amount is 10 USDT",
521
+ # "commission_desc": "0%",
522
+ # "currency_confirmations": 2
523
+ # },
524
+ # ...
525
+ # ],
526
+ # ...
527
+ # }
528
+ #
529
+ result = self.parse_deposit_withdraw_fees(response, codes)
530
+ # cache them for later use
531
+ self.options['transactionFees'] = result
532
+ return result
533
+
534
+ def parse_deposit_withdraw_fee(self, fee, currency: Currency = None):
535
+ #
536
+ # [
537
+ # {
538
+ # "type": "deposit", # or "withdraw"
539
+ # "name": "BTC",
540
+ # "currency_name": "BTC",
541
+ # "min": "0.001",
542
+ # "max": "0",
543
+ # "enabled": True,
544
+ # "comment": "Minimum deposit amount is 0.001 BTC. We do not support BSC and BEP20 network, please consider self when sending funds",
545
+ # "commission_desc": "0%",
546
+ # "currency_confirmations": 1
547
+ # },
548
+ # ...
549
+ # ]
550
+ #
551
+ result = self.deposit_withdraw_fee(fee)
552
+ for i in range(0, len(fee)):
553
+ provider = fee[i]
554
+ type = self.safe_string(provider, 'type')
555
+ networkId = self.safe_string(provider, 'name')
556
+ networkCode = self.network_id_to_code(networkId, self.safe_string(currency, 'code'))
557
+ commissionDesc = self.safe_string(provider, 'commission_desc')
558
+ splitCommissionDesc = []
559
+ percentage = None
560
+ if commissionDesc is not None:
561
+ splitCommissionDesc = commissionDesc.split('%')
562
+ splitCommissionDescLength = len(splitCommissionDesc)
563
+ percentage = splitCommissionDescLength >= 2
564
+ network = self.safe_value(result['networks'], networkCode)
565
+ if network is None:
566
+ result['networks'][networkCode] = {
567
+ 'withdraw': {
568
+ 'fee': None,
569
+ 'percentage': None,
570
+ },
571
+ 'deposit': {
572
+ 'fee': None,
573
+ 'percentage': None,
574
+ },
575
+ }
576
+ result['networks'][networkCode][type] = {
577
+ 'fee': self.parse_fixed_float_value(self.safe_string(splitCommissionDesc, 0)),
578
+ 'percentage': percentage,
579
+ }
580
+ return self.assign_default_deposit_withdraw_fees(result)
581
+
582
+ async def fetch_currencies(self, params={}) -> Currencies:
583
+ """
584
+ fetches all available currencies on an exchange
585
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#7cdf0ca8-9ff6-4cf3-aa33-bcec83155c49
586
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#4190035d-24b1-453d-833b-37e0a52f88e2
587
+ :param dict [params]: extra parameters specific to the exchange API endpoint
588
+ :returns dict: an associative dictionary of currencies
589
+ """
590
+ #
591
+ currencyList = await self.publicGetCurrencyListExtended(params)
592
+ #
593
+ # [
594
+ # {"name":"VLX","description":"Velas"},
595
+ # {"name":"RUB","description":"Russian Ruble"},
596
+ # {"name":"BTC","description":"Bitcoin"},
597
+ # {"name":"USD","description":"US Dollar"}
598
+ # ]
599
+ #
600
+ cryptoList = await self.publicGetPaymentsProvidersCryptoList(params)
601
+ #
602
+ # {
603
+ # "BTC":[
604
+ # {"type":"deposit", "name":"BTC", "currency_name":"BTC", "min":"0.001", "max":"0", "enabled":true,"comment":"Minimum deposit amount is 0.001 BTC. We do not support BSC and BEP20 network, please consider self when sending funds", "commission_desc":"0%", "currency_confirmations":1},
605
+ # {"type":"withdraw", "name":"BTC", "currency_name":"BTC", "min":"0.001", "max":"350", "enabled":true,"comment":"Do not withdraw directly to the Crowdfunding or ICO address account will not be credited with tokens from such sales.", "commission_desc":"0.0005 BTC", "currency_confirmations":6}
606
+ # ],
607
+ # "ETH":[
608
+ # {"type":"withdraw", "name":"ETH", "currency_name":"ETH", "min":"0.01", "max":"500", "enabled":true,"comment":"Do not withdraw directly to the Crowdfunding or ICO address account will not be credited with tokens from such sales.", "commission_desc":"0.004 ETH", "currency_confirmations":4},
609
+ # {"type":"deposit", "name":"ETH", "currency_name":"ETH", "min":"0.01", "max":"0", "enabled":true,"comment":"Minimum deposit amount is 0.01 ETH. We do not support BSC and BEP20 network, please consider self when sending funds", "commission_desc":"0%", "currency_confirmations":1}
610
+ # ],
611
+ # "USDT":[
612
+ # {"type":"deposit", "name":"USDT(OMNI)", "currency_name":"USDT", "min":"10", "max":"0", "enabled":false,"comment":"Minimum deposit amount is 10 USDT", "commission_desc":"0%", "currency_confirmations":2},
613
+ # {"type":"withdraw", "name":"USDT(OMNI)", "currency_name":"USDT", "min":"10", "max":"100000", "enabled":false,"comment":"Do not withdraw directly to the Crowdfunding or ICO address account will not be credited with tokens from such sales.", "commission_desc":"5 USDT", "currency_confirmations":6},
614
+ # {"type":"deposit", "name":"USDT(ERC20)", "currency_name":"USDT", "min":"10", "max":"0", "enabled":true,"comment":"Minimum deposit amount is 10 USDT", "commission_desc":"0%", "currency_confirmations":2},
615
+ # {"type":"withdraw", "name":"USDT(ERC20)", "currency_name":"USDT", "min":"55", "max":"200000", "enabled":true, "comment":"Caution! Do not withdraw directly to a crowdfund or ICO address, account will not be credited with tokens from such sales. Recommendation: Due to the high load of ERC20 network, using TRC20 address for withdrawal is recommended.", "commission_desc":"10 USDT", "currency_confirmations":6},
616
+ # {"type":"deposit", "name":"USDT(TRC20)", "currency_name":"USDT", "min":"10", "max":"100000", "enabled":true,"comment":"Minimum deposit amount is 10 USDT. Only TRON main network supported", "commission_desc":"0%", "currency_confirmations":2},
617
+ # {"type":"withdraw", "name":"USDT(TRC20)", "currency_name":"USDT", "min":"10", "max":"150000", "enabled":true,"comment":"Caution! Do not withdraw directly to a crowdfund or ICO address, account will not be credited with tokens from such sales. Only TRON main network supported.", "commission_desc":"1 USDT", "currency_confirmations":6}
618
+ # ],
619
+ # "XLM":[
620
+ # {"type":"deposit", "name":"XLM", "currency_name":"XLM", "min":"1", "max":"1000000", "enabled":true,"comment":"Attention! A deposit without memo(invoice) will not be credited. Minimum deposit amount is 1 XLM. We do not support BSC and BEP20 network, please consider self when sending funds", "commission_desc":"0%", "currency_confirmations":1},
621
+ # {"type":"withdraw", "name":"XLM", "currency_name":"XLM", "min":"21", "max":"1000000", "enabled":true,"comment":"Caution! Do not withdraw directly to a crowdfund or ICO address, account will not be credited with tokens from such sales.", "commission_desc":"0.01 XLM", "currency_confirmations":1}
622
+ # ],
623
+ # }
624
+ #
625
+ result: dict = {}
626
+ for i in range(0, len(currencyList)):
627
+ currency = currencyList[i]
628
+ currencyId = self.safe_string(currency, 'name')
629
+ name = self.safe_string(currency, 'description')
630
+ providers = self.safe_value(cryptoList, currencyId)
631
+ active = False
632
+ type = 'crypto'
633
+ limits: dict = {
634
+ 'deposit': {
635
+ 'min': None,
636
+ 'max': None,
637
+ },
638
+ 'withdraw': {
639
+ 'min': None,
640
+ 'max': None,
641
+ },
642
+ }
643
+ fee = None
644
+ depositEnabled = None
645
+ withdrawEnabled = None
646
+ if providers is None:
647
+ active = True
648
+ type = 'fiat'
649
+ else:
650
+ for j in range(0, len(providers)):
651
+ provider = providers[j]
652
+ typeInner = self.safe_string(provider, 'type')
653
+ minValue = self.safe_string(provider, 'min')
654
+ maxValue = self.safe_string(provider, 'max')
655
+ if Precise.string_eq(maxValue, '0.0'):
656
+ maxValue = None
657
+ activeProvider = self.safe_value(provider, 'enabled')
658
+ if typeInner == 'deposit':
659
+ if activeProvider and not depositEnabled:
660
+ depositEnabled = True
661
+ elif not activeProvider:
662
+ depositEnabled = False
663
+ elif typeInner == 'withdraw':
664
+ if activeProvider and not withdrawEnabled:
665
+ withdrawEnabled = True
666
+ elif not activeProvider:
667
+ withdrawEnabled = False
668
+ if activeProvider:
669
+ active = True
670
+ limitMin = self.number_to_string(limits[typeInner]['min'])
671
+ if (limits[typeInner]['min'] is None) or (Precise.string_lt(minValue, limitMin)):
672
+ limits[typeInner]['min'] = minValue
673
+ limits[typeInner]['max'] = maxValue
674
+ if typeInner == 'withdraw':
675
+ commissionDesc = self.safe_string(provider, 'commission_desc')
676
+ fee = self.parse_fixed_float_value(commissionDesc)
677
+ code = self.safe_currency_code(currencyId)
678
+ result[code] = {
679
+ 'id': currencyId,
680
+ 'code': code,
681
+ 'name': name,
682
+ 'type': type,
683
+ 'active': active,
684
+ 'deposit': depositEnabled,
685
+ 'withdraw': withdrawEnabled,
686
+ 'fee': fee,
687
+ 'precision': self.parse_number('1e-8'),
688
+ 'limits': limits,
689
+ 'info': providers,
690
+ 'networks': {},
691
+ }
692
+ return result
693
+
694
+ async def fetch_markets(self, params={}) -> List[Market]:
695
+ """
696
+ retrieves data on all markets for exmo
697
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#7de7e75c-5833-45a8-b937-c2276d235aaa
698
+ :param dict [params]: extra parameters specific to the exchange API endpoint
699
+ :returns dict[]: an array of objects representing market data
700
+ """
701
+ response = await self.publicGetPairSettings(params)
702
+ #
703
+ # {
704
+ # "BTC_USD":{
705
+ # "min_quantity":"0.0001",
706
+ # "max_quantity":"1000",
707
+ # "min_price":"1",
708
+ # "max_price":"30000",
709
+ # "max_amount":"500000",
710
+ # "min_amount":"1",
711
+ # "price_precision":8,
712
+ # "commission_taker_percent":"0.4",
713
+ # "commission_maker_percent":"0.4"
714
+ # },
715
+ # }
716
+ #
717
+ marginPairsDict: dict = {}
718
+ if self.check_required_credentials(False):
719
+ marginPairs = await self.privatePostMarginPairList(params)
720
+ #
721
+ # {
722
+ # "pairs": [
723
+ # {
724
+ # "buy_price": "55978.85",
725
+ # "default_leverage": "3",
726
+ # "is_fair_price": True,
727
+ # "last_trade_price": "55999.23",
728
+ # "liquidation_fee": "2",
729
+ # "liquidation_level": "10",
730
+ # "margin_call_level": "15",
731
+ # "max_leverage": "3",
732
+ # "max_order_price": "150000",
733
+ # "max_order_quantity": "1",
734
+ # "max_position_quantity": "1",
735
+ # "max_price_precision": 2,
736
+ # "min_order_price": "1",
737
+ # "min_order_quantity": "0.00002",
738
+ # "name": "BTC_USD",
739
+ # "position": 1,
740
+ # "sell_price": "55985.51",
741
+ # "ticker_updated": "1619019818936107989",
742
+ # "trade_maker_fee": "0",
743
+ # "trade_taker_fee": "0.05",
744
+ # "updated": "1619008608955599013"
745
+ # }
746
+ # ]
747
+ # }
748
+ #
749
+ pairs = self.safe_value(marginPairs, 'pairs')
750
+ marginPairsDict = self.index_by(pairs, 'name')
751
+ keys = list(response.keys())
752
+ result = []
753
+ for i in range(0, len(keys)):
754
+ id = keys[i]
755
+ market = response[id]
756
+ marginMarket = self.safe_value(marginPairsDict, id)
757
+ symbol = id.replace('_', '/')
758
+ baseId, quoteId = symbol.split('/')
759
+ base = self.safe_currency_code(baseId)
760
+ quote = self.safe_currency_code(quoteId)
761
+ takerString = self.safe_string(market, 'commission_taker_percent')
762
+ makerString = self.safe_string(market, 'commission_maker_percent')
763
+ maxQuantity = self.safe_string(market, 'max_quantity')
764
+ marginMaxQuantity = self.safe_string(marginMarket, 'max_order_quantity')
765
+ result.append({
766
+ 'id': id,
767
+ 'symbol': symbol,
768
+ 'base': base,
769
+ 'quote': quote,
770
+ 'settle': None,
771
+ 'baseId': baseId,
772
+ 'quoteId': quoteId,
773
+ 'settleId': None,
774
+ 'type': 'spot',
775
+ 'spot': True,
776
+ 'margin': marginMarket is not None,
777
+ 'swap': False,
778
+ 'future': False,
779
+ 'option': False,
780
+ 'active': None,
781
+ 'contract': False,
782
+ 'linear': None,
783
+ 'inverse': None,
784
+ 'taker': self.parse_number(Precise.string_div(takerString, '100')),
785
+ 'maker': self.parse_number(Precise.string_div(makerString, '100')),
786
+ 'contractSize': None,
787
+ 'expiry': None,
788
+ 'expiryDatetime': None,
789
+ 'strike': None,
790
+ 'optionType': None,
791
+ 'precision': {
792
+ 'amount': self.parse_number('1e-8'),
793
+ 'price': self.parse_number(self.parse_precision(self.safe_string(market, 'price_precision'))),
794
+ },
795
+ 'limits': {
796
+ 'leverage': {
797
+ 'min': None,
798
+ 'max': self.safe_number(market, 'leverage'),
799
+ },
800
+ 'amount': {
801
+ 'min': self.safe_number(market, 'min_quantity'),
802
+ 'max': self.parse_number(Precise.string_max(maxQuantity, marginMaxQuantity)),
803
+ },
804
+ 'price': {
805
+ 'min': self.safe_number(market, 'min_price'),
806
+ 'max': self.safe_number(market, 'max_price'),
807
+ },
808
+ 'cost': {
809
+ 'min': self.safe_number(market, 'min_amount'),
810
+ 'max': self.safe_number(market, 'max_amount'),
811
+ },
812
+ },
813
+ 'created': None,
814
+ 'info': market,
815
+ })
816
+ return result
817
+
818
+ async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
819
+ """
820
+ fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
821
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#65eeb949-74e5-4631-9184-c38387fe53e8
822
+ :param str symbol: unified symbol of the market to fetch OHLCV data for
823
+ :param str timeframe: the length of time each candle represents
824
+ :param int [since]: timestamp in ms of the earliest candle to fetch
825
+ :param int [limit]: the maximum amount of candles to fetch
826
+ :param dict [params]: extra parameters specific to the exchange API endpoint
827
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
828
+ """
829
+ await self.load_markets()
830
+ market = self.market(symbol)
831
+ request: dict = {
832
+ 'symbol': market['id'],
833
+ 'resolution': self.safe_string(self.timeframes, timeframe, timeframe),
834
+ }
835
+ maxLimit = 3000
836
+ duration = self.parse_timeframe(timeframe)
837
+ now = self.milliseconds()
838
+ if since is None:
839
+ if limit is None:
840
+ limit = 1000 # cap default at generous amount
841
+ else:
842
+ limit = min(limit, maxLimit)
843
+ request['from'] = self.parse_to_int(now / 1000) - limit * duration - 1
844
+ request['to'] = self.parse_to_int(now / 1000)
845
+ else:
846
+ request['from'] = self.parse_to_int(since / 1000) - 1
847
+ if limit is None:
848
+ limit = maxLimit
849
+ else:
850
+ limit = min(limit, maxLimit)
851
+ to = self.sum(since, limit * duration * 1000)
852
+ request['to'] = self.parse_to_int(to / 1000)
853
+ response = await self.publicGetCandlesHistory(self.extend(request, params))
854
+ #
855
+ # {
856
+ # "candles":[
857
+ # {"t":1584057600000,"o":0.02235144,"c":0.02400233,"h":0.025171,"l":0.02221,"v":5988.34031761},
858
+ # {"t":1584144000000,"o":0.0240373,"c":0.02367413,"h":0.024399,"l":0.0235,"v":2027.82522329},
859
+ # {"t":1584230400000,"o":0.02363458,"c":0.02319242,"h":0.0237948,"l":0.02223196,"v":1707.96944997},
860
+ # ]
861
+ # }
862
+ #
863
+ candles = self.safe_list(response, 'candles', [])
864
+ return self.parse_ohlcvs(candles, market, timeframe, since, limit)
865
+
866
+ def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
867
+ #
868
+ # {
869
+ # "t":1584057600000,
870
+ # "o":0.02235144,
871
+ # "c":0.02400233,
872
+ # "h":0.025171,
873
+ # "l":0.02221,
874
+ # "v":5988.34031761
875
+ # }
876
+ #
877
+ return [
878
+ self.safe_integer(ohlcv, 't'),
879
+ self.safe_number(ohlcv, 'o'),
880
+ self.safe_number(ohlcv, 'h'),
881
+ self.safe_number(ohlcv, 'l'),
882
+ self.safe_number(ohlcv, 'c'),
883
+ self.safe_number(ohlcv, 'v'),
884
+ ]
885
+
886
+ def parse_balance(self, response) -> Balances:
887
+ result: dict = {'info': response}
888
+ wallets = self.safe_value(response, 'wallets')
889
+ if wallets is not None:
890
+ currencyIds = list(wallets.keys())
891
+ for i in range(0, len(currencyIds)):
892
+ currencyId = currencyIds[i]
893
+ item = wallets[currencyId]
894
+ currency = self.safe_currency_code(currencyId)
895
+ account = self.account()
896
+ account['used'] = self.safe_string(item, 'used')
897
+ account['free'] = self.safe_string(item, 'free')
898
+ account['total'] = self.safe_string(item, 'balance')
899
+ result[currency] = account
900
+ else:
901
+ free = self.safe_value(response, 'balances', {})
902
+ used = self.safe_value(response, 'reserved', {})
903
+ currencyIds = list(free.keys())
904
+ for i in range(0, len(currencyIds)):
905
+ currencyId = currencyIds[i]
906
+ code = self.safe_currency_code(currencyId)
907
+ account = self.account()
908
+ if currencyId in free:
909
+ account['free'] = self.safe_string(free, currencyId)
910
+ if currencyId in used:
911
+ account['used'] = self.safe_string(used, currencyId)
912
+ result[code] = account
913
+ return self.safe_balance(result)
914
+
915
+ async def fetch_balance(self, params={}) -> Balances:
916
+ """
917
+ query for balance and get the amount of funds available for trading or funds locked in orders
918
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#59c5160f-27a1-4d9a-8cfb-7979c7ffaac6
919
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#c8388df7-1f9f-4d41-81c4-5a387d171dc6
920
+ :param dict [params]: extra parameters specific to the exchange API endpoint
921
+ :param str [params.marginMode]: *isolated* fetches the isolated margin balance
922
+ :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
923
+ """
924
+ await self.load_markets()
925
+ marginMode = None
926
+ marginMode, params = self.handle_margin_mode_and_params('fetchBalance', params)
927
+ if marginMode == 'cross':
928
+ raise BadRequest(self.id + ' does not support cross margin')
929
+ response = None
930
+ if marginMode == 'isolated':
931
+ response = await self.privatePostMarginUserWalletList(params)
932
+ #
933
+ # {
934
+ # "wallets": {
935
+ # "USD": {
936
+ # "balance": "1000",
937
+ # "free": "600",
938
+ # "used": "400"
939
+ # }
940
+ # }
941
+ # }
942
+ #
943
+ else:
944
+ response = await self.privatePostUserInfo(params)
945
+ #
946
+ # {
947
+ # "uid":131685,
948
+ # "server_date":1628999600,
949
+ # "balances":{
950
+ # "EXM":"0",
951
+ # "USD":"0",
952
+ # "EUR":"0",
953
+ # "GBP":"0",
954
+ # },
955
+ # }
956
+ #
957
+ return self.parse_balance(response)
958
+
959
+ async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
960
+ """
961
+ fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
962
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#c60c51a8-e683-4f45-a000-820723d37871
963
+ :param str symbol: unified symbol of the market to fetch the order book for
964
+ :param int [limit]: the maximum amount of order book entries to return
965
+ :param dict [params]: extra parameters specific to the exchange API endpoint
966
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
967
+ """
968
+ await self.load_markets()
969
+ market = self.market(symbol)
970
+ request: dict = {
971
+ 'pair': market['id'],
972
+ }
973
+ if limit is not None:
974
+ request['limit'] = limit
975
+ response = await self.publicGetOrderBook(self.extend(request, params))
976
+ result = self.safe_dict(response, market['id'])
977
+ return self.parse_order_book(result, market['symbol'], None, 'bid', 'ask')
978
+
979
+ async def fetch_order_books(self, symbols: Strings = None, limit: Int = None, params={}):
980
+ """
981
+ fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data for multiple markets
982
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#c60c51a8-e683-4f45-a000-820723d37871
983
+ :param str[]|None symbols: list of unified market symbols, all symbols fetched if None, default is None
984
+ :param int [limit]: max number of entries per orderbook to return, default is None
985
+ :param dict [params]: extra parameters specific to the exchange API endpoint
986
+ :returns dict: a dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbol
987
+ """
988
+ await self.load_markets()
989
+ ids = None
990
+ if symbols is None:
991
+ ids = ','.join(self.ids)
992
+ # max URL length is 2083 symbols, including http schema, hostname, tld, etc...
993
+ if len(ids) > 2048:
994
+ numIds = len(self.ids)
995
+ raise ExchangeError(self.id + ' fetchOrderBooks() has ' + str(numIds) + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchOrderBooks')
996
+ else:
997
+ ids = self.market_ids(symbols)
998
+ ids = ','.join(ids)
999
+ request: dict = {
1000
+ 'pair': ids,
1001
+ }
1002
+ if limit is not None:
1003
+ request['limit'] = limit
1004
+ response = await self.publicGetOrderBook(self.extend(request, params))
1005
+ result: dict = {}
1006
+ marketIds = list(response.keys())
1007
+ for i in range(0, len(marketIds)):
1008
+ marketId = marketIds[i]
1009
+ symbol = self.safe_symbol(marketId)
1010
+ result[symbol] = self.parse_order_book(response[marketId], symbol, None, 'bid', 'ask')
1011
+ return result
1012
+
1013
+ def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
1014
+ #
1015
+ # {
1016
+ # "buy_price":"0.00002996",
1017
+ # "sell_price":"0.00003002",
1018
+ # "last_trade":"0.00002992",
1019
+ # "high":"0.00003028",
1020
+ # "low":"0.00002935",
1021
+ # "avg":"0.00002963",
1022
+ # "vol":"1196546.3163222",
1023
+ # "vol_curr":"35.80066578",
1024
+ # "updated":1642291733
1025
+ # }
1026
+ #
1027
+ timestamp = self.safe_timestamp(ticker, 'updated')
1028
+ market = self.safe_market(None, market)
1029
+ last = self.safe_string(ticker, 'last_trade')
1030
+ return self.safe_ticker({
1031
+ 'symbol': market['symbol'],
1032
+ 'timestamp': timestamp,
1033
+ 'datetime': self.iso8601(timestamp),
1034
+ 'high': self.safe_string(ticker, 'high'),
1035
+ 'low': self.safe_string(ticker, 'low'),
1036
+ 'bid': self.safe_string(ticker, 'buy_price'),
1037
+ 'bidVolume': None,
1038
+ 'ask': self.safe_string(ticker, 'sell_price'),
1039
+ 'askVolume': None,
1040
+ 'vwap': None,
1041
+ 'open': None,
1042
+ 'close': last,
1043
+ 'last': last,
1044
+ 'previousClose': None,
1045
+ 'change': None,
1046
+ 'percentage': None,
1047
+ 'average': self.safe_string(ticker, 'avg'),
1048
+ 'baseVolume': self.safe_string(ticker, 'vol'),
1049
+ 'quoteVolume': self.safe_string(ticker, 'vol_curr'),
1050
+ 'info': ticker,
1051
+ }, market)
1052
+
1053
+ async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
1054
+ """
1055
+ fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
1056
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#4c8e6459-3503-4361-b012-c34bb9f7e385
1057
+ :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
1058
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1059
+ :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
1060
+ """
1061
+ await self.load_markets()
1062
+ symbols = self.market_symbols(symbols)
1063
+ response = await self.publicGetTicker(params)
1064
+ #
1065
+ # {
1066
+ # "ADA_BTC":{
1067
+ # "buy_price":"0.00002996",
1068
+ # "sell_price":"0.00003002",
1069
+ # "last_trade":"0.00002992",
1070
+ # "high":"0.00003028",
1071
+ # "low":"0.00002935",
1072
+ # "avg":"0.00002963",
1073
+ # "vol":"1196546.3163222",
1074
+ # "vol_curr":"35.80066578",
1075
+ # "updated":1642291733
1076
+ # }
1077
+ # }
1078
+ #
1079
+ result: dict = {}
1080
+ marketIds = list(response.keys())
1081
+ for i in range(0, len(marketIds)):
1082
+ marketId = marketIds[i]
1083
+ market = self.safe_market(marketId, None, '_')
1084
+ symbol = market['symbol']
1085
+ ticker = self.safe_value(response, marketId)
1086
+ result[symbol] = self.parse_ticker(ticker, market)
1087
+ return self.filter_by_array_tickers(result, 'symbol', symbols)
1088
+
1089
+ async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
1090
+ """
1091
+ fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
1092
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#4c8e6459-3503-4361-b012-c34bb9f7e385
1093
+ :param str symbol: unified symbol of the market to fetch the ticker for
1094
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1095
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
1096
+ """
1097
+ await self.load_markets()
1098
+ response = await self.publicGetTicker(params)
1099
+ market = self.market(symbol)
1100
+ return self.parse_ticker(response[market['id']], market)
1101
+
1102
+ def parse_trade(self, trade: dict, market: Market = None) -> Trade:
1103
+ #
1104
+ # fetchTrades(public)
1105
+ #
1106
+ # {
1107
+ # "trade_id":165087520,
1108
+ # "date":1587470005,
1109
+ # "type":"buy",
1110
+ # "quantity":"1.004",
1111
+ # "price":"0.02491461",
1112
+ # "amount":"0.02501426"
1113
+ # },
1114
+ #
1115
+ # fetchMyTrades, fetchOrderTrades
1116
+ #
1117
+ # {
1118
+ # "trade_id": 3,
1119
+ # "date": 1435488248,
1120
+ # "type": "buy",
1121
+ # "pair": "BTC_USD",
1122
+ # "order_id": 12345,
1123
+ # "quantity": 1,
1124
+ # "price": 100,
1125
+ # "amount": 100,
1126
+ # "exec_type": "taker",
1127
+ # "commission_amount": "0.02",
1128
+ # "commission_currency": "BTC",
1129
+ # "commission_percent": "0.2"
1130
+ # }
1131
+ #
1132
+ # fetchMyTrades(margin)
1133
+ #
1134
+ # {
1135
+ # "trade_id": "692861757015952517",
1136
+ # "trade_dt": "1693951853197811824",
1137
+ # "trade_type": "buy",
1138
+ # "pair": "ADA_USDT",
1139
+ # "quantity": "1.96607879",
1140
+ # "price": "0.2568",
1141
+ # "amount": "0.50488903"
1142
+ # }
1143
+ #
1144
+ timestamp = self.safe_timestamp(trade, 'date')
1145
+ id = self.safe_string(trade, 'trade_id')
1146
+ orderId = self.safe_string(trade, 'order_id')
1147
+ priceString = self.safe_string(trade, 'price')
1148
+ amountString = self.safe_string(trade, 'quantity')
1149
+ costString = self.safe_string(trade, 'amount')
1150
+ side = self.safe_string_2(trade, 'type', 'trade_type')
1151
+ type = None
1152
+ marketId = self.safe_string(trade, 'pair')
1153
+ market = self.safe_market(marketId, market, '_')
1154
+ symbol = market['symbol']
1155
+ isMaker = self.safe_value(trade, 'is_maker')
1156
+ takerOrMakerDefault = None
1157
+ if isMaker is not None:
1158
+ takerOrMakerDefault = 'maker' if isMaker else 'taker'
1159
+ takerOrMaker = self.safe_string(trade, 'exec_type', takerOrMakerDefault)
1160
+ fee = None
1161
+ feeCostString = self.safe_string(trade, 'commission_amount')
1162
+ if feeCostString is not None:
1163
+ feeCurrencyId = self.safe_string(trade, 'commission_currency')
1164
+ feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
1165
+ feeRateString = self.safe_string(trade, 'commission_percent')
1166
+ if feeRateString is not None:
1167
+ feeRateString = Precise.string_div(feeRateString, '1000', 18)
1168
+ fee = {
1169
+ 'cost': feeCostString,
1170
+ 'currency': feeCurrencyCode,
1171
+ 'rate': feeRateString,
1172
+ }
1173
+ return self.safe_trade({
1174
+ 'id': id,
1175
+ 'info': trade,
1176
+ 'timestamp': timestamp,
1177
+ 'datetime': self.iso8601(timestamp),
1178
+ 'symbol': symbol,
1179
+ 'order': orderId,
1180
+ 'type': type,
1181
+ 'side': side,
1182
+ 'takerOrMaker': takerOrMaker,
1183
+ 'price': priceString,
1184
+ 'amount': amountString,
1185
+ 'cost': costString,
1186
+ 'fee': fee,
1187
+ }, market)
1188
+
1189
+ async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
1190
+ """
1191
+ get the list of most recent trades for a particular symbol
1192
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#5a5a9c0d-cf17-47f6-9d62-6d4404ebd5ac
1193
+ :param str symbol: unified symbol of the market to fetch trades for
1194
+ :param int [since]: timestamp in ms of the earliest trade to fetch
1195
+ :param int [limit]: the maximum amount of trades to fetch
1196
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1197
+ :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
1198
+ """
1199
+ await self.load_markets()
1200
+ market = self.market(symbol)
1201
+ request: dict = {
1202
+ 'pair': market['id'],
1203
+ }
1204
+ response = await self.publicGetTrades(self.extend(request, params))
1205
+ #
1206
+ # {
1207
+ # "ETH_BTC":[
1208
+ # {
1209
+ # "trade_id":165087520,
1210
+ # "date":1587470005,
1211
+ # "type":"buy",
1212
+ # "quantity":"1.004",
1213
+ # "price":"0.02491461",
1214
+ # "amount":"0.02501426"
1215
+ # },
1216
+ # {
1217
+ # "trade_id":165087369,
1218
+ # "date":1587469938,
1219
+ # "type":"buy",
1220
+ # "quantity":"0.94",
1221
+ # "price":"0.02492348",
1222
+ # "amount":"0.02342807"
1223
+ # }
1224
+ # ]
1225
+ # }
1226
+ #
1227
+ data = self.safe_list(response, market['id'], [])
1228
+ return self.parse_trades(data, market, since, limit)
1229
+
1230
+ async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1231
+ """
1232
+ fetch all trades made by the user
1233
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#b8d8d9af-4f46-46a1-939b-ad261d79f452 # spot
1234
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#f4b1aaf8-399f-403b-ab5e-4926d967a106 # margin
1235
+ :param str symbol: a symbol is required but it can be a single string, or a non-empty array
1236
+ :param int [since]: the earliest time in ms to fetch trades for
1237
+ :param int [limit]: *required for margin orders* the maximum number of trades structures to retrieve
1238
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1239
+ *
1240
+ * EXCHANGE SPECIFIC PARAMETERS
1241
+ :param int [params.offset]: last deal offset, default = 0
1242
+ :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
1243
+ """
1244
+ if symbol is None:
1245
+ raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol argument')
1246
+ marginMode = None
1247
+ marginMode, params = self.handle_margin_mode_and_params('fetchMyTrades', params)
1248
+ if marginMode == 'cross':
1249
+ raise BadRequest(self.id + 'only isolated margin is supported')
1250
+ await self.load_markets()
1251
+ market = self.market(symbol)
1252
+ pair = market['id']
1253
+ isSpot = marginMode != 'isolated'
1254
+ if limit is None:
1255
+ limit = 100
1256
+ request: dict = {}
1257
+ if isSpot:
1258
+ request['pair'] = pair
1259
+ else:
1260
+ request['pair_name'] = pair
1261
+ if limit is not None:
1262
+ request['limit'] = limit
1263
+ offset = self.safe_integer(params, 'offset', 0)
1264
+ request['offset'] = offset
1265
+ response = None
1266
+ if isSpot:
1267
+ response = await self.privatePostUserTrades(self.extend(request, params))
1268
+ #
1269
+ # {
1270
+ # "BTC_USD": [
1271
+ # {
1272
+ # "trade_id": 20056872,
1273
+ # "client_id": 100500,
1274
+ # "date": 1435488248,
1275
+ # "type": "buy",
1276
+ # "pair": "BTC_USD",
1277
+ # "quantity": "1",
1278
+ # "price": "100",
1279
+ # "amount": "100",
1280
+ # "order_id": 7,
1281
+ # "parent_order_id": 117684023830293,
1282
+ # "exec_type": "taker",
1283
+ # "commission_amount": "0.02",
1284
+ # "commission_currency": "BTC",
1285
+ # "commission_percent": "0.2"
1286
+ # }
1287
+ # ],
1288
+ # ...
1289
+ # }
1290
+ #
1291
+ else:
1292
+ responseFromExchange = await self.privatePostMarginTrades(self.extend(request, params))
1293
+ #
1294
+ # {
1295
+ # "trades": {
1296
+ # "ADA_USDT": [
1297
+ # {
1298
+ # "trade_id": "692861757015952517",
1299
+ # "trade_dt": "1693951853197811824",
1300
+ # "trade_type": "buy",
1301
+ # "pair": "ADA_USDT",
1302
+ # "quantity": "1.96607879",
1303
+ # "price": "0.2568",
1304
+ # "amount": "0.50488903"
1305
+ # },
1306
+ # ]
1307
+ # ...
1308
+ # }
1309
+ # }
1310
+ #
1311
+ response = self.safe_value(responseFromExchange, 'trades')
1312
+ result = []
1313
+ marketIdsInner = list(response.keys())
1314
+ for i in range(0, len(marketIdsInner)):
1315
+ marketId = marketIdsInner[i]
1316
+ resultMarket = self.safe_market(marketId, None, '_')
1317
+ items = response[marketId]
1318
+ trades = self.parse_trades(items, resultMarket, since, limit)
1319
+ result = self.array_concat(result, trades)
1320
+ return self.filter_by_since_limit(result, since, limit)
1321
+
1322
+ async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
1323
+ """
1324
+ create a trade order
1325
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#80daa469-ec59-4d0a-b229-6a311d8dd1cd
1326
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#de6f4321-eeac-468c-87f7-c4ad7062e265 # stop market
1327
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#3561b86c-9ff1-436e-8e68-ac926b7eb523 # margin
1328
+ :param str symbol: unified symbol of the market to create an order in
1329
+ :param str type: 'market' or 'limit'
1330
+ :param str side: 'buy' or 'sell'
1331
+ :param float amount: how much of currency you want to trade in units of base currency
1332
+ :param float [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
1333
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1334
+ :param float [params.stopPrice]: the price at which a trigger order is triggered at
1335
+ :param str [params.timeInForce]: *spot only* 'fok', 'ioc' or 'post_only'
1336
+ :param boolean [params.postOnly]: *spot only* True for post only orders
1337
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1338
+ """
1339
+ await self.load_markets()
1340
+ market = self.market(symbol)
1341
+ isMarket = (type == 'market') and (price is None)
1342
+ marginMode = None
1343
+ marginMode, params = self.handle_margin_mode_and_params('createOrder', params)
1344
+ if marginMode == 'cross':
1345
+ raise BadRequest(self.id + ' only supports isolated margin')
1346
+ isSpot = (marginMode != 'isolated')
1347
+ triggerPrice = self.safe_number_n(params, ['triggerPrice', 'stopPrice', 'stop_price'])
1348
+ request: dict = {
1349
+ 'pair': market['id'],
1350
+ # 'leverage': 2,
1351
+ 'quantity': self.amount_to_precision(market['symbol'], amount),
1352
+ # spot - buy, sell, market_buy, market_sell, market_buy_total, market_sell_total
1353
+ # margin - limit_buy, limit_sell, market_buy, market_sell, stop_buy, stop_sell, stop_limit_buy, stop_limit_sell, trailing_stop_buy, trailing_stop_sell
1354
+ # 'stop_price': self.price_to_precision(symbol, stopPrice),
1355
+ # 'distance': 0, # distance for trailing stop orders
1356
+ # 'expire': 0, # expiration timestamp in UTC timezone for the order, unless expire is 0
1357
+ # 'client_id': 123, # optional, must be a positive integer
1358
+ # 'comment': '', # up to 50 latin symbols, whitespaces, underscores
1359
+ }
1360
+ clientOrderId = self.safe_value_2(params, 'client_id', 'clientOrderId')
1361
+ if clientOrderId is not None:
1362
+ clientOrderId = self.safe_integer_2(params, 'client_id', 'clientOrderId')
1363
+ if clientOrderId is None:
1364
+ raise BadRequest(self.id + ' createOrder() client order id must be an integer / numeric literal')
1365
+ else:
1366
+ request['client_id'] = clientOrderId
1367
+ leverage = self.safe_number(params, 'leverage')
1368
+ if not isSpot and (leverage is None):
1369
+ raise ArgumentsRequired(self.id + ' createOrder requires an extra param params["leverage"] for margin orders')
1370
+ params = self.omit(params, ['stopPrice', 'stop_price', 'triggerPrice', 'timeInForce', 'client_id', 'clientOrderId'])
1371
+ if price is not None:
1372
+ request['price'] = self.price_to_precision(market['symbol'], price)
1373
+ response = None
1374
+ if isSpot:
1375
+ if triggerPrice is not None:
1376
+ if type == 'limit':
1377
+ raise BadRequest(self.id + ' createOrder() cannot create stop limit orders for spot, only stop market')
1378
+ else:
1379
+ request['type'] = side
1380
+ request['trigger_price'] = self.price_to_precision(symbol, triggerPrice)
1381
+ response = await self.privatePostStopMarketOrderCreate(self.extend(request, params))
1382
+ else:
1383
+ execType = self.safe_string(params, 'exec_type')
1384
+ isPostOnly = None
1385
+ isPostOnly, params = self.handle_post_only(type == 'market', execType == 'post_only', params)
1386
+ timeInForce = self.safe_string(params, 'timeInForce')
1387
+ request['price'] = 0 if isMarket else self.price_to_precision(market['symbol'], price)
1388
+ if type == 'limit':
1389
+ request['type'] = side
1390
+ elif type == 'market':
1391
+ request['type'] = 'market_' + side
1392
+ if isPostOnly:
1393
+ request['exec_type'] = 'post_only'
1394
+ elif timeInForce is not None:
1395
+ request['exec_type'] = timeInForce
1396
+ response = await self.privatePostOrderCreate(self.extend(request, params))
1397
+ else:
1398
+ if triggerPrice is not None:
1399
+ request['stop_price'] = self.price_to_precision(symbol, triggerPrice)
1400
+ if type == 'limit':
1401
+ request['type'] = 'stop_limit_' + side
1402
+ elif type == 'market':
1403
+ request['type'] = 'stop_' + side
1404
+ else:
1405
+ request['type'] = type
1406
+ else:
1407
+ if type == 'limit' or type == 'market':
1408
+ request['type'] = type + '_' + side
1409
+ else:
1410
+ request['type'] = type
1411
+ response = await self.privatePostMarginUserOrderCreate(self.extend(request, params))
1412
+ return self.parse_order(response, market)
1413
+
1414
+ async def cancel_order(self, id: str, symbol: Str = None, params={}):
1415
+ """
1416
+ cancels an open order
1417
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#1f710d4b-75bc-4b65-ad68-006f863a3f26
1418
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#a4d0aae8-28f7-41ac-94fd-c4030130453d # stop market
1419
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#705dfec5-2b35-4667-862b-faf54eca6209 # margin
1420
+ :param str id: order id
1421
+ :param str symbol: not used by exmo cancelOrder()
1422
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1423
+ :param boolean [params.trigger]: True to cancel a trigger order
1424
+ :param str [params.marginMode]: set to 'cross' or 'isolated' to cancel a margin order
1425
+ :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1426
+ """
1427
+ await self.load_markets()
1428
+ request: dict = {}
1429
+ stop = self.safe_value_2(params, 'trigger', 'stop')
1430
+ params = self.omit(params, ['trigger', 'stop'])
1431
+ marginMode = None
1432
+ marginMode, params = self.handle_margin_mode_and_params('cancelOrder', params)
1433
+ if marginMode == 'cross':
1434
+ raise BadRequest(self.id + ' only supports isolated margin')
1435
+ response = None
1436
+ if (marginMode == 'isolated'):
1437
+ request['order_id'] = id
1438
+ response = await self.privatePostMarginUserOrderCancel(self.extend(request, params))
1439
+ #
1440
+ # {}
1441
+ #
1442
+ else:
1443
+ if stop:
1444
+ request['parent_order_id'] = id
1445
+ response = await self.privatePostStopMarketOrderCancel(self.extend(request, params))
1446
+ #
1447
+ # {}
1448
+ #
1449
+ else:
1450
+ request['order_id'] = id
1451
+ response = await self.privatePostOrderCancel(self.extend(request, params))
1452
+ #
1453
+ # {
1454
+ # "error": '',
1455
+ # "result": True
1456
+ # }
1457
+ #
1458
+ return self.parse_order(response)
1459
+
1460
+ async def fetch_order(self, id: str, symbol: Str = None, params={}):
1461
+ """
1462
+ *spot only* fetches information on an order made by the user
1463
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#cf27781e-28e5-4b39-a52d-3110f5d22459 # spot
1464
+ :param str symbol: not used by exmo fetchOrder
1465
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1466
+ :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1467
+ """
1468
+ await self.load_markets()
1469
+ request: dict = {
1470
+ 'order_id': str(id),
1471
+ }
1472
+ response = await self.privatePostOrderTrades(self.extend(request, params))
1473
+ #
1474
+ # {
1475
+ # "type": "buy",
1476
+ # "in_currency": "BTC",
1477
+ # "in_amount": "1",
1478
+ # "out_currency": "USD",
1479
+ # "out_amount": "100",
1480
+ # "trades": [
1481
+ # {
1482
+ # "trade_id": 3,
1483
+ # "date": 1435488248,
1484
+ # "type": "buy",
1485
+ # "pair": "BTC_USD",
1486
+ # "order_id": 12345,
1487
+ # "quantity": 1,
1488
+ # "price": 100,
1489
+ # "amount": 100
1490
+ # }
1491
+ # ]
1492
+ # }
1493
+ #
1494
+ order = self.parse_order(response)
1495
+ order['id'] = str(id)
1496
+ return order
1497
+
1498
+ async def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1499
+ """
1500
+ fetch all the trades made from a single order
1501
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#cf27781e-28e5-4b39-a52d-3110f5d22459 # spot
1502
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#00810661-9119-46c5-aec5-55abe9cb42c7 # margin
1503
+ :param str id: order id
1504
+ :param str symbol: unified market symbol
1505
+ :param int [since]: the earliest time in ms to fetch trades for
1506
+ :param int [limit]: the maximum number of trades to retrieve
1507
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1508
+ :param str [params.marginMode]: set to "isolated" to fetch trades for a margin order
1509
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
1510
+ """
1511
+ marginMode = None
1512
+ marginMode, params = self.handle_margin_mode_and_params('fetchOrderTrades', params)
1513
+ if marginMode == 'cross':
1514
+ raise BadRequest(self.id + ' only supports isolated margin')
1515
+ market = None
1516
+ if symbol is not None:
1517
+ market = self.market(symbol)
1518
+ request: dict = {
1519
+ 'order_id': str(id),
1520
+ }
1521
+ response = None
1522
+ if marginMode == 'isolated':
1523
+ response = await self.privatePostMarginUserOrderTrades(self.extend(request, params))
1524
+ #
1525
+ # {
1526
+ # "trades": [
1527
+ # {
1528
+ # "is_maker": False,
1529
+ # "order_id": "123",
1530
+ # "pair": "BTC_USD",
1531
+ # "price": "54122.25",
1532
+ # "quantity": "0.00069994",
1533
+ # "trade_dt": "1619069561718824428",
1534
+ # "trade_id": "692842802860135010",
1535
+ # "type": "sell"
1536
+ # }
1537
+ # ]
1538
+ # }
1539
+ #
1540
+ else:
1541
+ response = await self.privatePostOrderTrades(self.extend(request, params))
1542
+ #
1543
+ # {
1544
+ # "type": "buy",
1545
+ # "in_currency": "BTC",
1546
+ # "in_amount": "1",
1547
+ # "out_currency": "USD",
1548
+ # "out_amount": "100",
1549
+ # "trades": [
1550
+ # {
1551
+ # "trade_id": 3,
1552
+ # "date": 1435488248,
1553
+ # "type": "buy",
1554
+ # "pair": "BTC_USD",
1555
+ # "order_id": 12345,
1556
+ # "quantity": 1,
1557
+ # "price": 100,
1558
+ # "amount": 100,
1559
+ # "exec_type": "taker",
1560
+ # "commission_amount": "0.02",
1561
+ # "commission_currency": "BTC",
1562
+ # "commission_percent": "0.2"
1563
+ # }
1564
+ # ]
1565
+ # }
1566
+ #
1567
+ trades = self.safe_list(response, 'trades')
1568
+ return self.parse_trades(trades, market, since, limit)
1569
+
1570
+ async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1571
+ """
1572
+ fetch all unfilled currently open orders
1573
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#0e135370-daa4-4689-8acd-b6876dee9ba1 # spot open orders
1574
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#a7cfd4f0-476e-4675-b33f-22a46902f245 # margin
1575
+ :param str symbol: unified market symbol
1576
+ :param int [since]: the earliest time in ms to fetch open orders for
1577
+ :param int [limit]: the maximum number of open orders structures to retrieve
1578
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1579
+ :param str [params.marginMode]: set to "isolated" for margin orders
1580
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1581
+ """
1582
+ await self.load_markets()
1583
+ market = None
1584
+ if symbol is not None:
1585
+ market = self.market(symbol)
1586
+ symbol = market['symbol']
1587
+ marginMode = None
1588
+ marginMode, params = self.handle_margin_mode_and_params('fetchOpenOrders', params)
1589
+ isMargin = ((marginMode == 'cross') or (marginMode == 'isolated'))
1590
+ response = None
1591
+ orders = []
1592
+ if isMargin:
1593
+ response = await self.privatePostMarginUserOrderList(params)
1594
+ #
1595
+ # {
1596
+ # "orders": [
1597
+ # {
1598
+ # "client_id": "0",
1599
+ # "comment": "",
1600
+ # "created": "1619068707985325495",
1601
+ # "distance": "0",
1602
+ # "expire": 0,
1603
+ # "funding_currency": "BTC",
1604
+ # "funding_quantity": "0.01",
1605
+ # "funding_rate": "0.02",
1606
+ # "leverage": "2",
1607
+ # "order_id": "123",
1608
+ # "pair": "BTC_USD",
1609
+ # "previous_type": "limit_sell",
1610
+ # "price": "58000",
1611
+ # "quantity": "0.01",
1612
+ # "src": 0,
1613
+ # "stop_price": "0",
1614
+ # "trigger_price": "58000",
1615
+ # "type": "limit_sell",
1616
+ # "updated": 1619068707989411800
1617
+ # }
1618
+ # ]
1619
+ # }
1620
+ #
1621
+ params = self.extend(params, {
1622
+ 'status': 'open',
1623
+ })
1624
+ responseOrders = self.safe_value(response, 'orders')
1625
+ orders = self.parse_orders(responseOrders, market, since, limit, params)
1626
+ else:
1627
+ response = await self.privatePostUserOpenOrders(params)
1628
+ #
1629
+ # {
1630
+ # "USDT_USD": [
1631
+ # {
1632
+ # "parent_order_id": "507061384740151010",
1633
+ # "client_id": "100500",
1634
+ # "created": "1589547391",
1635
+ # "type": "stop_market_buy",
1636
+ # "pair": "USDT_USD",
1637
+ # "quantity": "1",
1638
+ # "trigger_price": "5",
1639
+ # "amount": "5"
1640
+ # }
1641
+ # ],
1642
+ # ...
1643
+ # }
1644
+ #
1645
+ marketIds = list(response.keys())
1646
+ for i in range(0, len(marketIds)):
1647
+ marketId = marketIds[i]
1648
+ marketInner = self.safe_market(marketId)
1649
+ params = self.extend(params, {
1650
+ 'status': 'open',
1651
+ })
1652
+ parsedOrders = self.parse_orders(response[marketId], marketInner, since, limit, params)
1653
+ orders = self.array_concat(orders, parsedOrders)
1654
+ return orders
1655
+
1656
+ def parse_status(self, status):
1657
+ if status is None:
1658
+ return None
1659
+ statuses: dict = {
1660
+ 'cancel_started': 'canceled',
1661
+ }
1662
+ if status.find('cancel') >= 0:
1663
+ status = 'canceled'
1664
+ return self.safe_string(statuses, status, status)
1665
+
1666
+ def parse_side(self, orderType):
1667
+ side: dict = {
1668
+ 'limit_buy': 'buy',
1669
+ 'limit_sell': 'sell',
1670
+ 'market_buy': 'buy',
1671
+ 'market_sell': 'sell',
1672
+ 'stop_buy': 'buy',
1673
+ 'stop_sell': 'sell',
1674
+ 'stop_limit_buy': 'buy',
1675
+ 'stop_limit_sell': 'sell',
1676
+ 'trailing_stop_buy': 'buy',
1677
+ 'trailing_stop_sell': 'sell',
1678
+ 'stop_market_sell': 'sell',
1679
+ 'stop_market_buy': 'buy',
1680
+ 'buy': 'buy',
1681
+ 'sell': 'sell',
1682
+ }
1683
+ return self.safe_string(side, orderType, orderType)
1684
+
1685
+ def parse_order(self, order: dict, market: Market = None) -> Order:
1686
+ #
1687
+ # fetchOrders, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders
1688
+ #
1689
+ # {
1690
+ # "order_id": "14",
1691
+ # "created": "1435517311",
1692
+ # "type": "buy",
1693
+ # "pair": "BTC_USD",
1694
+ # "price": "100",
1695
+ # "quantity": "1",
1696
+ # "amount": "100"
1697
+ # }
1698
+ #
1699
+ # fetchOrder
1700
+ #
1701
+ # {
1702
+ # "type": "buy",
1703
+ # "in_currency": "BTC",
1704
+ # "in_amount": "1",
1705
+ # "out_currency": "USD",
1706
+ # "out_amount": "100",
1707
+ # "trades": [
1708
+ # {
1709
+ # "trade_id": 3,
1710
+ # "date": 1435488248,
1711
+ # "type": "buy",
1712
+ # "pair": "BTC_USD",
1713
+ # "order_id": 12345,
1714
+ # "quantity": 1,
1715
+ # "price": 100,
1716
+ # "amount": 100
1717
+ # }
1718
+ # ]
1719
+ # }
1720
+ #
1721
+ # Margin fetchOpenOrders
1722
+ #
1723
+ # {
1724
+ # "client_id": "0",
1725
+ # "comment": "",
1726
+ # "created": "1619068707985325495",
1727
+ # "distance": "0",
1728
+ # "expire": 0,
1729
+ # "funding_currency": "BTC",
1730
+ # "funding_quantity": "0.01",
1731
+ # "funding_rate": "0.02",
1732
+ # "leverage": "2",
1733
+ # "order_id": "123",
1734
+ # "pair": "BTC_USD",
1735
+ # "previous_type": "limit_sell",
1736
+ # "price": "58000",
1737
+ # "quantity": "0.01",
1738
+ # "src": 0,
1739
+ # "stop_price": "0",
1740
+ # "trigger_price": "58000",
1741
+ # "type": "limit_sell",
1742
+ # "updated": 1619068707989411800
1743
+ # }
1744
+ #
1745
+ # Margin fetchClosedOrders
1746
+ #
1747
+ # {
1748
+ # "distance": "0",
1749
+ # "event_id": "692842802860022508",
1750
+ # "event_time": "1619069531190173720",
1751
+ # "event_type": "OrderCancelStarted",
1752
+ # "order_id": "123",
1753
+ # "order_status": "cancel_started",
1754
+ # "order_type": "limit_sell",
1755
+ # "pair": "BTC_USD",
1756
+ # "price": "54115",
1757
+ # "quantity": "0.001",
1758
+ # "stop_price": "0",
1759
+ # "trade_id": "0",
1760
+ # "trade_price": "0",
1761
+ # "trade_quantity": "0",
1762
+ # "trade_type": ""
1763
+ # },
1764
+ #
1765
+ id = self.safe_string_2(order, 'order_id', 'parent_order_id')
1766
+ eventTime = self.safe_integer_product_2(order, 'event_time', 'created', 0.000001)
1767
+ timestamp = self.safe_timestamp(order, 'created', eventTime)
1768
+ orderType = self.safe_string_2(order, 'type', 'order_type')
1769
+ side = self.parse_side(orderType)
1770
+ marketId = None
1771
+ if 'pair' in order:
1772
+ marketId = order['pair']
1773
+ elif ('in_currency' in order) and ('out_currency' in order):
1774
+ if side == 'buy':
1775
+ marketId = order['in_currency'] + '_' + order['out_currency']
1776
+ else:
1777
+ marketId = order['out_currency'] + '_' + order['in_currency']
1778
+ market = self.safe_market(marketId, market)
1779
+ symbol = market['symbol']
1780
+ amount = self.safe_string(order, 'quantity')
1781
+ if amount is None:
1782
+ amountField = 'in_amount' if (side == 'buy') else 'out_amount'
1783
+ amount = self.safe_string(order, amountField)
1784
+ price = self.safe_string(order, 'price')
1785
+ cost = self.safe_string(order, 'amount')
1786
+ transactions = self.safe_value(order, 'trades', [])
1787
+ clientOrderId = self.safe_integer(order, 'client_id')
1788
+ triggerPrice = self.safe_string(order, 'stop_price')
1789
+ if triggerPrice == '0':
1790
+ triggerPrice = None
1791
+ type = None
1792
+ if (orderType != 'buy') and (orderType != 'sell'):
1793
+ type = orderType
1794
+ return self.safe_order({
1795
+ 'id': id,
1796
+ 'clientOrderId': clientOrderId,
1797
+ 'datetime': self.iso8601(timestamp),
1798
+ 'timestamp': timestamp,
1799
+ 'lastTradeTimestamp': self.safe_integer_product(order, 'updated', 0.000001),
1800
+ 'status': self.parse_status(self.safe_string(order, 'order_status')),
1801
+ 'symbol': symbol,
1802
+ 'type': type,
1803
+ 'timeInForce': None,
1804
+ 'postOnly': None,
1805
+ 'side': side,
1806
+ 'price': price,
1807
+ 'stopPrice': triggerPrice,
1808
+ 'triggerPrice': triggerPrice,
1809
+ 'cost': cost,
1810
+ 'amount': amount,
1811
+ 'filled': None,
1812
+ 'remaining': None,
1813
+ 'average': None,
1814
+ 'trades': transactions,
1815
+ 'fee': None,
1816
+ 'info': order,
1817
+ }, market)
1818
+
1819
+ async def fetch_canceled_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1820
+ """
1821
+ fetches information on multiple canceled orders made by the user
1822
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#1d2524dd-ae6d-403a-a067-77b50d13fbe5 # margin
1823
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#a51be1d0-af5f-44e4-99d7-f7b04c6067d0 # spot canceled orders
1824
+ :param str symbol: unified market symbol of the market orders were made in
1825
+ :param int [since]: timestamp in ms of the earliest order, default is None
1826
+ :param int [limit]: max number of orders to return, default is None
1827
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1828
+ :param str [params.marginMode]: set to "isolated" for margin orders
1829
+ :returns dict: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1830
+ """
1831
+ await self.load_markets()
1832
+ marginMode = None
1833
+ marginMode, params = self.handle_margin_mode_and_params('fetchOrders', params)
1834
+ if marginMode == 'cross':
1835
+ raise BadRequest(self.id + ' only supports isolated margin')
1836
+ if limit is None:
1837
+ limit = 100
1838
+ isSpot = (marginMode != 'isolated')
1839
+ if symbol is not None:
1840
+ marketInner = self.market(symbol)
1841
+ symbol = marketInner['symbol']
1842
+ request: dict = {
1843
+ 'limit': limit,
1844
+ }
1845
+ request['offset'] = limit if (since is not None) else 0
1846
+ request['limit'] = limit
1847
+ market = None
1848
+ if symbol is not None:
1849
+ market = self.market(symbol)
1850
+ response = None
1851
+ if isSpot:
1852
+ response = await self.privatePostUserCancelledOrders(self.extend(request, params))
1853
+ #
1854
+ # [
1855
+ # {
1856
+ # "order_id": "27056153840",
1857
+ # "client_id": "0",
1858
+ # "created": "1653428646",
1859
+ # "type": "buy",
1860
+ # "pair": "BTC_USDT",
1861
+ # "quantity": "0.1",
1862
+ # "price": "10",
1863
+ # "amount": "1"
1864
+ # }
1865
+ # ]
1866
+ #
1867
+ params = self.extend(params, {
1868
+ 'status': 'canceled',
1869
+ })
1870
+ return self.parse_orders(response, market, since, limit, params)
1871
+ else:
1872
+ responseSwap = await self.privatePostMarginUserOrderHistory(self.extend(request, params))
1873
+ #
1874
+ # {
1875
+ # "items": [
1876
+ # {
1877
+ # "event_id": "692862104574106858",
1878
+ # "event_time": "1694116400173489405",
1879
+ # "event_type": "OrderCancelStarted",
1880
+ # "order_id": "692862104561289319",
1881
+ # "order_type": "stop_limit_sell",
1882
+ # "order_status": "cancel_started",
1883
+ # "trade_id": "0",
1884
+ # "trade_type":"",
1885
+ # "trade_quantity": "0",
1886
+ # "trade_price": "0",
1887
+ # "pair": "ADA_USDT",
1888
+ # "quantity": "12",
1889
+ # "price": "0.23",
1890
+ # "stop_price": "0.22",
1891
+ # "distance": "0"
1892
+ # }
1893
+ # ...
1894
+ # ]
1895
+ # }
1896
+ #
1897
+ items = self.safe_value(responseSwap, 'items')
1898
+ orders = self.parse_orders(items, market, since, limit, params)
1899
+ result = []
1900
+ for i in range(0, len(orders)):
1901
+ order = orders[i]
1902
+ if order['status'] == 'canceled':
1903
+ result.append(order)
1904
+ return result
1905
+
1906
+ async def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
1907
+ """
1908
+ *margin only* edit a trade order
1909
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#f27ee040-c75f-4b59-b608-d05bd45b7899 # margin
1910
+ :param str id: order id
1911
+ :param str symbol: unified CCXT market symbol
1912
+ :param str type: not used by exmo editOrder
1913
+ :param str side: not used by exmo editOrder
1914
+ :param float [amount]: how much of the currency you want to trade in units of the base currency
1915
+ :param float [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
1916
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1917
+ :param float [params.triggerPrice]: stop price for stop-market and stop-limit orders
1918
+ :param str params['marginMode']: must be set to isolated
1919
+ *
1920
+ * EXCHANGE SPECIFIC PARAMETERS
1921
+ :param int [params.distance]: distance for trailing stop orders
1922
+ :param int [params.expire]: expiration timestamp in UTC timezone for the order. order will not be expired if expire is 0
1923
+ :param str [params.comment]: optional comment for order. up to 50 latin symbols, whitespaces, underscores
1924
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1925
+ """
1926
+ await self.load_markets()
1927
+ market = self.market(symbol)
1928
+ marginMode = None
1929
+ marginMode, params = self.handle_margin_mode_and_params('editOrder', params)
1930
+ if marginMode != 'isolated':
1931
+ raise BadRequest(self.id + ' editOrder() can only be used for isolated margin orders')
1932
+ triggerPrice = self.safe_number_n(params, ['triggerPrice', 'stopPrice', 'stop_price'])
1933
+ params = self.omit(params, ['triggerPrice', 'stopPrice'])
1934
+ request: dict = {
1935
+ 'order_id': id, # id of the open order
1936
+ }
1937
+ if amount is not None:
1938
+ request['quantity'] = amount
1939
+ if price is not None:
1940
+ request['price'] = self.price_to_precision(market['symbol'], price)
1941
+ if triggerPrice is not None:
1942
+ request['stop_price'] = self.price_to_precision(market['symbol'], triggerPrice)
1943
+ response = await self.privatePostMarginUserOrderUpdate(self.extend(request, params))
1944
+ return self.parse_order(response)
1945
+
1946
+ async def fetch_deposit_address(self, code: str, params={}):
1947
+ """
1948
+ fetch the deposit address for a currency associated with self account
1949
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#c8f9ced9-7ab6-4383-a6a4-bc54469ba60e
1950
+ :param str code: unified currency code
1951
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1952
+ :returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
1953
+ """
1954
+ await self.load_markets()
1955
+ response = await self.privatePostDepositAddress(params)
1956
+ #
1957
+ # {
1958
+ # "TRX":"TBnwrf4ZdoYXE3C8L2KMs7YPSL3fg6q6V9",
1959
+ # "USDTTRC20":"TBnwrf4ZdoYXE3C8L2KMs7YPSL3fg6q6V9"
1960
+ # }
1961
+ #
1962
+ depositAddress = self.safe_string(response, code)
1963
+ address = None
1964
+ tag = None
1965
+ if depositAddress:
1966
+ addressAndTag = depositAddress.split(',')
1967
+ address = addressAndTag[0]
1968
+ numParts = len(addressAndTag)
1969
+ if numParts > 1:
1970
+ tag = addressAndTag[1]
1971
+ self.check_address(address)
1972
+ return {
1973
+ 'currency': code,
1974
+ 'address': address,
1975
+ 'tag': tag,
1976
+ 'network': None,
1977
+ 'info': response,
1978
+ }
1979
+
1980
+ def get_market_from_trades(self, trades):
1981
+ tradesBySymbol = self.index_by(trades, 'pair')
1982
+ symbols = list(tradesBySymbol.keys())
1983
+ numSymbols = len(symbols)
1984
+ if numSymbols == 1:
1985
+ return self.markets[symbols[0]]
1986
+ return None
1987
+
1988
+ async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
1989
+ """
1990
+ make a withdrawal
1991
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#3ab9c34d-ad58-4f87-9c57-2e2ea88a8325
1992
+ :param str code: unified currency code
1993
+ :param float amount: the amount to withdraw
1994
+ :param str address: the address to withdraw to
1995
+ :param str tag:
1996
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1997
+ :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
1998
+ """
1999
+ tag, params = self.handle_withdraw_tag_and_params(tag, params)
2000
+ await self.load_markets()
2001
+ currency = self.currency(code)
2002
+ request: dict = {
2003
+ 'amount': amount,
2004
+ 'currency': currency['id'],
2005
+ 'address': address,
2006
+ }
2007
+ if tag is not None:
2008
+ request['invoice'] = tag
2009
+ networks = self.safe_value(self.options, 'networks', {})
2010
+ network = self.safe_string_upper(params, 'network') # self line allows the user to specify either ERC20 or ETH
2011
+ network = self.safe_string(networks, network, network) # handle ERC20>ETH alias
2012
+ if network is not None:
2013
+ request['transport'] = network
2014
+ params = self.omit(params, 'network')
2015
+ response = await self.privatePostWithdrawCrypt(self.extend(request, params))
2016
+ return self.parse_transaction(response, currency)
2017
+
2018
+ def parse_transaction_status(self, status: Str):
2019
+ statuses: dict = {
2020
+ 'transferred': 'ok',
2021
+ 'paid': 'ok',
2022
+ 'pending': 'pending',
2023
+ 'processing': 'pending',
2024
+ 'verifying': 'pending',
2025
+ }
2026
+ return self.safe_string(statuses, status, status)
2027
+
2028
+ def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
2029
+ #
2030
+ # fetchDepositsWithdrawals
2031
+ #
2032
+ # {
2033
+ # "dt": 1461841192,
2034
+ # "type": "deposit",
2035
+ # "curr": "RUB",
2036
+ # "status": "processing",
2037
+ # "provider": "Qiwi(LA) [12345]",
2038
+ # "amount": "1",
2039
+ # "account": "",
2040
+ # "txid": "ec46f784ad976fd7f7539089d1a129fe46...",
2041
+ # }
2042
+ #
2043
+ # fetchWithdrawals
2044
+ #
2045
+ # {
2046
+ # "operation_id": 47412538520634344,
2047
+ # "created": 1573760013,
2048
+ # "updated": 1573760013,
2049
+ # "type": "withdraw",
2050
+ # "currency": "DOGE",
2051
+ # "status": "Paid",
2052
+ # "amount": "300",
2053
+ # "provider": "DOGE",
2054
+ # "commission": "0",
2055
+ # "account": "DOGE: DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS",
2056
+ # "order_id": 69670170,
2057
+ # "provider_type": "crypto",
2058
+ # "crypto_address": "DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS",
2059
+ # "card_number": "",
2060
+ # "wallet_address": "",
2061
+ # "email": "",
2062
+ # "phone": "",
2063
+ # "extra": {
2064
+ # "txid": "f2b66259ae1580f371d38dd27e31a23fff8c04122b65ee3ab5a3f612d579c792",
2065
+ # "confirmations": null,
2066
+ # "excode": "",
2067
+ # "invoice": ""
2068
+ # },
2069
+ # "error": ""
2070
+ # }
2071
+ #
2072
+ # withdraw
2073
+ #
2074
+ # {
2075
+ # "result": True,
2076
+ # "error": "",
2077
+ # "task_id": 11775077
2078
+ # }
2079
+ #
2080
+ timestamp = self.safe_timestamp_2(transaction, 'dt', 'created')
2081
+ amountString = self.safe_string(transaction, 'amount')
2082
+ if amountString is not None:
2083
+ amountString = Precise.string_abs(amountString)
2084
+ txid = self.safe_string(transaction, 'txid')
2085
+ if txid is None:
2086
+ extra = self.safe_value(transaction, 'extra', {})
2087
+ extraTxid = self.safe_string(extra, 'txid')
2088
+ if extraTxid != '':
2089
+ txid = extraTxid
2090
+ type = self.safe_string(transaction, 'type')
2091
+ currencyId = self.safe_string_2(transaction, 'curr', 'currency')
2092
+ code = self.safe_currency_code(currencyId, currency)
2093
+ address = None
2094
+ comment = None
2095
+ account = self.safe_string(transaction, 'account')
2096
+ if type == 'deposit':
2097
+ comment = account
2098
+ elif type == 'withdrawal':
2099
+ address = account
2100
+ if address is not None:
2101
+ parts = address.split(':')
2102
+ numParts = len(parts)
2103
+ if numParts == 2:
2104
+ address = self.safe_string(parts, 1)
2105
+ address = address.replace(' ', '')
2106
+ fee = {
2107
+ 'currency': None,
2108
+ 'cost': None,
2109
+ 'rate': None,
2110
+ }
2111
+ # fixed funding fees only(for now)
2112
+ if not self.fees['transaction']['percentage']:
2113
+ key = 'withdraw' if (type == 'withdrawal') else 'deposit'
2114
+ feeCost = self.safe_string(transaction, 'commission')
2115
+ if feeCost is None:
2116
+ transactionFees = self.safe_value(self.options, 'transactionFees', {})
2117
+ codeFees = self.safe_value(transactionFees, code, {})
2118
+ feeCost = self.safe_string(codeFees, key)
2119
+ # users don't pay for cashbacks, no fees for that
2120
+ provider = self.safe_string(transaction, 'provider')
2121
+ if provider == 'cashback':
2122
+ feeCost = '0'
2123
+ if feeCost is not None:
2124
+ # withdrawal amount includes the fee
2125
+ if type == 'withdrawal':
2126
+ amountString = Precise.string_sub(amountString, feeCost)
2127
+ fee['cost'] = self.parse_number(feeCost)
2128
+ fee['currency'] = code
2129
+ return {
2130
+ 'info': transaction,
2131
+ 'id': self.safe_string_2(transaction, 'order_id', 'task_id'),
2132
+ 'txid': txid,
2133
+ 'type': type,
2134
+ 'currency': code,
2135
+ 'network': self.safe_string(transaction, 'provider'),
2136
+ 'amount': self.parse_number(amountString),
2137
+ 'status': self.parse_transaction_status(self.safe_string_lower(transaction, 'status')),
2138
+ 'timestamp': timestamp,
2139
+ 'datetime': self.iso8601(timestamp),
2140
+ 'address': address,
2141
+ 'addressFrom': None,
2142
+ 'addressTo': address,
2143
+ 'tag': None,
2144
+ 'tagFrom': None,
2145
+ 'tagTo': None,
2146
+ 'updated': self.safe_timestamp(transaction, 'updated'),
2147
+ 'comment': comment,
2148
+ 'internal': None,
2149
+ 'fee': fee,
2150
+ }
2151
+
2152
+ async def fetch_deposits_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
2153
+ """
2154
+ fetch history of deposits and withdrawals
2155
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#31e69a33-4849-4e6a-b4b4-6d574238f6a7
2156
+ :param str [code]: unified currency code for the currency of the deposit/withdrawals, default is None
2157
+ :param int [since]: timestamp in ms of the earliest deposit/withdrawal, default is None
2158
+ :param int [limit]: max number of deposit/withdrawals to return, default is None
2159
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2160
+ :returns dict: a list of `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
2161
+ """
2162
+ await self.load_markets()
2163
+ request: dict = {}
2164
+ if since is not None:
2165
+ request['date'] = self.parse_to_int(since / 1000)
2166
+ currency = None
2167
+ if code is not None:
2168
+ currency = self.currency(code)
2169
+ response = await self.privatePostWalletHistory(self.extend(request, params))
2170
+ #
2171
+ # {
2172
+ # "result": True,
2173
+ # "error": "",
2174
+ # "begin": "1493942400",
2175
+ # "end": "1494028800",
2176
+ # "history": [
2177
+ # {
2178
+ # "dt": 1461841192,
2179
+ # "type": "deposit",
2180
+ # "curr": "RUB",
2181
+ # "status": "processing",
2182
+ # "provider": "Qiwi(LA) [12345]",
2183
+ # "amount": "1",
2184
+ # "account": "",
2185
+ # "txid": "ec46f784ad976fd7f7539089d1a129fe46...",
2186
+ # },
2187
+ # {
2188
+ # "dt": 1463414785,
2189
+ # "type": "withdrawal",
2190
+ # "curr": "USD",
2191
+ # "status": "paid",
2192
+ # "provider": "EXCODE",
2193
+ # "amount": "-1",
2194
+ # "account": "EX-CODE_19371_USDda...",
2195
+ # "txid": "",
2196
+ # },
2197
+ # ],
2198
+ # }
2199
+ #
2200
+ return self.parse_transactions(response['history'], currency, since, limit)
2201
+
2202
+ async def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
2203
+ """
2204
+ fetch all withdrawals made from an account
2205
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#97f1becd-7aad-4e0e-babe-7bbe09e33706
2206
+ :param str code: unified currency code
2207
+ :param int [since]: the earliest time in ms to fetch withdrawals for
2208
+ :param int [limit]: the maximum number of withdrawals structures to retrieve
2209
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2210
+ :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
2211
+ """
2212
+ await self.load_markets()
2213
+ currency = None
2214
+ request: dict = {
2215
+ 'type': 'withdraw',
2216
+ }
2217
+ if limit is not None:
2218
+ request['limit'] = limit # default: 100, maximum: 100
2219
+ if code is not None:
2220
+ currency = self.currency(code)
2221
+ request['currency'] = currency['id']
2222
+ response = await self.privatePostWalletOperations(self.extend(request, params))
2223
+ #
2224
+ # {
2225
+ # "items": [
2226
+ # {
2227
+ # "operation_id": 47412538520634344,
2228
+ # "created": 1573760013,
2229
+ # "updated": 1573760013,
2230
+ # "type": "withdraw",
2231
+ # "currency": "DOGE",
2232
+ # "status": "Paid",
2233
+ # "amount": "300",
2234
+ # "provider": "DOGE",
2235
+ # "commission": "0",
2236
+ # "account": "DOGE: DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS",
2237
+ # "order_id": 69670170,
2238
+ # "extra": {
2239
+ # "txid": "f2b66259ae1580f371d38dd27e31a23fff8c04122b65ee3ab5a3f612d579c792",
2240
+ # "excode": "",
2241
+ # "invoice": ""
2242
+ # },
2243
+ # "error": ""
2244
+ # },
2245
+ # ],
2246
+ # "count": 23
2247
+ # }
2248
+ #
2249
+ items = self.safe_list(response, 'items', [])
2250
+ return self.parse_transactions(items, currency, since, limit)
2251
+
2252
+ async def fetch_withdrawal(self, id: str, code: Str = None, params={}):
2253
+ """
2254
+ fetch data on a currency withdrawal via the withdrawal id
2255
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#97f1becd-7aad-4e0e-babe-7bbe09e33706
2256
+ :param str id: withdrawal id
2257
+ :param str code: unified currency code of the currency withdrawn, default is None
2258
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2259
+ :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
2260
+ """
2261
+ await self.load_markets()
2262
+ currency = None
2263
+ request: dict = {
2264
+ 'order_id': id,
2265
+ 'type': 'withdraw',
2266
+ }
2267
+ if code is not None:
2268
+ currency = self.currency(code)
2269
+ request['currency'] = currency['id']
2270
+ response = await self.privatePostWalletOperations(self.extend(request, params))
2271
+ #
2272
+ # {
2273
+ # "items": [
2274
+ # {
2275
+ # "operation_id": 47412538520634344,
2276
+ # "created": 1573760013,
2277
+ # "updated": 1573760013,
2278
+ # "type": "deposit",
2279
+ # "currency": "DOGE",
2280
+ # "status": "Paid",
2281
+ # "amount": "300",
2282
+ # "provider": "DOGE",
2283
+ # "commission": "0",
2284
+ # "account": "DOGE: DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS",
2285
+ # "order_id": 69670170,
2286
+ # "extra": {
2287
+ # "txid": "f2b66259ae1580f371d38dd27e31a23fff8c04122b65ee3ab5a3f612d579c792",
2288
+ # "excode": "",
2289
+ # "invoice": ""
2290
+ # },
2291
+ # "error": ""
2292
+ # },
2293
+ # ],
2294
+ # "count": 23
2295
+ # }
2296
+ #
2297
+ items = self.safe_value(response, 'items', [])
2298
+ first = self.safe_dict(items, 0, {})
2299
+ return self.parse_transaction(first, currency)
2300
+
2301
+ async def fetch_deposit(self, id=None, code: Str = None, params={}):
2302
+ """
2303
+ fetch information on a deposit
2304
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#97f1becd-7aad-4e0e-babe-7bbe09e33706
2305
+ :param str id: deposit id
2306
+ :param str code: unified currency code, default is None
2307
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2308
+ :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
2309
+ """
2310
+ await self.load_markets()
2311
+ currency = None
2312
+ request: dict = {
2313
+ 'order_id': id,
2314
+ 'type': 'deposit',
2315
+ }
2316
+ if code is not None:
2317
+ currency = self.currency(code)
2318
+ request['currency'] = currency['id']
2319
+ response = await self.privatePostWalletOperations(self.extend(request, params))
2320
+ #
2321
+ # {
2322
+ # "items": [
2323
+ # {
2324
+ # "operation_id": 47412538520634344,
2325
+ # "created": 1573760013,
2326
+ # "updated": 1573760013,
2327
+ # "type": "deposit",
2328
+ # "currency": "DOGE",
2329
+ # "status": "Paid",
2330
+ # "amount": "300",
2331
+ # "provider": "DOGE",
2332
+ # "commission": "0",
2333
+ # "account": "DOGE: DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS",
2334
+ # "order_id": 69670170,
2335
+ # "extra": {
2336
+ # "txid": "f2b66259ae1580f371d38dd27e31a23fff8c04122b65ee3ab5a3f612d579c792",
2337
+ # "excode": "",
2338
+ # "invoice": ""
2339
+ # },
2340
+ # "error": ""
2341
+ # },
2342
+ # ],
2343
+ # "count": 23
2344
+ # }
2345
+ #
2346
+ items = self.safe_value(response, 'items', [])
2347
+ first = self.safe_dict(items, 0, {})
2348
+ return self.parse_transaction(first, currency)
2349
+
2350
+ async def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
2351
+ """
2352
+ fetch all deposits made to an account
2353
+ :see: https://documenter.getpostman.com/view/10287440/SzYXWKPi#97f1becd-7aad-4e0e-babe-7bbe09e33706
2354
+ :param str code: unified currency code
2355
+ :param int [since]: the earliest time in ms to fetch deposits for
2356
+ :param int [limit]: the maximum number of deposits structures to retrieve
2357
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2358
+ :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
2359
+ """
2360
+ await self.load_markets()
2361
+ currency = None
2362
+ request: dict = {
2363
+ 'type': 'deposit',
2364
+ }
2365
+ if limit is not None:
2366
+ request['limit'] = limit # default: 100, maximum: 100
2367
+ if code is not None:
2368
+ currency = self.currency(code)
2369
+ request['currency'] = currency['id']
2370
+ response = await self.privatePostWalletOperations(self.extend(request, params))
2371
+ #
2372
+ # {
2373
+ # "items": [
2374
+ # {
2375
+ # "operation_id": 47412538520634344,
2376
+ # "created": 1573760013,
2377
+ # "updated": 1573760013,
2378
+ # "type": "deposit",
2379
+ # "currency": "DOGE",
2380
+ # "status": "Paid",
2381
+ # "amount": "300",
2382
+ # "provider": "DOGE",
2383
+ # "commission": "0",
2384
+ # "account": "DOGE: DBVy8pF1f8yxaCVEHqHeR7kkcHecLQ8nRS",
2385
+ # "order_id": 69670170,
2386
+ # "extra": {
2387
+ # "txid": "f2b66259ae1580f371d38dd27e31a23fff8c04122b65ee3ab5a3f612d579c792",
2388
+ # "excode": "",
2389
+ # "invoice": ""
2390
+ # },
2391
+ # "error": ""
2392
+ # },
2393
+ # ],
2394
+ # "count": 23
2395
+ # }
2396
+ #
2397
+ items = self.safe_list(response, 'items', [])
2398
+ return self.parse_transactions(items, currency, since, limit)
2399
+
2400
+ def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
2401
+ url = self.urls['api'][api] + '/'
2402
+ if api != 'web':
2403
+ url += self.version + '/'
2404
+ url += path
2405
+ if (api == 'public') or (api == 'web'):
2406
+ if params:
2407
+ url += '?' + self.urlencode(params)
2408
+ elif api == 'private':
2409
+ self.check_required_credentials()
2410
+ nonce = self.nonce()
2411
+ body = self.urlencode(self.extend({'nonce': nonce}, params))
2412
+ headers = {
2413
+ 'Content-Type': 'application/x-www-form-urlencoded',
2414
+ 'Key': self.apiKey,
2415
+ 'Sign': self.hmac(self.encode(body), self.encode(self.secret), hashlib.sha512),
2416
+ }
2417
+ return {'url': url, 'method': method, 'body': body, 'headers': headers}
2418
+
2419
+ def nonce(self):
2420
+ return self.milliseconds()
2421
+
2422
+ def handle_errors(self, httpCode: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
2423
+ if response is None:
2424
+ return None # fallback to default error handler
2425
+ if ('error' in response) and not ('result' in response):
2426
+ # error: {
2427
+ # "code": "140434",
2428
+ # "msg": "Your margin balance is not sufficient to place the order for '5 TON'. Please top up your margin wallet by "2.5 USDT"."
2429
+ # }
2430
+ #
2431
+ errorCode = self.safe_value(response, 'error', {})
2432
+ messageError = self.safe_string(errorCode, 'msg')
2433
+ code = self.safe_string(errorCode, 'code')
2434
+ feedback = self.id + ' ' + body
2435
+ self.throw_exactly_matched_exception(self.exceptions['exact'], code, feedback)
2436
+ self.throw_broadly_matched_exception(self.exceptions['broad'], messageError, feedback)
2437
+ raise ExchangeError(feedback)
2438
+ if ('result' in response) or ('errmsg' in response):
2439
+ #
2440
+ # {"result":false,"error":"Error 50052: Insufficient funds"}
2441
+ # {"s":"error","errmsg":"strconv.ParseInt: parsing \"\": invalid syntax"}
2442
+ #
2443
+ success = self.safe_bool(response, 'result', False)
2444
+ if isinstance(success, str):
2445
+ if (success == 'true') or (success == '1'):
2446
+ success = True
2447
+ else:
2448
+ success = False
2449
+ if not success:
2450
+ code = None
2451
+ message = self.safe_string_2(response, 'error', 'errmsg')
2452
+ errorParts = message.split(':')
2453
+ numParts = len(errorParts)
2454
+ if numParts > 1:
2455
+ errorSubParts = errorParts[0].split(' ')
2456
+ numSubParts = len(errorSubParts)
2457
+ code = errorSubParts[1] if (numSubParts > 1) else errorSubParts[0]
2458
+ feedback = self.id + ' ' + body
2459
+ self.throw_exactly_matched_exception(self.exceptions['exact'], code, feedback)
2460
+ self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
2461
+ raise ExchangeError(feedback)
2462
+ return None