ccxt 4.2.77__py2.py3-none-any.whl → 4.4.48__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (546) hide show
  1. ccxt/__init__.py +36 -14
  2. ccxt/abstract/alpaca.py +4 -0
  3. ccxt/abstract/bigone.py +1 -1
  4. ccxt/abstract/binance.py +112 -48
  5. ccxt/abstract/binancecoinm.py +112 -48
  6. ccxt/abstract/binanceus.py +147 -83
  7. ccxt/abstract/binanceusdm.py +112 -48
  8. ccxt/abstract/bingx.py +133 -78
  9. ccxt/abstract/bitbank.py +5 -0
  10. ccxt/abstract/bitfinex.py +136 -65
  11. ccxt/abstract/bitfinex1.py +69 -0
  12. ccxt/abstract/bitflyer.py +1 -0
  13. ccxt/abstract/bitget.py +8 -1
  14. ccxt/abstract/bitmart.py +13 -1
  15. ccxt/abstract/bitopro.py +1 -0
  16. ccxt/abstract/bitpanda.py +0 -12
  17. ccxt/abstract/bitrue.py +3 -3
  18. ccxt/abstract/bitstamp.py +26 -3
  19. ccxt/abstract/blofin.py +24 -0
  20. ccxt/abstract/btcbox.py +1 -0
  21. ccxt/abstract/bybit.py +29 -14
  22. ccxt/abstract/cex.py +28 -29
  23. ccxt/abstract/coinbase.py +6 -0
  24. ccxt/abstract/coinbaseadvanced.py +94 -0
  25. ccxt/abstract/{coinbasepro.py → coinbaseexchange.py} +1 -0
  26. ccxt/abstract/coinbaseinternational.py +1 -1
  27. ccxt/abstract/coincatch.py +94 -0
  28. ccxt/abstract/coinex.py +233 -123
  29. ccxt/abstract/coinmetro.py +1 -0
  30. ccxt/abstract/cryptocom.py +14 -0
  31. ccxt/abstract/defx.py +69 -0
  32. ccxt/abstract/deribit.py +1 -0
  33. ccxt/abstract/digifinex.py +1 -0
  34. ccxt/abstract/ellipx.py +25 -0
  35. ccxt/abstract/gate.py +20 -0
  36. ccxt/abstract/gateio.py +20 -0
  37. ccxt/abstract/gemini.py +1 -0
  38. ccxt/abstract/hashkey.py +67 -0
  39. ccxt/abstract/hyperliquid.py +1 -1
  40. ccxt/abstract/independentreserve.py +6 -0
  41. ccxt/abstract/kraken.py +4 -3
  42. ccxt/abstract/krakenfutures.py +4 -0
  43. ccxt/abstract/kucoin.py +24 -0
  44. ccxt/abstract/kucoinfutures.py +34 -0
  45. ccxt/abstract/luno.py +2 -0
  46. ccxt/abstract/mexc.py +4 -0
  47. ccxt/abstract/myokx.py +340 -0
  48. ccxt/abstract/oceanex.py +5 -0
  49. ccxt/abstract/okx.py +30 -0
  50. ccxt/abstract/onetrading.py +0 -12
  51. ccxt/abstract/oxfun.py +34 -0
  52. ccxt/abstract/paradex.py +40 -0
  53. ccxt/abstract/phemex.py +1 -0
  54. ccxt/abstract/upbit.py +4 -0
  55. ccxt/abstract/vertex.py +19 -0
  56. ccxt/abstract/whitebit.py +31 -1
  57. ccxt/abstract/woo.py +6 -2
  58. ccxt/abstract/woofipro.py +119 -0
  59. ccxt/abstract/xt.py +153 -0
  60. ccxt/abstract/zonda.py +6 -0
  61. ccxt/ace.py +164 -60
  62. ccxt/alpaca.py +727 -63
  63. ccxt/ascendex.py +395 -249
  64. ccxt/async_support/__init__.py +36 -14
  65. ccxt/async_support/ace.py +164 -60
  66. ccxt/async_support/alpaca.py +727 -63
  67. ccxt/async_support/ascendex.py +396 -249
  68. ccxt/async_support/base/exchange.py +531 -155
  69. ccxt/async_support/base/ws/aiohttp_client.py +28 -5
  70. ccxt/async_support/base/ws/cache.py +3 -2
  71. ccxt/async_support/base/ws/client.py +26 -5
  72. ccxt/async_support/base/ws/fast_client.py +4 -3
  73. ccxt/async_support/base/ws/functions.py +1 -1
  74. ccxt/async_support/base/ws/future.py +40 -31
  75. ccxt/async_support/base/ws/order_book_side.py +3 -0
  76. ccxt/async_support/bequant.py +1 -1
  77. ccxt/async_support/bigone.py +329 -202
  78. ccxt/async_support/binance.py +3030 -1087
  79. ccxt/async_support/binancecoinm.py +2 -1
  80. ccxt/async_support/binanceus.py +12 -1
  81. ccxt/async_support/binanceusdm.py +3 -1
  82. ccxt/async_support/bingx.py +3104 -880
  83. ccxt/async_support/bit2c.py +119 -38
  84. ccxt/async_support/bitbank.py +215 -76
  85. ccxt/async_support/bitbns.py +124 -53
  86. ccxt/async_support/bitfinex.py +3236 -1078
  87. ccxt/async_support/bitfinex1.py +1711 -0
  88. ccxt/async_support/bitflyer.py +238 -49
  89. ccxt/async_support/bitget.py +1513 -563
  90. ccxt/async_support/bithumb.py +199 -65
  91. ccxt/async_support/bitmart.py +1320 -435
  92. ccxt/async_support/bitmex.py +308 -111
  93. ccxt/async_support/bitopro.py +256 -96
  94. ccxt/async_support/bitrue.py +365 -233
  95. ccxt/async_support/bitso.py +201 -89
  96. ccxt/async_support/bitstamp.py +438 -269
  97. ccxt/async_support/bitteam.py +179 -73
  98. ccxt/async_support/bitvavo.py +180 -70
  99. ccxt/async_support/bl3p.py +92 -25
  100. ccxt/async_support/blockchaincom.py +193 -79
  101. ccxt/async_support/blofin.py +392 -148
  102. ccxt/async_support/btcalpha.py +161 -55
  103. ccxt/async_support/btcbox.py +250 -34
  104. ccxt/async_support/btcmarkets.py +232 -85
  105. ccxt/async_support/btcturk.py +159 -60
  106. ccxt/async_support/bybit.py +2231 -1193
  107. ccxt/async_support/cex.py +1409 -1329
  108. ccxt/async_support/coinbase.py +1454 -287
  109. ccxt/async_support/coinbaseadvanced.py +17 -0
  110. ccxt/async_support/{coinbasepro.py → coinbaseexchange.py} +233 -99
  111. ccxt/async_support/coinbaseinternational.py +428 -88
  112. ccxt/async_support/coincatch.py +5152 -0
  113. ccxt/async_support/coincheck.py +121 -38
  114. ccxt/async_support/coinex.py +4020 -3339
  115. ccxt/async_support/coinlist.py +273 -116
  116. ccxt/async_support/coinmate.py +204 -97
  117. ccxt/async_support/coinmetro.py +203 -110
  118. ccxt/async_support/coinone.py +142 -68
  119. ccxt/async_support/coinsph.py +206 -89
  120. ccxt/async_support/coinspot.py +137 -62
  121. ccxt/async_support/cryptocom.py +515 -185
  122. ccxt/async_support/currencycom.py +203 -85
  123. ccxt/async_support/defx.py +2066 -0
  124. ccxt/async_support/delta.py +404 -109
  125. ccxt/async_support/deribit.py +557 -323
  126. ccxt/async_support/digifinex.py +340 -223
  127. ccxt/async_support/ellipx.py +1826 -0
  128. ccxt/async_support/exmo.py +259 -128
  129. ccxt/async_support/gate.py +1472 -463
  130. ccxt/async_support/gemini.py +206 -84
  131. ccxt/async_support/hashkey.py +4164 -0
  132. ccxt/async_support/hitbtc.py +334 -178
  133. ccxt/async_support/hollaex.py +134 -83
  134. ccxt/async_support/htx.py +1095 -563
  135. ccxt/async_support/huobijp.py +105 -56
  136. ccxt/async_support/hyperliquid.py +1633 -268
  137. ccxt/async_support/idex.py +148 -95
  138. ccxt/async_support/independentreserve.py +236 -31
  139. ccxt/async_support/indodax.py +165 -62
  140. ccxt/async_support/kraken.py +871 -354
  141. ccxt/async_support/krakenfutures.py +324 -100
  142. ccxt/async_support/kucoin.py +917 -357
  143. ccxt/async_support/kucoinfutures.py +1004 -149
  144. ccxt/async_support/kuna.py +138 -106
  145. ccxt/async_support/latoken.py +135 -79
  146. ccxt/async_support/lbank.py +290 -113
  147. ccxt/async_support/luno.py +112 -62
  148. ccxt/async_support/lykke.py +104 -55
  149. ccxt/async_support/mercado.py +36 -29
  150. ccxt/async_support/mexc.py +995 -429
  151. ccxt/async_support/myokx.py +43 -0
  152. ccxt/async_support/ndax.py +163 -82
  153. ccxt/async_support/novadax.py +121 -75
  154. ccxt/async_support/oceanex.py +175 -59
  155. ccxt/async_support/okcoin.py +222 -163
  156. ccxt/async_support/okx.py +1776 -454
  157. ccxt/async_support/onetrading.py +132 -414
  158. ccxt/async_support/oxfun.py +2832 -0
  159. ccxt/async_support/p2b.py +79 -51
  160. ccxt/async_support/paradex.py +2017 -0
  161. ccxt/async_support/paymium.py +56 -32
  162. ccxt/async_support/phemex.py +572 -196
  163. ccxt/async_support/poloniex.py +218 -95
  164. ccxt/async_support/poloniexfutures.py +260 -92
  165. ccxt/async_support/probit.py +143 -110
  166. ccxt/async_support/timex.py +123 -70
  167. ccxt/async_support/tokocrypto.py +129 -93
  168. ccxt/async_support/tradeogre.py +39 -25
  169. ccxt/async_support/upbit.py +322 -113
  170. ccxt/async_support/vertex.py +2983 -0
  171. ccxt/async_support/wavesexchange.py +227 -173
  172. ccxt/async_support/wazirx.py +145 -65
  173. ccxt/async_support/whitebit.py +533 -138
  174. ccxt/async_support/woo.py +1137 -296
  175. ccxt/async_support/woofipro.py +2716 -0
  176. ccxt/async_support/xt.py +4628 -0
  177. ccxt/async_support/yobit.py +160 -92
  178. ccxt/async_support/zaif.py +80 -33
  179. ccxt/async_support/zonda.py +140 -69
  180. ccxt/base/errors.py +51 -20
  181. ccxt/base/exchange.py +1722 -480
  182. ccxt/base/precise.py +10 -0
  183. ccxt/base/types.py +223 -4
  184. ccxt/bequant.py +1 -1
  185. ccxt/bigone.py +329 -202
  186. ccxt/binance.py +3030 -1087
  187. ccxt/binancecoinm.py +2 -1
  188. ccxt/binanceus.py +12 -1
  189. ccxt/binanceusdm.py +3 -1
  190. ccxt/bingx.py +3104 -880
  191. ccxt/bit2c.py +119 -38
  192. ccxt/bitbank.py +215 -76
  193. ccxt/bitbns.py +124 -53
  194. ccxt/bitfinex.py +3235 -1078
  195. ccxt/bitfinex1.py +1710 -0
  196. ccxt/bitflyer.py +238 -49
  197. ccxt/bitget.py +1513 -563
  198. ccxt/bithumb.py +198 -65
  199. ccxt/bitmart.py +1320 -435
  200. ccxt/bitmex.py +308 -111
  201. ccxt/bitopro.py +256 -96
  202. ccxt/bitrue.py +365 -233
  203. ccxt/bitso.py +201 -89
  204. ccxt/bitstamp.py +438 -269
  205. ccxt/bitteam.py +179 -73
  206. ccxt/bitvavo.py +180 -70
  207. ccxt/bl3p.py +92 -25
  208. ccxt/blockchaincom.py +193 -79
  209. ccxt/blofin.py +392 -148
  210. ccxt/btcalpha.py +161 -55
  211. ccxt/btcbox.py +250 -34
  212. ccxt/btcmarkets.py +232 -85
  213. ccxt/btcturk.py +159 -60
  214. ccxt/bybit.py +2231 -1193
  215. ccxt/cex.py +1408 -1329
  216. ccxt/coinbase.py +1454 -287
  217. ccxt/coinbaseadvanced.py +17 -0
  218. ccxt/{coinbasepro.py → coinbaseexchange.py} +233 -99
  219. ccxt/coinbaseinternational.py +428 -88
  220. ccxt/coincatch.py +5152 -0
  221. ccxt/coincheck.py +121 -38
  222. ccxt/coinex.py +4020 -3339
  223. ccxt/coinlist.py +273 -116
  224. ccxt/coinmate.py +204 -97
  225. ccxt/coinmetro.py +203 -110
  226. ccxt/coinone.py +142 -68
  227. ccxt/coinsph.py +206 -89
  228. ccxt/coinspot.py +137 -62
  229. ccxt/cryptocom.py +515 -185
  230. ccxt/currencycom.py +203 -85
  231. ccxt/defx.py +2065 -0
  232. ccxt/delta.py +404 -109
  233. ccxt/deribit.py +557 -323
  234. ccxt/digifinex.py +340 -223
  235. ccxt/ellipx.py +1826 -0
  236. ccxt/exmo.py +259 -128
  237. ccxt/gate.py +1472 -463
  238. ccxt/gemini.py +206 -84
  239. ccxt/hashkey.py +4164 -0
  240. ccxt/hitbtc.py +334 -178
  241. ccxt/hollaex.py +134 -83
  242. ccxt/htx.py +1095 -563
  243. ccxt/huobijp.py +105 -56
  244. ccxt/hyperliquid.py +1632 -268
  245. ccxt/idex.py +148 -95
  246. ccxt/independentreserve.py +235 -31
  247. ccxt/indodax.py +165 -62
  248. ccxt/kraken.py +871 -354
  249. ccxt/krakenfutures.py +324 -100
  250. ccxt/kucoin.py +917 -357
  251. ccxt/kucoinfutures.py +1004 -149
  252. ccxt/kuna.py +138 -106
  253. ccxt/latoken.py +135 -79
  254. ccxt/lbank.py +290 -113
  255. ccxt/luno.py +112 -62
  256. ccxt/lykke.py +104 -55
  257. ccxt/mercado.py +36 -29
  258. ccxt/mexc.py +994 -429
  259. ccxt/myokx.py +43 -0
  260. ccxt/ndax.py +163 -82
  261. ccxt/novadax.py +121 -75
  262. ccxt/oceanex.py +175 -59
  263. ccxt/okcoin.py +222 -163
  264. ccxt/okx.py +1776 -454
  265. ccxt/onetrading.py +132 -414
  266. ccxt/oxfun.py +2831 -0
  267. ccxt/p2b.py +79 -51
  268. ccxt/paradex.py +2017 -0
  269. ccxt/paymium.py +56 -32
  270. ccxt/phemex.py +572 -196
  271. ccxt/poloniex.py +218 -95
  272. ccxt/poloniexfutures.py +260 -92
  273. ccxt/pro/__init__.py +29 -5
  274. ccxt/pro/alpaca.py +32 -17
  275. ccxt/pro/ascendex.py +62 -14
  276. ccxt/pro/bequant.py +4 -0
  277. ccxt/pro/binance.py +1596 -329
  278. ccxt/pro/binancecoinm.py +1 -0
  279. ccxt/pro/binanceus.py +2 -9
  280. ccxt/pro/binanceusdm.py +2 -0
  281. ccxt/pro/bingx.py +527 -134
  282. ccxt/pro/bitcoincom.py +4 -1
  283. ccxt/pro/bitfinex.py +731 -266
  284. ccxt/pro/bitfinex1.py +635 -0
  285. ccxt/pro/bitget.py +726 -357
  286. ccxt/pro/bithumb.py +380 -0
  287. ccxt/pro/bitmart.py +138 -39
  288. ccxt/pro/bitmex.py +199 -40
  289. ccxt/pro/bitopro.py +25 -13
  290. ccxt/pro/bitrue.py +31 -32
  291. ccxt/pro/bitstamp.py +7 -6
  292. ccxt/pro/bitvavo.py +203 -81
  293. ccxt/pro/blockchaincom.py +30 -17
  294. ccxt/pro/blofin.py +692 -0
  295. ccxt/pro/bybit.py +791 -82
  296. ccxt/pro/cex.py +99 -51
  297. ccxt/pro/coinbase.py +220 -30
  298. ccxt/{async_support/hitbtc3.py → pro/coinbaseadvanced.py} +5 -5
  299. ccxt/pro/{coinbasepro.py → coinbaseexchange.py} +19 -19
  300. ccxt/pro/coinbaseinternational.py +193 -30
  301. ccxt/pro/coincatch.py +1464 -0
  302. ccxt/pro/coincheck.py +11 -6
  303. ccxt/pro/coinex.py +965 -665
  304. ccxt/pro/coinone.py +17 -10
  305. ccxt/pro/cryptocom.py +446 -66
  306. ccxt/pro/currencycom.py +11 -10
  307. ccxt/pro/defx.py +832 -0
  308. ccxt/pro/deribit.py +167 -31
  309. ccxt/pro/exmo.py +252 -20
  310. ccxt/pro/gate.py +729 -64
  311. ccxt/pro/gemini.py +44 -26
  312. ccxt/pro/hashkey.py +802 -0
  313. ccxt/pro/hitbtc.py +208 -103
  314. ccxt/pro/hollaex.py +25 -9
  315. ccxt/pro/htx.py +83 -39
  316. ccxt/pro/huobijp.py +17 -16
  317. ccxt/pro/hyperliquid.py +502 -31
  318. ccxt/pro/idex.py +28 -13
  319. ccxt/pro/independentreserve.py +21 -16
  320. ccxt/pro/kraken.py +298 -51
  321. ccxt/pro/krakenfutures.py +166 -75
  322. ccxt/pro/kucoin.py +395 -77
  323. ccxt/pro/kucoinfutures.py +400 -99
  324. ccxt/pro/lbank.py +52 -31
  325. ccxt/pro/luno.py +12 -10
  326. ccxt/pro/mexc.py +400 -50
  327. ccxt/pro/myokx.py +28 -0
  328. ccxt/pro/ndax.py +25 -12
  329. ccxt/pro/okcoin.py +28 -9
  330. ccxt/pro/okx.py +935 -124
  331. ccxt/pro/onetrading.py +41 -24
  332. ccxt/pro/oxfun.py +1054 -0
  333. ccxt/pro/p2b.py +100 -24
  334. ccxt/pro/paradex.py +352 -0
  335. ccxt/pro/phemex.py +92 -33
  336. ccxt/pro/poloniex.py +128 -49
  337. ccxt/pro/poloniexfutures.py +53 -32
  338. ccxt/pro/probit.py +92 -85
  339. ccxt/pro/upbit.py +401 -8
  340. ccxt/pro/vertex.py +943 -0
  341. ccxt/pro/wazirx.py +46 -28
  342. ccxt/pro/whitebit.py +65 -12
  343. ccxt/pro/woo.py +437 -65
  344. ccxt/pro/woofipro.py +1271 -0
  345. ccxt/pro/xt.py +1067 -0
  346. ccxt/probit.py +143 -110
  347. ccxt/static_dependencies/__init__.py +1 -1
  348. ccxt/static_dependencies/lark/__init__.py +38 -0
  349. ccxt/static_dependencies/lark/__pyinstaller/__init__.py +6 -0
  350. ccxt/static_dependencies/lark/__pyinstaller/hook-lark.py +14 -0
  351. ccxt/static_dependencies/lark/ast_utils.py +59 -0
  352. ccxt/static_dependencies/lark/common.py +86 -0
  353. ccxt/static_dependencies/lark/exceptions.py +292 -0
  354. ccxt/static_dependencies/lark/grammar.py +130 -0
  355. ccxt/static_dependencies/lark/grammars/__init__.py +0 -0
  356. ccxt/static_dependencies/lark/indenter.py +143 -0
  357. ccxt/static_dependencies/lark/lark.py +658 -0
  358. ccxt/static_dependencies/lark/lexer.py +678 -0
  359. ccxt/static_dependencies/lark/load_grammar.py +1428 -0
  360. ccxt/static_dependencies/lark/parse_tree_builder.py +391 -0
  361. ccxt/static_dependencies/lark/parser_frontends.py +257 -0
  362. ccxt/static_dependencies/lark/parsers/__init__.py +0 -0
  363. ccxt/static_dependencies/lark/parsers/cyk.py +340 -0
  364. ccxt/static_dependencies/lark/parsers/earley.py +314 -0
  365. ccxt/static_dependencies/lark/parsers/earley_common.py +42 -0
  366. ccxt/static_dependencies/lark/parsers/earley_forest.py +801 -0
  367. ccxt/static_dependencies/lark/parsers/grammar_analysis.py +203 -0
  368. ccxt/static_dependencies/lark/parsers/lalr_analysis.py +332 -0
  369. ccxt/static_dependencies/lark/parsers/lalr_interactive_parser.py +158 -0
  370. ccxt/static_dependencies/lark/parsers/lalr_parser.py +122 -0
  371. ccxt/static_dependencies/lark/parsers/lalr_parser_state.py +110 -0
  372. ccxt/static_dependencies/lark/parsers/xearley.py +165 -0
  373. ccxt/static_dependencies/lark/py.typed +0 -0
  374. ccxt/static_dependencies/lark/reconstruct.py +107 -0
  375. ccxt/static_dependencies/lark/tools/__init__.py +70 -0
  376. ccxt/static_dependencies/lark/tools/nearley.py +202 -0
  377. ccxt/static_dependencies/lark/tools/serialize.py +32 -0
  378. ccxt/static_dependencies/lark/tools/standalone.py +196 -0
  379. ccxt/static_dependencies/lark/tree.py +267 -0
  380. ccxt/static_dependencies/lark/tree_matcher.py +186 -0
  381. ccxt/static_dependencies/lark/tree_templates.py +180 -0
  382. ccxt/static_dependencies/lark/utils.py +343 -0
  383. ccxt/static_dependencies/lark/visitors.py +596 -0
  384. ccxt/static_dependencies/marshmallow/__init__.py +81 -0
  385. ccxt/static_dependencies/marshmallow/base.py +65 -0
  386. ccxt/static_dependencies/marshmallow/class_registry.py +94 -0
  387. ccxt/static_dependencies/marshmallow/decorators.py +231 -0
  388. ccxt/static_dependencies/marshmallow/error_store.py +60 -0
  389. ccxt/static_dependencies/marshmallow/exceptions.py +71 -0
  390. ccxt/static_dependencies/marshmallow/fields.py +2114 -0
  391. ccxt/static_dependencies/marshmallow/orderedset.py +89 -0
  392. ccxt/static_dependencies/marshmallow/py.typed +0 -0
  393. ccxt/static_dependencies/marshmallow/schema.py +1228 -0
  394. ccxt/static_dependencies/marshmallow/types.py +12 -0
  395. ccxt/static_dependencies/marshmallow/utils.py +378 -0
  396. ccxt/static_dependencies/marshmallow/validate.py +678 -0
  397. ccxt/static_dependencies/marshmallow/warnings.py +2 -0
  398. ccxt/static_dependencies/marshmallow_dataclass/__init__.py +1047 -0
  399. ccxt/static_dependencies/marshmallow_dataclass/collection_field.py +51 -0
  400. ccxt/static_dependencies/marshmallow_dataclass/lazy_class_attribute.py +45 -0
  401. ccxt/static_dependencies/marshmallow_dataclass/mypy.py +71 -0
  402. ccxt/static_dependencies/marshmallow_dataclass/py.typed +0 -0
  403. ccxt/static_dependencies/marshmallow_dataclass/typing.py +14 -0
  404. ccxt/static_dependencies/marshmallow_dataclass/union_field.py +82 -0
  405. ccxt/static_dependencies/marshmallow_oneofschema/__init__.py +1 -0
  406. ccxt/static_dependencies/marshmallow_oneofschema/one_of_schema.py +193 -0
  407. ccxt/static_dependencies/marshmallow_oneofschema/py.typed +0 -0
  408. ccxt/static_dependencies/starknet/__init__.py +0 -0
  409. ccxt/static_dependencies/starknet/cairo/__init__.py +0 -0
  410. ccxt/static_dependencies/starknet/cairo/data_types.py +123 -0
  411. ccxt/static_dependencies/starknet/cairo/deprecated_parse/__init__.py +0 -0
  412. ccxt/static_dependencies/starknet/cairo/deprecated_parse/cairo_types.py +77 -0
  413. ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser.py +46 -0
  414. ccxt/static_dependencies/starknet/cairo/deprecated_parse/parser_transformer.py +138 -0
  415. ccxt/static_dependencies/starknet/cairo/felt.py +64 -0
  416. ccxt/static_dependencies/starknet/cairo/type_parser.py +121 -0
  417. ccxt/static_dependencies/starknet/cairo/v1/__init__.py +0 -0
  418. ccxt/static_dependencies/starknet/cairo/v1/type_parser.py +59 -0
  419. ccxt/static_dependencies/starknet/cairo/v2/__init__.py +0 -0
  420. ccxt/static_dependencies/starknet/cairo/v2/type_parser.py +77 -0
  421. ccxt/static_dependencies/starknet/ccxt_utils.py +7 -0
  422. ccxt/static_dependencies/starknet/common.py +15 -0
  423. ccxt/static_dependencies/starknet/constants.py +39 -0
  424. ccxt/static_dependencies/starknet/hash/__init__.py +0 -0
  425. ccxt/static_dependencies/starknet/hash/address.py +79 -0
  426. ccxt/static_dependencies/starknet/hash/compiled_class_hash_objects.py +111 -0
  427. ccxt/static_dependencies/starknet/hash/selector.py +16 -0
  428. ccxt/static_dependencies/starknet/hash/storage.py +12 -0
  429. ccxt/static_dependencies/starknet/hash/utils.py +78 -0
  430. ccxt/static_dependencies/starknet/models/__init__.py +0 -0
  431. ccxt/static_dependencies/starknet/models/typed_data.py +45 -0
  432. ccxt/static_dependencies/starknet/serialization/__init__.py +24 -0
  433. ccxt/static_dependencies/starknet/serialization/_calldata_reader.py +40 -0
  434. ccxt/static_dependencies/starknet/serialization/_context.py +142 -0
  435. ccxt/static_dependencies/starknet/serialization/data_serializers/__init__.py +10 -0
  436. ccxt/static_dependencies/starknet/serialization/data_serializers/_common.py +82 -0
  437. ccxt/static_dependencies/starknet/serialization/data_serializers/array_serializer.py +43 -0
  438. ccxt/static_dependencies/starknet/serialization/data_serializers/bool_serializer.py +37 -0
  439. ccxt/static_dependencies/starknet/serialization/data_serializers/byte_array_serializer.py +66 -0
  440. ccxt/static_dependencies/starknet/serialization/data_serializers/cairo_data_serializer.py +71 -0
  441. ccxt/static_dependencies/starknet/serialization/data_serializers/enum_serializer.py +71 -0
  442. ccxt/static_dependencies/starknet/serialization/data_serializers/felt_serializer.py +50 -0
  443. ccxt/static_dependencies/starknet/serialization/data_serializers/named_tuple_serializer.py +58 -0
  444. ccxt/static_dependencies/starknet/serialization/data_serializers/option_serializer.py +43 -0
  445. ccxt/static_dependencies/starknet/serialization/data_serializers/output_serializer.py +40 -0
  446. ccxt/static_dependencies/starknet/serialization/data_serializers/payload_serializer.py +72 -0
  447. ccxt/static_dependencies/starknet/serialization/data_serializers/struct_serializer.py +36 -0
  448. ccxt/static_dependencies/starknet/serialization/data_serializers/tuple_serializer.py +36 -0
  449. ccxt/static_dependencies/starknet/serialization/data_serializers/uint256_serializer.py +76 -0
  450. ccxt/static_dependencies/starknet/serialization/data_serializers/uint_serializer.py +100 -0
  451. ccxt/static_dependencies/starknet/serialization/data_serializers/unit_serializer.py +32 -0
  452. ccxt/static_dependencies/starknet/serialization/errors.py +10 -0
  453. ccxt/static_dependencies/starknet/serialization/factory.py +229 -0
  454. ccxt/static_dependencies/starknet/serialization/function_serialization_adapter.py +110 -0
  455. ccxt/static_dependencies/starknet/serialization/tuple_dataclass.py +59 -0
  456. ccxt/static_dependencies/starknet/utils/__init__.py +0 -0
  457. ccxt/static_dependencies/starknet/utils/constructor_args_translator.py +86 -0
  458. ccxt/static_dependencies/starknet/utils/iterable.py +13 -0
  459. ccxt/static_dependencies/starknet/utils/schema.py +13 -0
  460. ccxt/static_dependencies/starknet/utils/typed_data.py +182 -0
  461. ccxt/static_dependencies/starkware/__init__.py +0 -0
  462. ccxt/static_dependencies/starkware/crypto/__init__.py +0 -0
  463. ccxt/static_dependencies/starkware/crypto/fast_pedersen_hash.py +50 -0
  464. ccxt/static_dependencies/starkware/crypto/math_utils.py +78 -0
  465. ccxt/static_dependencies/starkware/crypto/signature.py +2344 -0
  466. ccxt/static_dependencies/starkware/crypto/utils.py +63 -0
  467. ccxt/static_dependencies/sympy/__init__.py +0 -0
  468. ccxt/static_dependencies/sympy/core/__init__.py +0 -0
  469. ccxt/static_dependencies/sympy/core/intfunc.py +35 -0
  470. ccxt/static_dependencies/sympy/external/__init__.py +0 -0
  471. ccxt/static_dependencies/sympy/external/gmpy.py +345 -0
  472. ccxt/static_dependencies/sympy/external/importtools.py +187 -0
  473. ccxt/static_dependencies/sympy/external/ntheory.py +637 -0
  474. ccxt/static_dependencies/sympy/external/pythonmpq.py +341 -0
  475. ccxt/static_dependencies/typing_inspect/__init__.py +0 -0
  476. ccxt/static_dependencies/typing_inspect/typing_inspect.py +851 -0
  477. ccxt/test/{test_async.py → tests_async.py} +456 -391
  478. ccxt/test/tests_helpers.py +285 -0
  479. ccxt/test/tests_init.py +39 -0
  480. ccxt/test/{test_sync.py → tests_sync.py} +456 -393
  481. ccxt/timex.py +123 -70
  482. ccxt/tokocrypto.py +129 -93
  483. ccxt/tradeogre.py +39 -25
  484. ccxt/upbit.py +322 -113
  485. ccxt/vertex.py +2983 -0
  486. ccxt/wavesexchange.py +227 -173
  487. ccxt/wazirx.py +145 -65
  488. ccxt/whitebit.py +533 -138
  489. ccxt/woo.py +1137 -296
  490. ccxt/woofipro.py +2716 -0
  491. ccxt/xt.py +4627 -0
  492. ccxt/yobit.py +159 -92
  493. ccxt/zaif.py +80 -33
  494. ccxt/zonda.py +140 -69
  495. ccxt-4.4.48.dist-info/LICENSE.txt +21 -0
  496. ccxt-4.4.48.dist-info/METADATA +646 -0
  497. ccxt-4.4.48.dist-info/RECORD +669 -0
  498. {ccxt-4.2.77.dist-info → ccxt-4.4.48.dist-info}/WHEEL +1 -1
  499. ccxt/abstract/bitbay.py +0 -47
  500. ccxt/abstract/bitfinex2.py +0 -139
  501. ccxt/abstract/hitbtc3.py +0 -115
  502. ccxt/async_support/bitbay.py +0 -17
  503. ccxt/async_support/bitfinex2.py +0 -3496
  504. ccxt/async_support/flowbtc.py +0 -34
  505. ccxt/bitbay.py +0 -17
  506. ccxt/bitfinex2.py +0 -3496
  507. ccxt/flowbtc.py +0 -34
  508. ccxt/hitbtc3.py +0 -16
  509. ccxt/pro/bitfinex2.py +0 -1081
  510. ccxt/test/base/__init__.py +0 -28
  511. ccxt/test/base/test_account.py +0 -26
  512. ccxt/test/base/test_balance.py +0 -56
  513. ccxt/test/base/test_borrow_interest.py +0 -35
  514. ccxt/test/base/test_borrow_rate.py +0 -32
  515. ccxt/test/base/test_calculate_fee.py +0 -51
  516. ccxt/test/base/test_crypto.py +0 -127
  517. ccxt/test/base/test_currency.py +0 -76
  518. ccxt/test/base/test_datetime.py +0 -103
  519. ccxt/test/base/test_decimal_to_precision.py +0 -392
  520. ccxt/test/base/test_deep_extend.py +0 -68
  521. ccxt/test/base/test_deposit_withdrawal.py +0 -50
  522. ccxt/test/base/test_exchange_datetime_functions.py +0 -76
  523. ccxt/test/base/test_funding_rate_history.py +0 -29
  524. ccxt/test/base/test_last_price.py +0 -32
  525. ccxt/test/base/test_ledger_entry.py +0 -45
  526. ccxt/test/base/test_ledger_item.py +0 -48
  527. ccxt/test/base/test_leverage_tier.py +0 -33
  528. ccxt/test/base/test_margin_mode.py +0 -24
  529. ccxt/test/base/test_margin_modification.py +0 -35
  530. ccxt/test/base/test_market.py +0 -190
  531. ccxt/test/base/test_number.py +0 -411
  532. ccxt/test/base/test_ohlcv.py +0 -32
  533. ccxt/test/base/test_open_interest.py +0 -32
  534. ccxt/test/base/test_order.py +0 -64
  535. ccxt/test/base/test_order_book.py +0 -63
  536. ccxt/test/base/test_position.py +0 -60
  537. ccxt/test/base/test_shared_methods.py +0 -345
  538. ccxt/test/base/test_status.py +0 -24
  539. ccxt/test/base/test_throttle.py +0 -126
  540. ccxt/test/base/test_ticker.py +0 -86
  541. ccxt/test/base/test_trade.py +0 -47
  542. ccxt/test/base/test_trading_fee.py +0 -26
  543. ccxt/test/base/test_transaction.py +0 -39
  544. ccxt-4.2.77.dist-info/METADATA +0 -626
  545. ccxt-4.2.77.dist-info/RECORD +0 -534
  546. {ccxt-4.2.77.dist-info → ccxt-4.4.48.dist-info}/top_level.txt +0 -0
ccxt/async_support/cex.py CHANGED
@@ -5,21 +5,17 @@
5
5
 
6
6
  from ccxt.async_support.base.exchange import Exchange
7
7
  from ccxt.abstract.cex import ImplicitAPI
8
+ import asyncio
8
9
  import hashlib
9
- import json
10
- from ccxt.base.types import Balances, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade
10
+ from ccxt.base.types import Account, Balances, Currencies, Currency, DepositAddress, Int, LedgerEntry, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFeeInterface, TradingFees, Transaction, TransferEntry
11
11
  from typing import List
12
12
  from ccxt.base.errors import ExchangeError
13
+ from ccxt.base.errors import AuthenticationError
14
+ from ccxt.base.errors import PermissionDenied
13
15
  from ccxt.base.errors import ArgumentsRequired
14
- from ccxt.base.errors import BadSymbol
15
- from ccxt.base.errors import NullResponse
16
+ from ccxt.base.errors import BadRequest
16
17
  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 DDoSProtection
20
- from ccxt.base.errors import RateLimitExceeded
21
- from ccxt.base.errors import InvalidNonce
22
- from ccxt.base.errors import AuthenticationError
18
+ from ccxt.base.errors import NullResponse
23
19
  from ccxt.base.decimal_to_precision import TICK_SIZE
24
20
  from ccxt.base.precise import Precise
25
21
 
@@ -31,1569 +27,1653 @@ class cex(Exchange, ImplicitAPI):
31
27
  'id': 'cex',
32
28
  'name': 'CEX.IO',
33
29
  'countries': ['GB', 'EU', 'CY', 'RU'],
34
- 'rateLimit': 1500,
30
+ 'rateLimit': 300, # 200 req/min
35
31
  'pro': True,
36
32
  'has': {
37
33
  'CORS': None,
38
34
  'spot': True,
39
- 'margin': False, # has but not through api
35
+ 'margin': False, # has, but not through api
40
36
  'swap': False,
41
37
  'future': False,
42
38
  'option': False,
43
- 'addMargin': False,
44
39
  'cancelAllOrders': True,
45
40
  'cancelOrder': True,
46
- 'cancelOrders': False,
47
- 'createDepositAddress': False,
48
- 'createMarketBuyOrderWithCost': True,
49
- 'createMarketOrderWithCost': False,
50
- 'createMarketSellOrderWithCost': False,
51
41
  'createOrder': True,
52
- 'createStopLimitOrder': False,
53
- 'createStopMarketOrder': False,
54
- 'createStopOrder': False,
55
- 'editOrder': True,
42
+ 'createStopOrder': True,
43
+ 'createTriggerOrder': True,
44
+ 'fetchAccounts': True,
56
45
  'fetchBalance': True,
46
+ 'fetchClosedOrder': True,
57
47
  'fetchClosedOrders': True,
58
48
  'fetchCurrencies': True,
59
- 'fetchDeposit': False,
60
49
  'fetchDepositAddress': True,
61
- 'fetchDepositAddresses': False,
62
- 'fetchDeposits': False,
63
- 'fetchDepositsWithdrawals': False,
50
+ 'fetchDepositsWithdrawals': True,
64
51
  'fetchFundingHistory': False,
65
52
  'fetchFundingRate': False,
66
53
  'fetchFundingRateHistory': False,
67
54
  'fetchFundingRates': False,
68
- 'fetchIndexOHLCV': False,
69
- 'fetchMarginMode': False,
55
+ 'fetchLedger': True,
70
56
  'fetchMarkets': True,
71
- 'fetchMarkOHLCV': False,
72
57
  'fetchOHLCV': True,
73
- 'fetchOpenInterestHistory': False,
58
+ 'fetchOpenOrder': True,
74
59
  'fetchOpenOrders': True,
75
- 'fetchOrder': True,
76
60
  'fetchOrderBook': True,
77
- 'fetchOrders': True,
78
- 'fetchPositionMode': False,
79
- 'fetchPremiumIndexOHLCV': False,
80
61
  'fetchTicker': True,
81
62
  'fetchTickers': True,
63
+ 'fetchTime': True,
82
64
  'fetchTrades': True,
83
- 'fetchTradingFee': False,
84
65
  'fetchTradingFees': True,
85
- 'fetchTransactions': False,
86
- 'fetchTransfer': False,
87
- 'fetchTransfers': False,
88
- 'fetchWithdrawal': False,
89
- 'fetchWithdrawals': False,
90
- 'fetchWithdrawalWhitelist': False,
91
- 'reduceMargin': False,
92
- 'setLeverage': False,
93
- 'setMargin': False,
94
- 'setMarginMode': False,
95
- 'transfer': False,
96
- 'withdraw': False,
97
- },
98
- 'timeframes': {
99
- '1m': '1m',
100
- '1h': '1h',
101
- '1d': '1d',
66
+ 'transfer': True,
102
67
  },
103
68
  'urls': {
104
69
  'logo': 'https://user-images.githubusercontent.com/1294454/27766442-8ddc33b0-5ed8-11e7-8b98-f786aef0f3c9.jpg',
105
70
  'api': {
106
- 'rest': 'https://cex.io/api',
71
+ 'public': 'https://trade.cex.io/api/spot/rest-public',
72
+ 'private': 'https://trade.cex.io/api/spot/rest',
107
73
  },
108
74
  'www': 'https://cex.io',
109
- 'doc': 'https://cex.io/cex-api',
75
+ 'doc': 'https://trade.cex.io/docs/',
110
76
  'fees': [
111
77
  'https://cex.io/fee-schedule',
112
78
  'https://cex.io/limits-commissions',
113
79
  ],
114
80
  'referral': 'https://cex.io/r/0/up105393824/0/',
115
81
  },
116
- 'requiredCredentials': {
117
- 'apiKey': True,
118
- 'secret': True,
119
- 'uid': True,
120
- },
121
82
  'api': {
122
83
  'public': {
123
- 'get': [
124
- 'currency_profile',
125
- 'currency_limits/',
126
- 'last_price/{pair}/',
127
- 'last_prices/{currencies}/',
128
- 'ohlcv/hd/{yyyymmdd}/{pair}',
129
- 'order_book/{pair}/',
130
- 'ticker/{pair}/',
131
- 'tickers/{currencies}/',
132
- 'trade_history/{pair}/',
133
- ],
134
- 'post': [
135
- 'convert/{pair}',
136
- 'price_stats/{pair}',
137
- ],
84
+ 'get': {},
85
+ 'post': {
86
+ 'get_server_time': 1,
87
+ 'get_pairs_info': 1,
88
+ 'get_currencies_info': 1,
89
+ 'get_processing_info': 10,
90
+ 'get_ticker': 1,
91
+ 'get_trade_history': 1,
92
+ 'get_order_book': 1,
93
+ 'get_candles': 1,
94
+ },
138
95
  },
139
96
  'private': {
140
- 'post': [
141
- 'active_orders_status/',
142
- 'archived_orders/{pair}/',
143
- 'balance/',
144
- 'cancel_order/',
145
- 'cancel_orders/{pair}/',
146
- 'cancel_replace_order/{pair}/',
147
- 'close_position/{pair}/',
148
- 'get_address/',
149
- 'get_crypto_address',
150
- 'get_myfee/',
151
- 'get_order/',
152
- 'get_order_tx/',
153
- 'open_orders/{pair}/',
154
- 'open_orders/',
155
- 'open_position/{pair}/',
156
- 'open_positions/{pair}/',
157
- 'place_order/{pair}/',
158
- 'raw_tx_history',
159
- ],
97
+ 'get': {},
98
+ 'post': {
99
+ 'get_my_current_fee': 5,
100
+ 'get_fee_strategy': 1,
101
+ 'get_my_volume': 5,
102
+ 'do_create_account': 1,
103
+ 'get_my_account_status_v3': 5,
104
+ 'get_my_wallet_balance': 5,
105
+ 'get_my_orders': 5,
106
+ 'do_my_new_order': 1,
107
+ 'do_cancel_my_order': 1,
108
+ 'do_cancel_all_orders': 5,
109
+ 'get_order_book': 1,
110
+ 'get_candles': 1,
111
+ 'get_trade_history': 1,
112
+ 'get_my_transaction_history': 1,
113
+ 'get_my_funding_history': 5,
114
+ 'do_my_internal_transfer': 1,
115
+ 'get_processing_info': 10,
116
+ 'get_deposit_address': 5,
117
+ 'do_deposit_funds_from_wallet': 1,
118
+ 'do_withdrawal_funds_to_wallet': 1,
119
+ },
160
120
  },
161
121
  },
162
- 'fees': {
163
- 'trading': {
164
- 'maker': self.parse_number('0.0016'),
165
- 'taker': self.parse_number('0.0025'),
166
- },
167
- 'funding': {
168
- 'withdraw': {},
169
- 'deposit': {
170
- # 'USD': amount => amount * 0.035 + 0.25,
171
- # 'EUR': amount => amount * 0.035 + 0.24,
172
- # 'RUB': amount => amount * 0.05 + 15.57,
173
- # 'GBP': amount => amount * 0.035 + 0.2,
174
- 'BTC': 0.0,
175
- 'ETH': 0.0,
176
- 'BCH': 0.0,
177
- 'DASH': 0.0,
178
- 'BTG': 0.0,
179
- 'ZEC': 0.0,
180
- 'XRP': 0.0,
181
- 'XLM': 0.0,
122
+ 'features': {
123
+ 'spot': {
124
+ 'sandbox': False,
125
+ 'createOrder': {
126
+ 'marginMode': False,
127
+ 'triggerPrice': True,
128
+ 'triggerPriceType': None,
129
+ 'triggerDirection': False,
130
+ 'stopLossPrice': False, # todo
131
+ 'takeProfitPrice': False, # todo
132
+ 'attachedStopLossTakeProfit': None,
133
+ 'timeInForce': {
134
+ 'IOC': True,
135
+ 'FOK': True,
136
+ 'PO': False, # todo check
137
+ 'GTD': True,
138
+ },
139
+ 'hedged': False,
140
+ 'leverage': False,
141
+ 'marketBuyRequiresPrice': False,
142
+ 'marketBuyByCost': True, # todo check
143
+ 'selfTradePrevention': False,
144
+ 'trailing': False,
145
+ 'iceberg': False,
146
+ },
147
+ 'createOrders': None,
148
+ 'fetchMyTrades': None,
149
+ 'fetchOrder': None,
150
+ 'fetchOpenOrders': {
151
+ 'marginMode': False,
152
+ 'limit': 1000,
153
+ 'trigger': False,
154
+ 'trailing': False,
182
155
  },
156
+ 'fetchOrders': None,
157
+ 'fetchClosedOrders': {
158
+ 'marginMode': False,
159
+ 'limit': 1000,
160
+ 'daysBack': 100000,
161
+ 'daysBackCanceled': 1,
162
+ 'untilDays': 100000,
163
+ 'trigger': False,
164
+ 'trailing': False,
165
+ },
166
+ 'fetchOHLCV': {
167
+ 'limit': 1000,
168
+ },
169
+ },
170
+ 'swap': {
171
+ 'linear': None,
172
+ 'inverse': None,
173
+ },
174
+ 'future': {
175
+ 'linear': None,
176
+ 'inverse': None,
183
177
  },
184
178
  },
185
179
  'precisionMode': TICK_SIZE,
186
180
  'exceptions': {
187
181
  'exact': {},
188
182
  'broad': {
183
+ 'You have negative balance on following accounts': InsufficientFunds,
184
+ 'Mandatory parameter side should be one of BUY,SELL': BadRequest,
185
+ 'API orders from Main account are not allowed': BadRequest,
186
+ 'check failed': BadRequest,
189
187
  'Insufficient funds': InsufficientFunds,
190
- 'Nonce must be incremented': InvalidNonce,
191
- 'Invalid Order': InvalidOrder,
192
- 'Order not found': OrderNotFound,
193
- 'limit exceeded': RateLimitExceeded, # {"error":"rate limit exceeded"}
194
- 'Invalid API key': AuthenticationError,
195
- 'There was an error while placing your order': InvalidOrder,
196
- 'Sorry, too many clients already': DDoSProtection,
197
- 'Invalid Symbols Pair': BadSymbol,
198
- 'Wrong currency pair': BadSymbol, # {"error":"There was an error while placing your order: Wrong currency pair.","safe":true}
188
+ 'Get deposit address for main account is not allowed': PermissionDenied,
189
+ 'Market Trigger orders are not allowed': BadRequest, # for some reason, triggerPrice does not work for market orders
190
+ 'key not passed or incorrect': AuthenticationError,
199
191
  },
200
192
  },
193
+ 'timeframes': {
194
+ '1m': '1m',
195
+ '5m': '5m',
196
+ '15m': '15m',
197
+ '30m': '30m',
198
+ '1h': '1h',
199
+ '2h': '2h',
200
+ '4h': '4h',
201
+ '1d': '1d',
202
+ },
201
203
  'options': {
202
- 'fetchOHLCVWarning': True,
203
- 'createMarketBuyOrderRequiresPrice': True,
204
- 'order': {
205
- 'status': {
206
- 'c': 'canceled',
207
- 'd': 'closed',
208
- 'cd': 'canceled',
209
- 'a': 'open',
210
- },
211
- },
212
- 'defaultNetwork': 'ERC20',
213
- 'defaultNetworks': {
214
- 'USDT': 'TRC20',
215
- },
216
204
  'networks': {
217
- 'ERC20': 'Ethereum',
218
- 'BTC': 'BTC',
219
- 'BEP20': 'Binance Smart Chain',
220
- 'TRC20': 'Tron',
205
+ 'BTC': 'bitcoin',
206
+ 'ERC20': 'ERC20',
207
+ 'BSC20': 'binancesmartchain',
208
+ 'DOGE': 'dogecoin',
209
+ 'ALGO': 'algorand',
210
+ 'XLM': 'stellar',
211
+ 'ATOM': 'cosmos',
212
+ 'LTC': 'litecoin',
213
+ 'XRP': 'ripple',
214
+ 'FTM': 'fantom',
215
+ 'MINA': 'mina',
216
+ 'THETA': 'theta',
217
+ 'XTZ': 'tezos',
218
+ 'TIA': 'celestia',
219
+ 'CRONOS': 'cronos', # CRC20
220
+ 'MATIC': 'polygon',
221
+ 'TON': 'ton',
222
+ 'TRC20': 'tron',
223
+ 'SOLANA': 'solana',
224
+ 'SGB': 'songbird',
225
+ 'DYDX': 'dydx',
226
+ 'DASH': 'dash',
227
+ 'ZIL': 'zilliqa',
228
+ 'EOS': 'eos',
229
+ 'AVALANCHEC': 'avalanche',
230
+ 'ETHPOW': 'ethereumpow',
231
+ 'NEAR': 'near',
232
+ 'ARB': 'arbitrum',
233
+ 'DOT': 'polkadot',
234
+ 'OPT': 'optimism',
235
+ 'INJ': 'injective',
236
+ 'ADA': 'cardano',
237
+ 'ONT': 'ontology',
238
+ 'ICP': 'icp',
239
+ 'KAVA': 'kava',
240
+ 'KSM': 'kusama',
241
+ 'SEI': 'sei',
242
+ # 'OSM': 'osmosis',
243
+ 'NEO': 'neo',
244
+ 'NEO3': 'neo3',
245
+ # 'TERRAOLD': 'terra', # tbd
246
+ # 'TERRA': 'terra2', # tbd
247
+ # 'EVER': 'everscale', # tbd
248
+ 'XDC': 'xdc',
221
249
  },
222
250
  },
223
251
  })
224
252
 
225
- async def fetch_currencies_from_cache(self, params={}):
226
- # self method is now redundant
227
- # currencies are now fetched before markets
228
- options = self.safe_value(self.options, 'fetchCurrencies', {})
229
- timestamp = self.safe_integer(options, 'timestamp')
230
- expires = self.safe_integer(options, 'expires', 1000)
231
- now = self.milliseconds()
232
- if (timestamp is None) or ((now - timestamp) > expires):
233
- response = await self.publicGetCurrencyProfile(params)
234
- self.options['fetchCurrencies'] = self.extend(options, {
235
- 'response': response,
236
- 'timestamp': now,
237
- })
238
- return self.safe_value(self.options['fetchCurrencies'], 'response')
239
-
240
- async def fetch_currencies(self, params={}):
253
+ async def fetch_currencies(self, params={}) -> Currencies:
241
254
  """
242
255
  fetches all available currencies on an exchange
256
+
257
+ https://trade.cex.io/docs/#rest-public-api-calls-currencies-info
258
+
243
259
  :param dict [params]: extra parameters specific to the exchange API endpoint
244
260
  :returns dict: an associative dictionary of currencies
245
261
  """
246
- response = await self.fetch_currencies_from_cache(params)
247
- self.options['currencies'] = {
248
- 'timestamp': self.milliseconds(),
249
- 'response': response,
250
- }
262
+ promises = []
263
+ promises.append(self.publicPostGetCurrenciesInfo(params))
251
264
  #
252
- # {
253
- # "e":"currency_profile",
254
- # "ok":"ok",
255
- # "data":{
256
- # "symbols":[
257
- # {
258
- # "code":"GHS",
259
- # "contract":true,
260
- # "commodity":true,
261
- # "fiat":false,
262
- # "description":"CEX.IO doesn't provide cloud mining services anymore.",
263
- # "precision":8,
264
- # "scale":0,
265
- # "minimumCurrencyAmount":"0.00000001",
266
- # "minimalWithdrawalAmount":-1
267
- # },
268
- # {
269
- # "code":"BTC",
270
- # "contract":false,
271
- # "commodity":false,
272
- # "fiat":false,
273
- # "description":"",
274
- # "precision":8,
275
- # "scale":0,
276
- # "minimumCurrencyAmount":"0.00000001",
277
- # "minimalWithdrawalAmount":0.002
278
- # },
279
- # {
280
- # "code":"ETH",
281
- # "contract":false,
282
- # "commodity":false,
283
- # "fiat":false,
284
- # "description":"",
285
- # "precision":8,
286
- # "scale":2,
287
- # "minimumCurrencyAmount":"0.00000100",
288
- # "minimalWithdrawalAmount":0.01
289
- # }
290
- # ],
291
- # "pairs":[
292
- # {
293
- # "symbol1":"BTC",
294
- # "symbol2":"USD",
295
- # "pricePrecision":1,
296
- # "priceScale":"/1000000",
297
- # "minLotSize":0.002,
298
- # "minLotSizeS2":20
299
- # },
300
- # {
301
- # "symbol1":"ETH",
302
- # "symbol2":"USD",
303
- # "pricePrecision":2,
304
- # "priceScale":"/10000",
305
- # "minLotSize":0.1,
306
- # "minLotSizeS2":20
307
- # }
308
- # ]
309
- # }
310
- # }
265
+ # {
266
+ # "ok": "ok",
267
+ # "data": [
268
+ # {
269
+ # "currency": "ZAP",
270
+ # "fiat": False,
271
+ # "precision": "8",
272
+ # "walletPrecision": "6",
273
+ # "walletDeposit": True,
274
+ # "walletWithdrawal": True
275
+ # },
276
+ # ...
277
+ #
278
+ promises.append(self.publicPostGetProcessingInfo(params))
279
+ #
280
+ # {
281
+ # "ok": "ok",
282
+ # "data": {
283
+ # "ADA": {
284
+ # "name": "Cardano",
285
+ # "blockchains": {
286
+ # "cardano": {
287
+ # "type": "coin",
288
+ # "deposit": "enabled",
289
+ # "minDeposit": "1",
290
+ # "withdrawal": "enabled",
291
+ # "minWithdrawal": "5",
292
+ # "withdrawalFee": "1",
293
+ # "withdrawalFeePercent": "0",
294
+ # "depositConfirmations": "15"
295
+ # }
296
+ # }
297
+ # },
298
+ # ...
311
299
  #
312
- data = self.safe_value(response, 'data', [])
313
- currencies = self.safe_value(data, 'symbols', [])
314
- result = {}
315
- for i in range(0, len(currencies)):
316
- currency = currencies[i]
317
- id = self.safe_string(currency, 'code')
318
- code = self.safe_currency_code(id)
319
- active = True
320
- result[code] = {
321
- 'id': id,
322
- 'code': code,
323
- 'name': id,
324
- 'active': active,
325
- 'deposit': None,
326
- 'withdraw': None,
327
- 'precision': self.parse_number(self.parse_precision(self.safe_string(currency, 'precision'))),
328
- 'fee': None,
300
+ responses = await asyncio.gather(*promises)
301
+ dataCurrencies = self.safe_list(responses[0], 'data', [])
302
+ dataNetworks = self.safe_dict(responses[1], 'data', {})
303
+ currenciesIndexed = self.index_by(dataCurrencies, 'currency')
304
+ data = self.deep_extend(currenciesIndexed, dataNetworks)
305
+ return self.parse_currencies(self.to_array(data))
306
+
307
+ def parse_currency(self, rawCurrency: dict) -> Currency:
308
+ id = self.safe_string(rawCurrency, 'currency')
309
+ code = self.safe_currency_code(id)
310
+ type = 'fiat' if self.safe_bool(rawCurrency, 'fiat') else 'crypto'
311
+ currencyDepositEnabled = self.safe_bool(rawCurrency, 'walletDeposit')
312
+ currencyWithdrawEnabled = self.safe_bool(rawCurrency, 'walletWithdrawal')
313
+ currencyPrecision = self.parse_number(self.parse_precision(self.safe_string(rawCurrency, 'precision')))
314
+ networks: dict = {}
315
+ rawNetworks = self.safe_dict(rawCurrency, 'blockchains', {})
316
+ keys = list(rawNetworks.keys())
317
+ for j in range(0, len(keys)):
318
+ networkId = keys[j]
319
+ rawNetwork = rawNetworks[networkId]
320
+ networkCode = self.network_id_to_code(networkId)
321
+ deposit = self.safe_string(rawNetwork, 'deposit') == 'enabled'
322
+ withdraw = self.safe_string(rawNetwork, 'withdrawal') == 'enabled'
323
+ networks[networkCode] = {
324
+ 'id': networkId,
325
+ 'network': networkCode,
326
+ 'margin': None,
327
+ 'deposit': deposit,
328
+ 'withdraw': withdraw,
329
+ 'fee': self.safe_number(rawNetwork, 'withdrawalFee'),
330
+ 'precision': currencyPrecision,
329
331
  'limits': {
330
- 'amount': {
331
- 'min': self.safe_number(currency, 'minimumCurrencyAmount'),
332
+ 'deposit': {
333
+ 'min': self.safe_number(rawNetwork, 'minDeposit'),
332
334
  'max': None,
333
335
  },
334
336
  'withdraw': {
335
- 'min': self.safe_number(currency, 'minimalWithdrawalAmount'),
337
+ 'min': self.safe_number(rawNetwork, 'minWithdrawal'),
336
338
  'max': None,
337
339
  },
338
340
  },
339
- 'info': currency,
341
+ 'info': rawNetwork,
340
342
  }
341
- return result
343
+ return self.safe_currency_structure({
344
+ 'id': id,
345
+ 'code': code,
346
+ 'name': None,
347
+ 'type': type,
348
+ 'active': None,
349
+ 'deposit': currencyDepositEnabled,
350
+ 'withdraw': currencyWithdrawEnabled,
351
+ 'fee': None,
352
+ 'precision': currencyPrecision,
353
+ 'limits': {
354
+ 'amount': {
355
+ 'min': None,
356
+ 'max': None,
357
+ },
358
+ 'withdraw': {
359
+ 'min': None,
360
+ 'max': None,
361
+ },
362
+ },
363
+ 'networks': networks,
364
+ 'info': rawCurrency,
365
+ })
342
366
 
343
- async def fetch_markets(self, params={}):
367
+ async def fetch_markets(self, params={}) -> List[Market]:
344
368
  """
345
- retrieves data on all markets for cex
369
+ retrieves data on all markets for ace
370
+
371
+ https://trade.cex.io/docs/#rest-public-api-calls-pairs-info
372
+
346
373
  :param dict [params]: extra parameters specific to the exchange API endpoint
347
374
  :returns dict[]: an array of objects representing market data
348
375
  """
349
- currenciesResponse = await self.fetch_currencies_from_cache(params)
350
- currenciesData = self.safe_value(currenciesResponse, 'data', {})
351
- currencies = self.safe_value(currenciesData, 'symbols', [])
352
- currenciesById = self.index_by(currencies, 'code')
353
- pairs = self.safe_value(currenciesData, 'pairs', [])
354
- response = await self.publicGetCurrencyLimits(params)
376
+ response = await self.publicPostGetPairsInfo(params)
355
377
  #
356
- # {
357
- # "e":"currency_limits",
358
- # "ok":"ok",
359
- # "data": {
360
- # "pairs":[
361
- # {
362
- # "symbol1":"BTC",
363
- # "symbol2":"USD",
364
- # "minLotSize":0.002,
365
- # "minLotSizeS2":20,
366
- # "maxLotSize":30,
367
- # "minPrice":"1500",
368
- # "maxPrice":"35000"
369
- # },
370
- # {
371
- # "symbol1":"BCH",
372
- # "symbol2":"EUR",
373
- # "minLotSize":0.1,
374
- # "minLotSizeS2":20,
375
- # "maxLotSize":null,
376
- # "minPrice":"25",
377
- # "maxPrice":"8192"
378
- # }
379
- # ]
380
- # }
381
- # }
378
+ # {
379
+ # "ok": "ok",
380
+ # "data": [
381
+ # {
382
+ # "base": "AI",
383
+ # "quote": "USD",
384
+ # "baseMin": "30",
385
+ # "baseMax": "2516000",
386
+ # "baseLotSize": "0.000001",
387
+ # "quoteMin": "10",
388
+ # "quoteMax": "1000000",
389
+ # "quoteLotSize": "0.01000000",
390
+ # "basePrecision": "6",
391
+ # "quotePrecision": "8",
392
+ # "pricePrecision": "4",
393
+ # "minPrice": "0.0377",
394
+ # "maxPrice": "19.5000"
395
+ # },
396
+ # ...
382
397
  #
383
- result = []
384
- markets = self.safe_value(response['data'], 'pairs')
385
- for i in range(0, len(markets)):
386
- market = markets[i]
387
- baseId = self.safe_string(market, 'symbol1')
388
- quoteId = self.safe_string(market, 'symbol2')
389
- base = self.safe_currency_code(baseId)
390
- quote = self.safe_currency_code(quoteId)
391
- baseCurrency = self.safe_value(currenciesById, baseId, {})
392
- quoteCurrency = self.safe_value(currenciesById, quoteId, {})
393
- pricePrecisionString = self.safe_string(quoteCurrency, 'precision', '8')
394
- for j in range(0, len(pairs)):
395
- pair = pairs[j]
396
- if (pair['symbol1'] == baseId) and (pair['symbol2'] == quoteId):
397
- # we might need to account for `priceScale` here
398
- pricePrecisionString = self.safe_string(pair, 'pricePrecision', pricePrecisionString)
399
- baseCurrencyPrecision = self.safe_string(baseCurrency, 'precision', '8')
400
- baseCurrencyScale = self.safe_string(baseCurrency, 'scale', '0')
401
- amountPrecisionString = Precise.string_sub(baseCurrencyPrecision, baseCurrencyScale)
402
- result.append({
403
- 'id': baseId + '/' + quoteId,
404
- 'symbol': base + '/' + quote,
405
- 'base': base,
406
- 'quote': quote,
407
- 'settle': None,
408
- 'baseId': baseId,
409
- 'quoteId': quoteId,
410
- 'settleId': None,
411
- 'type': 'spot',
412
- 'spot': True,
413
- 'margin': None,
414
- 'swap': False,
415
- 'future': False,
416
- 'option': False,
417
- 'active': None,
418
- 'contract': False,
419
- 'linear': None,
420
- 'inverse': None,
421
- 'contractSize': None,
422
- 'expiry': None,
423
- 'expiryDatetime': None,
424
- 'strike': None,
425
- 'optionType': None,
426
- 'precision': {
427
- 'amount': self.parse_number(self.parse_precision(amountPrecisionString)),
428
- 'price': self.parse_number(self.parse_precision(pricePrecisionString)),
398
+ data = self.safe_list(response, 'data', [])
399
+ return self.parse_markets(data)
400
+
401
+ def parse_market(self, market: dict) -> Market:
402
+ baseId = self.safe_string(market, 'base')
403
+ base = self.safe_currency_code(baseId)
404
+ quoteId = self.safe_string(market, 'quote')
405
+ quote = self.safe_currency_code(quoteId)
406
+ id = base + '-' + quote # not actual id, but for self exchange we can use self abbreviation, because e.g. tickers have hyphen in between
407
+ symbol = base + '/' + quote
408
+ return self.safe_market_structure({
409
+ 'id': id,
410
+ 'symbol': symbol,
411
+ 'base': base,
412
+ 'baseId': baseId,
413
+ 'quote': quote,
414
+ 'quoteId': quoteId,
415
+ 'settle': None,
416
+ 'settleId': None,
417
+ 'type': 'spot',
418
+ 'spot': True,
419
+ 'margin': False,
420
+ 'swap': False,
421
+ 'future': False,
422
+ 'option': False,
423
+ 'contract': False,
424
+ 'linear': None,
425
+ 'inverse': None,
426
+ 'contractSize': None,
427
+ 'expiry': None,
428
+ 'expiryDatetime': None,
429
+ 'strike': None,
430
+ 'optionType': None,
431
+ 'limits': {
432
+ 'amount': {
433
+ 'min': self.safe_number(market, 'baseMin'),
434
+ 'max': self.safe_number(market, 'baseMax'),
429
435
  },
430
- 'limits': {
431
- 'leverage': {
432
- 'min': None,
433
- 'max': None,
434
- },
435
- 'amount': {
436
- 'min': self.safe_number(market, 'minLotSize'),
437
- 'max': self.safe_number(market, 'maxLotSize'),
438
- },
439
- 'price': {
440
- 'min': self.safe_number(market, 'minPrice'),
441
- 'max': self.safe_number(market, 'maxPrice'),
442
- },
443
- 'cost': {
444
- 'min': self.safe_number(market, 'minLotSizeS2'),
445
- 'max': None,
446
- },
436
+ 'price': {
437
+ 'min': self.safe_number(market, 'minPrice'),
438
+ 'max': self.safe_number(market, 'maxPrice'),
447
439
  },
448
- 'created': None,
449
- 'info': market,
450
- })
451
- return result
452
-
453
- def parse_balance(self, response) -> Balances:
454
- result = {'info': response}
455
- ommited = ['username', 'timestamp']
456
- balances = self.omit(response, ommited)
457
- currencyIds = list(balances.keys())
458
- for i in range(0, len(currencyIds)):
459
- currencyId = currencyIds[i]
460
- balance = self.safe_value(balances, currencyId, {})
461
- account = self.account()
462
- account['free'] = self.safe_string(balance, 'available')
463
- # https://github.com/ccxt/ccxt/issues/5484
464
- account['used'] = self.safe_string(balance, 'orders', '0')
465
- code = self.safe_currency_code(currencyId)
466
- result[code] = account
467
- return self.safe_balance(result)
440
+ 'cost': {
441
+ 'min': self.safe_number(market, 'quoteMin'),
442
+ 'max': self.safe_number(market, 'quoteMax'),
443
+ },
444
+ 'leverage': {
445
+ 'min': None,
446
+ 'max': None,
447
+ },
448
+ },
449
+ 'precision': {
450
+ 'amount': self.safe_string(market, 'baseLotSize'),
451
+ 'price': self.parse_number(self.parse_precision(self.safe_string(market, 'pricePrecision'))),
452
+ # 'cost': self.parse_number(self.parse_precision(self.safe_string(market, 'quoteLotSize'))), # buggy, doesn't reflect their documentation
453
+ 'base': self.parse_number(self.parse_precision(self.safe_string(market, 'basePrecision'))),
454
+ 'quote': self.parse_number(self.parse_precision(self.safe_string(market, 'quotePrecision'))),
455
+ },
456
+ 'active': None,
457
+ 'created': None,
458
+ 'info': market,
459
+ })
468
460
 
469
- async def fetch_balance(self, params={}) -> Balances:
461
+ async def fetch_time(self, params={}):
470
462
  """
471
- :see: https://docs.cex.io/#account-balance
472
- query for balance and get the amount of funds available for trading or funds locked in orders
463
+ fetches the current integer timestamp in milliseconds from the exchange server
473
464
  :param dict [params]: extra parameters specific to the exchange API endpoint
474
- :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
465
+ :returns int: the current integer timestamp in milliseconds from the exchange server
475
466
  """
476
- await self.load_markets()
477
- response = await self.privatePostBalance(params)
478
- return self.parse_balance(response)
467
+ response = await self.publicPostGetServerTime(params)
468
+ #
469
+ # {
470
+ # "ok": "ok",
471
+ # "data": {
472
+ # "timestamp": "1728472063472",
473
+ # "ISODate": "2024-10-09T11:07:43.472Z"
474
+ # }
475
+ # }
476
+ #
477
+ data = self.safe_dict(response, 'data')
478
+ timestamp = self.safe_integer(data, 'timestamp')
479
+ return timestamp
479
480
 
480
- async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
481
+ async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
481
482
  """
482
- :see: https://docs.cex.io/#orderbook
483
- fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
484
- :param str symbol: unified symbol of the market to fetch the order book for
485
- :param int [limit]: the maximum amount of order book entries to return
483
+ fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
484
+
485
+ https://trade.cex.io/docs/#rest-public-api-calls-ticker
486
+
487
+ :param str symbol:
486
488
  :param dict [params]: extra parameters specific to the exchange API endpoint
487
- :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
489
+ :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
488
490
  """
489
491
  await self.load_markets()
490
- market = self.market(symbol)
491
- request = {
492
- 'pair': market['id'],
493
- }
494
- if limit is not None:
495
- request['depth'] = limit
496
- response = await self.publicGetOrderBookPair(self.extend(request, params))
497
- timestamp = self.safe_timestamp(response, 'timestamp')
498
- return self.parse_order_book(response, market['symbol'], timestamp)
492
+ response = await self.fetch_tickers([symbol], params)
493
+ return self.safe_dict(response, symbol, {})
499
494
 
500
- def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
501
- #
502
- # [
503
- # 1591403940,
504
- # 0.024972,
505
- # 0.024972,
506
- # 0.024969,
507
- # 0.024969,
508
- # 0.49999900
509
- # ]
510
- #
511
- return [
512
- self.safe_timestamp(ohlcv, 0),
513
- self.safe_number(ohlcv, 1),
514
- self.safe_number(ohlcv, 2),
515
- self.safe_number(ohlcv, 3),
516
- self.safe_number(ohlcv, 4),
517
- self.safe_number(ohlcv, 5),
518
- ]
519
-
520
- async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
495
+ async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
521
496
  """
522
- :see: https://docs.cex.io/#historical-ohlcv-chart
523
- fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
524
- :param str symbol: unified symbol of the market to fetch OHLCV data for
525
- :param str timeframe: the length of time each candle represents
526
- :param int [since]: timestamp in ms of the earliest candle to fetch
527
- :param int [limit]: the maximum amount of candles to fetch
497
+ fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
498
+
499
+ https://trade.cex.io/docs/#rest-public-api-calls-ticker
500
+
501
+ :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
528
502
  :param dict [params]: extra parameters specific to the exchange API endpoint
529
- :returns int[][]: A list of candles ordered, open, high, low, close, volume
503
+ :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
530
504
  """
531
505
  await self.load_markets()
532
- market = self.market(symbol)
533
- if since is None:
534
- since = self.milliseconds() - 86400000 # yesterday
535
- else:
536
- if self.options['fetchOHLCVWarning']:
537
- raise ExchangeError(self.id + " fetchOHLCV warning: CEX can return historical candles for a certain date only, self might produce an empty or None reply. Set exchange.options['fetchOHLCVWarning'] = False or add({'options': {'fetchOHLCVWarning': False}}) to constructor params to suppress self warning message.")
538
- request = {
539
- 'pair': market['id'],
540
- 'yyyymmdd': self.yyyymmdd(since, ''),
541
- }
542
- try:
543
- response = await self.publicGetOhlcvHdYyyymmddPair(self.extend(request, params))
544
- #
545
- # {
546
- # "time":20200606,
547
- # "data1m":"[[1591403940,0.024972,0.024972,0.024969,0.024969,0.49999900]]",
548
- # }
549
- #
550
- key = 'data' + self.safe_string(self.timeframes, timeframe, timeframe)
551
- data = self.safe_string(response, key)
552
- ohlcvs = json.loads(data)
553
- return self.parse_ohlcvs(ohlcvs, market, timeframe, since, limit)
554
- except Exception as e:
555
- if isinstance(e, NullResponse):
556
- return []
557
- return None
506
+ request = {}
507
+ if symbols is not None:
508
+ request['pairs'] = self.market_ids(symbols)
509
+ response = await self.publicPostGetTicker(self.extend(request, params))
510
+ #
511
+ # {
512
+ # "ok": "ok",
513
+ # "data": {
514
+ # "AI-USD": {
515
+ # "bestBid": "0.3917",
516
+ # "bestAsk": "0.3949",
517
+ # "bestBidChange": "0.0035",
518
+ # "bestBidChangePercentage": "0.90",
519
+ # "bestAskChange": "0.0038",
520
+ # "bestAskChangePercentage": "0.97",
521
+ # "low": "0.3787",
522
+ # "high": "0.3925",
523
+ # "volume30d": "2945.722277",
524
+ # "lastTradeDateISO": "2024-10-11T06:18:42.077Z",
525
+ # "volume": "120.736000",
526
+ # "quoteVolume": "46.65654070",
527
+ # "lastTradeVolume": "67.914000",
528
+ # "volumeUSD": "46.65",
529
+ # "last": "0.3949",
530
+ # "lastTradePrice": "0.3925",
531
+ # "priceChange": "0.0038",
532
+ # "priceChangePercentage": "0.97"
533
+ # },
534
+ # ...
535
+ #
536
+ data = self.safe_dict(response, 'data', {})
537
+ return self.parse_tickers(data, symbols)
558
538
 
559
- def parse_ticker(self, ticker, market: Market = None) -> Ticker:
560
- timestamp = self.safe_timestamp(ticker, 'timestamp')
561
- volume = self.safe_string(ticker, 'volume')
562
- high = self.safe_string(ticker, 'high')
563
- low = self.safe_string(ticker, 'low')
564
- bid = self.safe_string(ticker, 'bid')
565
- ask = self.safe_string(ticker, 'ask')
566
- last = self.safe_string(ticker, 'last')
567
- symbol = self.safe_symbol(None, market)
539
+ def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
540
+ marketId = self.safe_string(ticker, 'id')
541
+ symbol = self.safe_symbol(marketId, market)
568
542
  return self.safe_ticker({
569
543
  'symbol': symbol,
570
- 'timestamp': timestamp,
571
- 'datetime': self.iso8601(timestamp),
572
- 'high': high,
573
- 'low': low,
574
- 'bid': bid,
544
+ 'timestamp': None,
545
+ 'datetime': None,
546
+ 'high': self.safe_number(ticker, 'high'),
547
+ 'low': self.safe_number(ticker, 'low'),
548
+ 'bid': self.safe_number(ticker, 'bestBid'),
575
549
  'bidVolume': None,
576
- 'ask': ask,
550
+ 'ask': self.safe_number(ticker, 'bestAsk'),
577
551
  'askVolume': None,
578
552
  'vwap': None,
579
553
  'open': None,
580
- 'close': last,
581
- 'last': last,
554
+ 'close': self.safe_string(ticker, 'lastTradePrice'),
582
555
  'previousClose': None,
583
- 'change': None,
584
- 'percentage': None,
556
+ 'change': self.safe_number(ticker, 'priceChange'),
557
+ 'percentage': self.safe_number(ticker, 'priceChangePercentage'),
585
558
  'average': None,
586
- 'baseVolume': volume,
587
- 'quoteVolume': None,
559
+ 'baseVolume': self.safe_string(ticker, 'volume'),
560
+ 'quoteVolume': self.safe_string(ticker, 'quoteVolume'),
588
561
  'info': ticker,
589
562
  }, market)
590
563
 
591
- async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
592
- """
593
- fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
594
- :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
595
- :param dict [params]: extra parameters specific to the exchange API endpoint
596
- :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
564
+ async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
597
565
  """
598
- await self.load_markets()
599
- symbols = self.market_symbols(symbols)
600
- currencies = list(self.currencies.keys())
601
- request = {
602
- 'currencies': '/'.join(currencies),
603
- }
604
- response = await self.publicGetTickersCurrencies(self.extend(request, params))
605
- tickers = self.safe_value(response, 'data', [])
606
- result = {}
607
- for t in range(0, len(tickers)):
608
- ticker = tickers[t]
609
- marketId = self.safe_string(ticker, 'pair')
610
- market = self.safe_market(marketId, None, ':')
611
- symbol = market['symbol']
612
- result[symbol] = self.parse_ticker(ticker, market)
613
- return self.filter_by_array_tickers(result, 'symbol', symbols)
566
+ get the list of most recent trades for a particular symbol
614
567
 
615
- async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
616
- """
617
- :see: https://docs.cex.io/#ticker
618
- fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
619
- :param str symbol: unified symbol of the market to fetch the ticker for
568
+ https://trade.cex.io/docs/#rest-public-api-calls-trade-history
569
+
570
+ :param str symbol: unified symbol of the market to fetch trades for
571
+ :param int [since]: timestamp in ms of the earliest trade to fetch
572
+ :param int [limit]: the maximum amount of trades to fetch
620
573
  :param dict [params]: extra parameters specific to the exchange API endpoint
621
- :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
574
+ :param int [params.until]: timestamp in ms of the latest entry
575
+ :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
622
576
  """
623
577
  await self.load_markets()
624
578
  market = self.market(symbol)
625
- request = {
579
+ request: dict = {
626
580
  'pair': market['id'],
627
581
  }
628
- ticker = await self.publicGetTickerPair(self.extend(request, params))
629
- return self.parse_ticker(ticker, market)
582
+ if since is not None:
583
+ request['fromDateISO'] = self.iso8601(since)
584
+ until = None
585
+ until, params = self.handle_param_integer_2(params, 'until', 'till')
586
+ if until is not None:
587
+ request['toDateISO'] = self.iso8601(until)
588
+ if limit is not None:
589
+ request['pageSize'] = min(limit, 10000) # has a bug, still returns more trades
590
+ response = await self.publicPostGetTradeHistory(self.extend(request, params))
591
+ #
592
+ # {
593
+ # "ok": "ok",
594
+ # "data": {
595
+ # "pageSize": "10",
596
+ # "trades": [
597
+ # {
598
+ # "tradeId": "1728630559823-0",
599
+ # "dateISO": "2024-10-11T07:09:19.823Z",
600
+ # "side": "SELL",
601
+ # "price": "60879.5",
602
+ # "amount": "0.00165962"
603
+ # },
604
+ # ... followed by older trades
605
+ #
606
+ data = self.safe_dict(response, 'data', {})
607
+ trades = self.safe_list(data, 'trades', [])
608
+ return self.parse_trades(trades, market, since, limit)
630
609
 
631
- def parse_trade(self, trade, market: Market = None) -> Trade:
610
+ def parse_trade(self, trade: dict, market: Market = None) -> Trade:
632
611
  #
633
- # fetchTrades(public)
612
+ # public fetchTrades
634
613
  #
635
- # {
636
- # "type": "sell",
637
- # "date": "1638401878",
638
- # "amount": "0.401000",
639
- # "price": "249",
640
- # "tid": "11922"
641
- # }
614
+ # {
615
+ # "tradeId": "1728630559823-0",
616
+ # "dateISO": "2024-10-11T07:09:19.823Z",
617
+ # "side": "SELL",
618
+ # "price": "60879.5",
619
+ # "amount": "0.00165962"
620
+ # },
642
621
  #
643
- timestamp = self.safe_timestamp(trade, 'date')
644
- id = self.safe_string(trade, 'tid')
645
- type = None
646
- side = self.safe_string(trade, 'type')
647
- priceString = self.safe_string(trade, 'price')
648
- amountString = self.safe_string(trade, 'amount')
622
+ dateStr = self.safe_string(trade, 'dateISO')
623
+ timestamp = self.parse8601(dateStr)
649
624
  market = self.safe_market(None, market)
650
625
  return self.safe_trade({
651
626
  'info': trade,
652
- 'id': id,
653
627
  'timestamp': timestamp,
654
628
  'datetime': self.iso8601(timestamp),
655
629
  'symbol': market['symbol'],
656
- 'type': type,
657
- 'side': side,
630
+ 'id': self.safe_string(trade, 'tradeId'),
658
631
  'order': None,
632
+ 'type': None,
659
633
  'takerOrMaker': None,
660
- 'price': priceString,
661
- 'amount': amountString,
634
+ 'side': self.safe_string_lower(trade, 'side'),
635
+ 'price': self.safe_string(trade, 'price'),
636
+ 'amount': self.safe_string(trade, 'amount'),
662
637
  'cost': None,
663
638
  'fee': None,
664
639
  }, market)
665
640
 
666
- async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
641
+ async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
667
642
  """
668
- :see: https://docs.cex.io/#trade-history
669
- get the list of most recent trades for a particular symbol
670
- :param str symbol: unified symbol of the market to fetch trades for
671
- :param int [since]: timestamp in ms of the earliest trade to fetch
672
- :param int [limit]: the maximum amount of trades to fetch
643
+ fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
644
+
645
+ https://trade.cex.io/docs/#rest-public-api-calls-order-book
646
+
647
+ :param str symbol: unified symbol of the market to fetch the order book for
648
+ :param int [limit]: the maximum amount of order book entries to return
673
649
  :param dict [params]: extra parameters specific to the exchange API endpoint
674
- :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
650
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
651
+ """
652
+ await self.load_markets()
653
+ market = self.market(symbol)
654
+ request: dict = {
655
+ 'pair': market['id'],
656
+ }
657
+ response = await self.publicPostGetOrderBook(self.extend(request, params))
658
+ #
659
+ # {
660
+ # "ok": "ok",
661
+ # "data": {
662
+ # "timestamp": "1728636922648",
663
+ # "currency1": "BTC",
664
+ # "currency2": "USDT",
665
+ # "bids": [
666
+ # [
667
+ # "60694.1",
668
+ # "13.12849761"
669
+ # ],
670
+ # [
671
+ # "60694.0",
672
+ # "0.71829244"
673
+ # ],
674
+ # ...
675
+ #
676
+ orderBook = self.safe_dict(response, 'data', {})
677
+ timestamp = self.safe_integer(orderBook, 'timestamp')
678
+ return self.parse_order_book(orderBook, market['symbol'], timestamp)
679
+
680
+ async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
675
681
  """
682
+ fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
683
+
684
+ https://trade.cex.io/docs/#rest-public-api-calls-candles
685
+
686
+ :param str symbol: unified symbol of the market to fetch OHLCV data for
687
+ :param str timeframe: the length of time each candle represents
688
+ :param int [since]: timestamp in ms of the earliest candle to fetch
689
+ :param int [limit]: the maximum amount of candles to fetch
690
+ :param dict [params]: extra parameters specific to the exchange API endpoint
691
+ :param int [params.until]: timestamp in ms of the latest entry
692
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
693
+ """
694
+ dataType = None
695
+ dataType, params = self.handle_option_and_params(params, 'fetchOHLCV', 'dataType')
696
+ if dataType is None:
697
+ raise ArgumentsRequired(self.id + ' fetchOHLCV requires a parameter "dataType" to be either "bestBid" or "bestAsk"')
676
698
  await self.load_markets()
677
699
  market = self.market(symbol)
678
- request = {
700
+ request: dict = {
679
701
  'pair': market['id'],
702
+ 'resolution': self.timeframes[timeframe],
703
+ 'dataType': dataType,
680
704
  }
681
- response = await self.publicGetTradeHistoryPair(self.extend(request, params))
682
- return self.parse_trades(response, market, since, limit)
705
+ if since is not None:
706
+ request['fromISO'] = self.iso8601(since)
707
+ until = None
708
+ until, params = self.handle_param_integer_2(params, 'until', 'till')
709
+ if until is not None:
710
+ request['toISO'] = self.iso8601(until)
711
+ elif since is None:
712
+ # exchange still requires that we provide one of them
713
+ request['toISO'] = self.iso8601(self.milliseconds())
714
+ if since is not None and until is not None and limit is not None:
715
+ raise ArgumentsRequired(self.id + ' fetchOHLCV does not support fetching candles with both a limit and since/until')
716
+ elif (since is not None or until is not None) and limit is None:
717
+ raise ArgumentsRequired(self.id + ' fetchOHLCV requires a limit parameter when fetching candles with since or until')
718
+ if limit is not None:
719
+ request['limit'] = limit
720
+ response = await self.publicPostGetCandles(self.extend(request, params))
721
+ #
722
+ # {
723
+ # "ok": "ok",
724
+ # "data": [
725
+ # {
726
+ # "timestamp": "1728643320000",
727
+ # "open": "61061",
728
+ # "high": "61095.1",
729
+ # "low": "61048.5",
730
+ # "close": "61087.8",
731
+ # "volume": "0",
732
+ # "resolution": "1m",
733
+ # "isClosed": True,
734
+ # "timestampISO": "2024-10-11T10:42:00.000Z"
735
+ # },
736
+ # ...
737
+ #
738
+ data = self.safe_list(response, 'data', [])
739
+ return self.parse_ohlcvs(data, market, timeframe, since, limit)
740
+
741
+ def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
742
+ return [
743
+ self.safe_integer(ohlcv, 'timestamp'),
744
+ self.safe_number(ohlcv, 'open'),
745
+ self.safe_number(ohlcv, 'high'),
746
+ self.safe_number(ohlcv, 'low'),
747
+ self.safe_number(ohlcv, 'close'),
748
+ self.safe_number(ohlcv, 'volume'),
749
+ ]
683
750
 
684
- async def fetch_trading_fees(self, params={}):
751
+ async def fetch_trading_fees(self, params={}) -> TradingFees:
685
752
  """
686
- :see: https://docs.cex.io/#get-my-fee
687
753
  fetch the trading fees for multiple markets
754
+
755
+ https://trade.cex.io/docs/#rest-public-api-calls-candles
756
+
688
757
  :param dict [params]: extra parameters specific to the exchange API endpoint
689
758
  :returns dict: a dictionary of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>` indexed by market symbols
690
759
  """
691
760
  await self.load_markets()
692
- response = await self.privatePostGetMyfee(params)
761
+ response = await self.privatePostGetMyCurrentFee(params)
693
762
  #
694
- # {
695
- # "e": "get_myfee",
696
- # "ok": "ok",
697
- # "data": {
698
- # 'BTC:USD': {buy: '0.25', sell: '0.25', buyMaker: '0.15', sellMaker: "0.15"},
699
- # 'ETH:USD': {buy: '0.25', sell: '0.25', buyMaker: '0.15', sellMaker: "0.15"},
700
- # ..
701
- # }
702
- # }
763
+ # {
764
+ # "ok": "ok",
765
+ # "data": {
766
+ # "tradingFee": {
767
+ # "AI-USD": {
768
+ # "percent": "0.25"
769
+ # },
770
+ # ...
703
771
  #
704
- data = self.safe_value(response, 'data', {})
705
- result = {}
772
+ data = self.safe_dict(response, 'data', {})
773
+ fees = self.safe_dict(data, 'tradingFee', {})
774
+ return self.parse_trading_fees(fees, True)
775
+
776
+ def parse_trading_fees(self, response, useKeyAsId=False) -> TradingFees:
777
+ result: dict = {}
778
+ keys = list(response.keys())
779
+ for i in range(0, len(keys)):
780
+ key = keys[i]
781
+ market = None
782
+ if useKeyAsId:
783
+ market = self.safe_market(key)
784
+ parsed = self.parse_trading_fee(response[key], market)
785
+ result[parsed['symbol']] = parsed
706
786
  for i in range(0, len(self.symbols)):
707
787
  symbol = self.symbols[i]
708
- market = self.market(symbol)
709
- fee = self.safe_value(data, market['id'], {})
710
- makerString = self.safe_string(fee, 'buyMaker')
711
- takerString = self.safe_string(fee, 'buy')
712
- maker = self.parse_number(Precise.string_div(makerString, '100'))
713
- taker = self.parse_number(Precise.string_div(takerString, '100'))
714
- result[symbol] = {
715
- 'info': fee,
716
- 'symbol': symbol,
717
- 'maker': maker,
718
- 'taker': taker,
719
- 'percentage': True,
720
- }
788
+ if not (symbol in result):
789
+ market = self.market(symbol)
790
+ result[symbol] = self.parse_trading_fee(response, market)
721
791
  return result
722
792
 
793
+ def parse_trading_fee(self, fee: dict, market: Market = None) -> TradingFeeInterface:
794
+ return {
795
+ 'info': fee,
796
+ 'symbol': self.safe_string(market, 'symbol'),
797
+ 'maker': self.safe_number(fee, 'percent'),
798
+ 'taker': self.safe_number(fee, 'percent'),
799
+ 'percentage': None,
800
+ 'tierBased': None,
801
+ }
802
+
803
+ async def fetch_accounts(self, params={}) -> List[Account]:
804
+ await self.load_markets()
805
+ response = await self.privatePostGetMyAccountStatusV3(params)
806
+ #
807
+ # {
808
+ # "ok": "ok",
809
+ # "data": {
810
+ # "convertedCurrency": "USD",
811
+ # "balancesPerAccounts": {
812
+ # "": {
813
+ # "AI": {
814
+ # "balance": "0.000000",
815
+ # "balanceOnHold": "0.000000"
816
+ # },
817
+ # "USDT": {
818
+ # "balance": "0.00000000",
819
+ # "balanceOnHold": "0.00000000"
820
+ # }
821
+ # }
822
+ # }
823
+ # }
824
+ # }
825
+ #
826
+ data = self.safe_dict(response, 'data', {})
827
+ balances = self.safe_dict(data, 'balancesPerAccounts', {})
828
+ arrays = self.to_array(balances)
829
+ return self.parse_accounts(arrays, params)
830
+
831
+ def parse_account(self, account: dict) -> Account:
832
+ return {
833
+ 'id': None,
834
+ 'type': None,
835
+ 'code': None,
836
+ 'info': account,
837
+ }
838
+
839
+ async def fetch_balance(self, params={}) -> Balances:
840
+ """
841
+ query for balance and get the amount of funds available for trading or funds locked in orders
842
+
843
+ https://trade.cex.io/docs/#rest-private-api-calls-account-status-v3
844
+
845
+ :param dict [params]: extra parameters specific to the exchange API endpoint
846
+ :param dict [params.method]: 'privatePostGetMyWalletBalance' or 'privatePostGetMyAccountStatusV3'
847
+ :param dict [params.account]: in case 'privatePostGetMyAccountStatusV3' is chosen, self can specify the account name(default is empty string)
848
+ :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
849
+ """
850
+ accountName = None
851
+ accountName, params = self.handle_param_string(params, 'account', '') # default is empty string
852
+ method = None
853
+ method, params = self.handle_param_string(params, 'method', 'privatePostGetMyWalletBalance')
854
+ accountBalance = None
855
+ if method == 'privatePostGetMyAccountStatusV3':
856
+ response = await self.privatePostGetMyAccountStatusV3(params)
857
+ #
858
+ # {
859
+ # "ok": "ok",
860
+ # "data": {
861
+ # "convertedCurrency": "USD",
862
+ # "balancesPerAccounts": {
863
+ # "": {
864
+ # "AI": {
865
+ # "balance": "0.000000",
866
+ # "balanceOnHold": "0.000000"
867
+ # },
868
+ # ....
869
+ #
870
+ data = self.safe_dict(response, 'data', {})
871
+ balances = self.safe_dict(data, 'balancesPerAccounts', {})
872
+ accountBalance = self.safe_dict(balances, accountName, {})
873
+ else:
874
+ response = await self.privatePostGetMyWalletBalance(params)
875
+ #
876
+ # {
877
+ # "ok": "ok",
878
+ # "data": {
879
+ # "AI": {
880
+ # "balance": "25.606429"
881
+ # },
882
+ # "USDT": {
883
+ # "balance": "7.935449"
884
+ # },
885
+ # ...
886
+ #
887
+ accountBalance = self.safe_dict(response, 'data', {})
888
+ return self.parse_balance(accountBalance)
889
+
890
+ def parse_balance(self, response) -> Balances:
891
+ result: dict = {
892
+ 'info': response,
893
+ }
894
+ keys = list(response.keys())
895
+ for i in range(0, len(keys)):
896
+ key = keys[i]
897
+ balance = self.safe_dict(response, key, {})
898
+ code = self.safe_currency_code(key)
899
+ account: dict = {
900
+ 'used': self.safe_string(balance, 'balanceOnHold'),
901
+ 'total': self.safe_string(balance, 'balance'),
902
+ }
903
+ result[code] = account
904
+ return self.safe_balance(result)
905
+
906
+ async def fetch_orders_by_status(self, status: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
907
+ """
908
+ fetches information on multiple orders made by the user
909
+
910
+ https://trade.cex.io/docs/#rest-private-api-calls-orders
911
+
912
+ :param str status: order status to fetch for
913
+ :param str symbol: unified market symbol of the market orders were made in
914
+ :param int [since]: the earliest time in ms to fetch orders for
915
+ :param int [limit]: the maximum number of order structures to retrieve
916
+ :param dict [params]: extra parameters specific to the exchange API endpoint
917
+ :param int [params.until]: timestamp in ms of the latest entry
918
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
919
+ """
920
+ await self.load_markets()
921
+ request: dict = {}
922
+ isClosedOrders = (status == 'closed')
923
+ if isClosedOrders:
924
+ request['archived'] = True
925
+ market = None
926
+ if symbol is not None:
927
+ market = self.market(symbol)
928
+ request['pair'] = market['id']
929
+ if limit is not None:
930
+ request['pageSize'] = limit
931
+ if since is not None:
932
+ request['serverCreateTimestampFrom'] = since
933
+ elif isClosedOrders:
934
+ # exchange requires a `since` parameter for closed orders, so set default to allowed 365
935
+ request['serverCreateTimestampFrom'] = self.milliseconds() - 364 * 24 * 60 * 60 * 1000
936
+ until = None
937
+ until, params = self.handle_param_integer_2(params, 'until', 'till')
938
+ if until is not None:
939
+ request['serverCreateTimestampTo'] = until
940
+ response = await self.privatePostGetMyOrders(self.extend(request, params))
941
+ #
942
+ # if called without `pair`
943
+ #
944
+ # {
945
+ # "ok": "ok",
946
+ # "data": [
947
+ # {
948
+ # "orderId": "1313003",
949
+ # "clientOrderId": "037F0AFEB93A",
950
+ # "clientId": "up421412345",
951
+ # "accountId": null,
952
+ # "status": "FILLED",
953
+ # "statusIsFinal": True,
954
+ # "currency1": "AI",
955
+ # "currency2": "USDT",
956
+ # "side": "BUY",
957
+ # "orderType": "Market",
958
+ # "timeInForce": "IOC",
959
+ # "comment": null,
960
+ # "rejectCode": null,
961
+ # "rejectReason": null,
962
+ # "initialOnHoldAmountCcy1": null,
963
+ # "initialOnHoldAmountCcy2": "10.23456700",
964
+ # "executedAmountCcy1": "25.606429",
965
+ # "executedAmountCcy2": "10.20904439",
966
+ # "requestedAmountCcy1": null,
967
+ # "requestedAmountCcy2": "10.20904439",
968
+ # "originalAmountCcy2": "10.23456700",
969
+ # "feeAmount": "0.02552261",
970
+ # "feeCurrency": "USDT",
971
+ # "price": null,
972
+ # "averagePrice": "0.3986",
973
+ # "clientCreateTimestamp": "1728474625320",
974
+ # "serverCreateTimestamp": "1728474624956",
975
+ # "lastUpdateTimestamp": "1728474628015",
976
+ # "expireTime": null,
977
+ # "effectiveTime": null
978
+ # },
979
+ # ...
980
+ #
981
+ data = self.safe_list(response, 'data', [])
982
+ return self.parse_orders(data, market, since, limit)
983
+
984
+ async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
985
+ """
986
+
987
+ https://trade.cex.io/docs/#rest-private-api-calls-orders
988
+
989
+ fetches information on multiple canceled orders made by the user
990
+ :param str symbol: unified market symbol of the market orders were made in
991
+ :param int [since]: timestamp in ms of the earliest order, default is None
992
+ :param int [limit]: max number of orders to return, default is None
993
+ :param dict [params]: extra parameters specific to the exchange API endpoint
994
+ :returns dict: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
995
+ """
996
+ return await self.fetch_orders_by_status('closed', symbol, since, limit, params)
997
+
998
+ async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
999
+ """
1000
+
1001
+ https://trade.cex.io/docs/#rest-private-api-calls-orders
1002
+
1003
+ fetches information on multiple canceled orders made by the user
1004
+ :param str symbol: unified market symbol of the market orders were made in
1005
+ :param int [since]: timestamp in ms of the earliest order, default is None
1006
+ :param int [limit]: max number of orders to return, default is None
1007
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1008
+ :returns dict: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1009
+ """
1010
+ return await self.fetch_orders_by_status('open', symbol, since, limit, params)
1011
+
1012
+ async def fetch_open_order(self, id: str, symbol: Str = None, params={}):
1013
+ """
1014
+ fetches information on an open order made by the user
1015
+
1016
+ https://trade.cex.io/docs/#rest-private-api-calls-orders
1017
+
1018
+ :param str id: order id
1019
+ :param str [symbol]: unified symbol of the market the order was made in
1020
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1021
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1022
+ """
1023
+ await self.load_markets()
1024
+ request: dict = {
1025
+ 'orderId': int(id),
1026
+ }
1027
+ result = await self.fetch_open_orders(symbol, None, None, self.extend(request, params))
1028
+ return result[0]
1029
+
1030
+ async def fetch_closed_order(self, id: str, symbol: Str = None, params={}):
1031
+ """
1032
+ fetches information on an closed order made by the user
1033
+
1034
+ https://trade.cex.io/docs/#rest-private-api-calls-orders
1035
+
1036
+ :param str id: order id
1037
+ :param str [symbol]: unified symbol of the market the order was made in
1038
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1039
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1040
+ """
1041
+ await self.load_markets()
1042
+ request: dict = {
1043
+ 'orderId': int(id),
1044
+ }
1045
+ result = await self.fetch_closed_orders(symbol, None, None, self.extend(request, params))
1046
+ return result[0]
1047
+
1048
+ def parse_order_status(self, status: Str):
1049
+ statuses: dict = {
1050
+ 'PENDING_NEW': 'open',
1051
+ 'NEW': 'open',
1052
+ 'PARTIALLY_FILLED': 'open',
1053
+ 'FILLED': 'closed',
1054
+ 'EXPIRED': 'expired',
1055
+ 'REJECTED': 'rejected',
1056
+ 'PENDING_CANCEL': 'canceling',
1057
+ 'CANCELLED': 'canceled',
1058
+ }
1059
+ return self.safe_string(statuses, status, status)
1060
+
1061
+ def parse_order(self, order: dict, market: Market = None) -> Order:
1062
+ #
1063
+ # "orderId": "1313003",
1064
+ # "clientOrderId": "037F0AFEB93A",
1065
+ # "clientId": "up421412345",
1066
+ # "accountId": null,
1067
+ # "status": "FILLED",
1068
+ # "statusIsFinal": True,
1069
+ # "currency1": "AI",
1070
+ # "currency2": "USDT",
1071
+ # "side": "BUY",
1072
+ # "orderType": "Market",
1073
+ # "timeInForce": "IOC",
1074
+ # "comment": null,
1075
+ # "rejectCode": null,
1076
+ # "rejectReason": null,
1077
+ # "initialOnHoldAmountCcy1": null,
1078
+ # "initialOnHoldAmountCcy2": "10.23456700",
1079
+ # "executedAmountCcy1": "25.606429",
1080
+ # "executedAmountCcy2": "10.20904439",
1081
+ # "requestedAmountCcy1": null,
1082
+ # "requestedAmountCcy2": "10.20904439",
1083
+ # "originalAmountCcy2": "10.23456700",
1084
+ # "feeAmount": "0.02552261",
1085
+ # "feeCurrency": "USDT",
1086
+ # "price": null,
1087
+ # "averagePrice": "0.3986",
1088
+ # "clientCreateTimestamp": "1728474625320",
1089
+ # "serverCreateTimestamp": "1728474624956",
1090
+ # "lastUpdateTimestamp": "1728474628015",
1091
+ # "expireTime": null,
1092
+ # "effectiveTime": null
1093
+ #
1094
+ currency1 = self.safe_string(order, 'currency1')
1095
+ currency2 = self.safe_string(order, 'currency2')
1096
+ marketId = None
1097
+ if currency1 is not None and currency2 is not None:
1098
+ marketId = currency1 + '-' + currency2
1099
+ market = self.safe_market(marketId, market)
1100
+ symbol = market['symbol']
1101
+ status = self.parse_order_status(self.safe_string(order, 'status'))
1102
+ fee = {}
1103
+ feeAmount = self.safe_number(order, 'feeAmount')
1104
+ if feeAmount is not None:
1105
+ currencyId = self.safe_string(order, 'feeCurrency')
1106
+ feeCode = self.safe_currency_code(currencyId)
1107
+ fee['currency'] = feeCode
1108
+ fee['cost'] = feeAmount
1109
+ timestamp = self.safe_integer(order, 'serverCreateTimestamp')
1110
+ requestedBase = self.safe_number(order, 'requestedAmountCcy1')
1111
+ executedBase = self.safe_number(order, 'executedAmountCcy1')
1112
+ # requestedQuote = self.safe_number(order, 'requestedAmountCcy2')
1113
+ executedQuote = self.safe_number(order, 'executedAmountCcy2')
1114
+ return self.safe_order({
1115
+ 'id': self.safe_string(order, 'orderId'),
1116
+ 'clientOrderId': self.safe_string(order, 'clientOrderId'),
1117
+ 'timestamp': timestamp,
1118
+ 'datetime': self.iso8601(timestamp),
1119
+ 'lastUpdateTimestamp': self.safe_integer(order, 'lastUpdateTimestamp'),
1120
+ 'lastTradeTimestamp': None,
1121
+ 'symbol': symbol,
1122
+ 'type': self.safe_string_lower(order, 'orderType'),
1123
+ 'timeInForce': self.safe_string(order, 'timeInForce'),
1124
+ 'postOnly': None,
1125
+ 'side': self.safe_string_lower(order, 'side'),
1126
+ 'price': self.safe_number(order, 'price'),
1127
+ 'triggerPrice': self.safe_number(order, 'stopPrice'),
1128
+ 'amount': requestedBase,
1129
+ 'cost': executedQuote,
1130
+ 'average': self.safe_number(order, 'averagePrice'),
1131
+ 'filled': executedBase,
1132
+ 'remaining': None,
1133
+ 'status': status,
1134
+ 'fee': fee,
1135
+ 'trades': None,
1136
+ 'info': order,
1137
+ }, market)
1138
+
723
1139
  async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
724
1140
  """
725
- :see: https://docs.cex.io/#place-order
726
1141
  create a trade order
727
- :see: https://cex.io/rest-api#place-order
1142
+
1143
+ https://trade.cex.io/docs/#rest-private-api-calls-new-order
1144
+
728
1145
  :param str symbol: unified symbol of the market to create an order in
729
1146
  :param str type: 'market' or 'limit'
730
1147
  :param str side: 'buy' or 'sell'
731
1148
  :param float amount: how much of currency you want to trade in units of base currency
732
- :param float [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
1149
+ :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
733
1150
  :param dict [params]: extra parameters specific to the exchange API endpoint
734
- :param float [params.cost]: the quote quantity that can be used alternative for the amount for market buy orders
1151
+ :param str [params.accountId]: account-id to use(default is empty string)
1152
+ :param float [params.triggerPrice]: the price at which a trigger order is triggered at
735
1153
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
736
1154
  """
1155
+ accountId = None
1156
+ accountId, params = self.handle_option_and_params(params, 'createOrder', 'accountId')
1157
+ if accountId is None:
1158
+ raise ArgumentsRequired(self.id + ' createOrder() : API trading is now allowed from main account, set params["accountId"] or .options["createOrder"]["accountId"] to the name of your sub-account')
737
1159
  await self.load_markets()
738
1160
  market = self.market(symbol)
739
- request = {
740
- 'pair': market['id'],
741
- 'type': side,
1161
+ request: dict = {
1162
+ 'clientOrderId': self.uuid(),
1163
+ 'currency1': market['baseId'],
1164
+ 'currency2': market['quoteId'],
1165
+ 'accountId': accountId,
1166
+ 'orderType': self.capitalize(type.lower()),
1167
+ 'side': side.upper(),
1168
+ 'timestamp': self.milliseconds(),
1169
+ 'amountCcy1': self.amount_to_precision(symbol, amount),
742
1170
  }
743
- # for market buy it requires the amount of quote currency to spend
744
- if (type == 'market') and (side == 'buy'):
745
- quoteAmount = None
746
- createMarketBuyOrderRequiresPrice = True
747
- createMarketBuyOrderRequiresPrice, params = self.handle_option_and_params(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', True)
748
- cost = self.safe_string(params, 'cost')
749
- params = self.omit(params, 'cost')
750
- if cost is not None:
751
- quoteAmount = self.cost_to_precision(symbol, cost)
752
- elif createMarketBuyOrderRequiresPrice:
753
- if price is None:
754
- raise InvalidOrder(self.id + ' createOrder() requires the price argument for market buy orders to calculate the total cost to spend(amount * price), alternatively set the createMarketBuyOrderRequiresPrice option or param to False and pass the cost to spend in the amount argument')
755
- else:
756
- amountString = self.number_to_string(amount)
757
- priceString = self.number_to_string(price)
758
- costRequest = Precise.string_mul(amountString, priceString)
759
- quoteAmount = self.cost_to_precision(symbol, costRequest)
760
- else:
761
- quoteAmount = self.cost_to_precision(symbol, amount)
762
- request['amount'] = quoteAmount
763
- else:
764
- request['amount'] = self.amount_to_precision(symbol, amount)
1171
+ timeInForce = None
1172
+ timeInForce, params = self.handle_option_and_params(params, 'createOrder', 'timeInForce', 'GTC')
765
1173
  if type == 'limit':
766
- request['price'] = self.number_to_string(price)
767
- else:
768
- request['order_type'] = type
769
- response = await self.privatePostPlaceOrderPair(self.extend(request, params))
1174
+ request['price'] = self.price_to_precision(symbol, price)
1175
+ request['timeInForce'] = timeInForce
1176
+ triggerPrice = None
1177
+ triggerPrice, params = self.handle_param_string(params, 'triggerPrice')
1178
+ if triggerPrice is not None:
1179
+ request['type'] = 'Stop Limit'
1180
+ request['stopPrice'] = triggerPrice
1181
+ response = await self.privatePostDoMyNewOrder(self.extend(request, params))
770
1182
  #
771
- # {
772
- # "id": "12978363524",
773
- # "time": 1586610022259,
774
- # "type": "buy",
775
- # "price": "0.033934",
776
- # "amount": "0.10722802",
777
- # "pending": "0.10722802",
778
- # "complete": False
779
- # }
1183
+ # on success
780
1184
  #
781
- placedAmount = self.safe_string(response, 'amount')
782
- remaining = self.safe_string(response, 'pending')
783
- timestamp = self.safe_value(response, 'time')
784
- complete = self.safe_value(response, 'complete')
785
- status = 'closed' if complete else 'open'
786
- filled = None
787
- if (placedAmount is not None) and (remaining is not None):
788
- filled = Precise.string_max(Precise.string_sub(placedAmount, remaining), '0')
789
- return self.safe_order({
790
- 'id': self.safe_string(response, 'id'),
791
- 'info': response,
792
- 'clientOrderId': None,
793
- 'timestamp': timestamp,
794
- 'datetime': self.iso8601(timestamp),
795
- 'lastTradeTimestamp': None,
796
- 'type': type,
797
- 'side': self.safe_string(response, 'type'),
798
- 'symbol': market['symbol'],
799
- 'status': status,
800
- 'price': self.safe_string(response, 'price'),
801
- 'amount': placedAmount,
802
- 'cost': None,
803
- 'average': None,
804
- 'remaining': remaining,
805
- 'filled': filled,
806
- 'fee': None,
807
- 'trades': None,
808
- })
1185
+ # {
1186
+ # "ok": "ok",
1187
+ # "data": {
1188
+ # "messageType": "executionReport",
1189
+ # "clientId": "up132245425",
1190
+ # "orderId": "1318485",
1191
+ # "clientOrderId": "b5b6cd40-154c-4c1c-bd51-4a442f3d50b9",
1192
+ # "accountId": "sub1",
1193
+ # "status": "FILLED",
1194
+ # "currency1": "LTC",
1195
+ # "currency2": "USDT",
1196
+ # "side": "BUY",
1197
+ # "executedAmountCcy1": "0.23000000",
1198
+ # "executedAmountCcy2": "15.09030000",
1199
+ # "requestedAmountCcy1": "0.23000000",
1200
+ # "requestedAmountCcy2": null,
1201
+ # "orderType": "Market",
1202
+ # "timeInForce": null,
1203
+ # "comment": null,
1204
+ # "executionType": "Trade",
1205
+ # "executionId": "1726747124624_101_41116",
1206
+ # "transactTime": "2024-10-15T15:08:12.794Z",
1207
+ # "expireTime": null,
1208
+ # "effectiveTime": null,
1209
+ # "averagePrice": "65.61",
1210
+ # "lastQuantity": "0.23000000",
1211
+ # "lastAmountCcy1": "0.23000000",
1212
+ # "lastAmountCcy2": "15.09030000",
1213
+ # "lastPrice": "65.61",
1214
+ # "feeAmount": "0.03772575",
1215
+ # "feeCurrency": "USDT",
1216
+ # "clientCreateTimestamp": "1729004892014",
1217
+ # "serverCreateTimestamp": "1729004891628",
1218
+ # "lastUpdateTimestamp": "1729004892786"
1219
+ # }
1220
+ # }
1221
+ #
1222
+ # on failure, there are extra fields
1223
+ #
1224
+ # "status": "REJECTED",
1225
+ # "requestedAmountCcy1": null,
1226
+ # "orderRejectReason": "{\\" code \\ ":405,\\" reason \\ ":\\" Either AmountCcy1(OrderQty)or AmountCcy2(CashOrderQty)should be specified for market order not both \\ "}",
1227
+ # "rejectCode": 405,
1228
+ # "rejectReason": "Either AmountCcy1(OrderQty) or AmountCcy2(CashOrderQty) should be specified for market order not both",
1229
+ #
1230
+ data = self.safe_dict(response, 'data')
1231
+ return self.parse_order(data, market)
809
1232
 
810
1233
  async def cancel_order(self, id: str, symbol: Str = None, params={}):
811
1234
  """
812
- :see: https://docs.cex.io/#cancel-order
813
1235
  cancels an open order
1236
+
1237
+ https://trade.cex.io/docs/#rest-private-api-calls-cancel-order
1238
+
814
1239
  :param str id: order id
815
- :param str symbol: not used by cex cancelOrder()
1240
+ :param str symbol: unified symbol of the market the order was made in
816
1241
  :param dict [params]: extra parameters specific to the exchange API endpoint
817
1242
  :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
818
1243
  """
819
1244
  await self.load_markets()
820
- request = {
821
- 'id': id,
1245
+ request: dict = {
1246
+ 'orderId': int(id),
1247
+ 'cancelRequestId': 'c_' + str((self.milliseconds())),
1248
+ 'timestamp': self.milliseconds(),
822
1249
  }
823
- response = await self.privatePostCancelOrder(self.extend(request, params))
824
- # 'true'
825
- return self.extend(self.parse_order({}), {'info': response, 'type': None, 'id': id, 'status': 'canceled'})
1250
+ response = await self.privatePostDoCancelMyOrder(self.extend(request, params))
1251
+ #
1252
+ # {"ok":"ok","data":{}}
1253
+ #
1254
+ data = self.safe_dict(response, 'data', {})
1255
+ return self.parse_order(data)
826
1256
 
827
1257
  async def cancel_all_orders(self, symbol: Str = None, params={}):
828
1258
  """
829
- :see: https://docs.cex.io/#cancel-all-orders-for-given-pair
830
1259
  cancel all open orders in a market
831
- :param str symbol: unified market symbol of the market to cancel orders in
832
- :param dict [params]: extra parameters specific to the cex api endpoint
833
- :param str [params.marginMode]: 'cross' or 'isolated', for spot margin trading
1260
+
1261
+ https://trade.cex.io/docs/#rest-private-api-calls-cancel-all-orders
1262
+
1263
+ :param str symbol: alpaca cancelAllOrders cannot setting symbol, it will cancel all open orders
1264
+ :param dict [params]: extra parameters specific to the exchange API endpoint
834
1265
  :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
835
1266
  """
836
- if symbol is None:
837
- raise ArgumentsRequired(self.id + ' cancelAllOrders requires a symbol.')
838
1267
  await self.load_markets()
839
- market = self.market(symbol)
840
- request = {
841
- 'pair': market['id'],
842
- }
843
- orders = await self.privatePostCancelOrdersPair(self.extend(request, params))
1268
+ response = await self.privatePostDoCancelAllOrders(params)
844
1269
  #
845
- # {
846
- # "e":"cancel_orders",
847
- # "ok":"ok",
848
- # "data":[
849
- # ]
850
- # }
1270
+ # {
1271
+ # "ok": "ok",
1272
+ # "data": {
1273
+ # "clientOrderIds": [
1274
+ # "3AF77B67109F"
1275
+ # ]
1276
+ # }
1277
+ # }
851
1278
  #
852
- return orders
853
-
854
- def parse_order(self, order, market: Market = None) -> Order:
855
- # Depending on the call, 'time' can be a unix int, unix string or ISO string
856
- # Yes, really
857
- timestamp = self.safe_value(order, 'time')
858
- if isinstance(timestamp, str) and timestamp.find('T') >= 0:
859
- # ISO8601 string
860
- timestamp = self.parse8601(timestamp)
861
- elif timestamp is not None:
862
- # either integer or string integer
863
- timestamp = int(timestamp)
864
- symbol = None
865
- baseId = self.safe_string(order, 'symbol1')
866
- quoteId = self.safe_string(order, 'symbol2')
867
- if market is None and baseId is not None and quoteId is not None:
868
- base = self.safe_currency_code(baseId)
869
- quote = self.safe_currency_code(quoteId)
870
- if (base is not None) and (quote is not None):
871
- symbol = base + '/' + quote
872
- if symbol in self.markets:
873
- market = self.market(symbol)
874
- status = self.parse_order_status(self.safe_string(order, 'status'))
875
- price = self.safe_string(order, 'price')
876
- amount = self.omit_zero(self.safe_string(order, 'amount'))
877
- # sell orders can have a negative amount
878
- # https://github.com/ccxt/ccxt/issues/5338
879
- if amount is not None:
880
- amount = Precise.string_abs(amount)
881
- elif market is not None:
882
- amountKey = 'a:' + market['base'] + 'cds:'
883
- amount = Precise.string_abs(self.safe_string(order, amountKey))
884
- remaining = self.safe_string_2(order, 'pending', 'remains')
885
- filled = Precise.string_sub(amount, remaining)
886
- fee = None
887
- cost = None
888
- if market is not None:
889
- symbol = market['symbol']
890
- taCost = self.safe_string(order, 'ta:' + market['quote'])
891
- ttaCost = self.safe_string(order, 'tta:' + market['quote'])
892
- cost = Precise.string_add(taCost, ttaCost)
893
- baseFee = 'fa:' + market['base']
894
- baseTakerFee = 'tfa:' + market['base']
895
- quoteFee = 'fa:' + market['quote']
896
- quoteTakerFee = 'tfa:' + market['quote']
897
- feeRate = self.safe_string(order, 'tradingFeeMaker')
898
- if not feeRate:
899
- feeRate = self.safe_string(order, 'tradingFeeTaker', feeRate)
900
- if feeRate:
901
- feeRate = Precise.string_div(feeRate, '100') # convert to mathematically-correct percentage coefficients: 1.0 = 100%
902
- if (baseFee in order) or (baseTakerFee in order):
903
- baseFeeCost = self.safe_number_2(order, baseFee, baseTakerFee)
904
- fee = {
905
- 'currency': market['base'],
906
- 'rate': self.parse_number(feeRate),
907
- 'cost': baseFeeCost,
908
- }
909
- elif (quoteFee in order) or (quoteTakerFee in order):
910
- quoteFeeCost = self.safe_number_2(order, quoteFee, quoteTakerFee)
911
- fee = {
912
- 'currency': market['quote'],
913
- 'rate': self.parse_number(feeRate),
914
- 'cost': quoteFeeCost,
915
- }
916
- if not cost:
917
- cost = Precise.string_mul(price, filled)
918
- side = self.safe_string(order, 'type')
919
- trades = None
920
- orderId = self.safe_string(order, 'id')
921
- if 'vtx' in order:
922
- trades = []
923
- for i in range(0, len(order['vtx'])):
924
- item = order['vtx'][i]
925
- tradeSide = self.safe_string(item, 'type')
926
- if tradeSide == 'cancel':
927
- # looks like self might represent the cancelled part of an order
928
- # {"id": "4426729543",
929
- # "type": "cancel",
930
- # "time": "2017-09-22T00:24:30.476Z",
931
- # "user": "up106404164",
932
- # "c": "user:up106404164:a:BCH",
933
- # "d": "order:4426728375:a:BCH",
934
- # "a": "0.09935956",
935
- # "amount": "0.09935956",
936
- # "balance": "0.42580261",
937
- # "symbol": "BCH",
938
- # "order": "4426728375",
939
- # "buy": null,
940
- # "sell": null,
941
- # "pair": null,
942
- # "pos": null,
943
- # "cs": "0.42580261",
944
- # "ds": 0}
945
- continue
946
- tradePrice = self.safe_string(item, 'price')
947
- if tradePrice is None:
948
- # self represents the order
949
- # {
950
- # "a": "0.47000000",
951
- # "c": "user:up106404164:a:EUR",
952
- # "d": "order:6065499239:a:EUR",
953
- # "cs": "1432.93",
954
- # "ds": "476.72",
955
- # "id": "6065499249",
956
- # "buy": null,
957
- # "pos": null,
958
- # "pair": null,
959
- # "sell": null,
960
- # "time": "2018-04-22T13:07:22.152Z",
961
- # "type": "buy",
962
- # "user": "up106404164",
963
- # "order": "6065499239",
964
- # "amount": "-715.97000000",
965
- # "symbol": "EUR",
966
- # "balance": "1432.93000000"}
967
- continue
968
- # todo: deal with these
969
- if tradeSide == 'costsNothing':
970
- continue
971
- # --
972
- # if side != tradeSide:
973
- # raise Error(json.dumps(order, null, 2))
974
- # if orderId != item['order']:
975
- # raise Error(json.dumps(order, null, 2))
976
- # --
977
- # partial buy trade
978
- # {
979
- # "a": "0.01589885",
980
- # "c": "user:up106404164:a:BTC",
981
- # "d": "order:6065499239:a:BTC",
982
- # "cs": "0.36300000",
983
- # "ds": 0,
984
- # "id": "6067991213",
985
- # "buy": "6065499239",
986
- # "pos": null,
987
- # "pair": null,
988
- # "sell": "6067991206",
989
- # "time": "2018-04-22T23:09:11.773Z",
990
- # "type": "buy",
991
- # "user": "up106404164",
992
- # "order": "6065499239",
993
- # "price": 7146.5,
994
- # "amount": "0.01589885",
995
- # "symbol": "BTC",
996
- # "balance": "0.36300000",
997
- # "symbol2": "EUR",
998
- # "fee_amount": "0.19"}
999
- # --
1000
- # trade with zero amount, but non-zero fee
1001
- # {
1002
- # "a": "0.00000000",
1003
- # "c": "user:up106404164:a:EUR",
1004
- # "d": "order:5840654423:a:EUR",
1005
- # "cs": 559744,
1006
- # "ds": 0,
1007
- # "id": "5840654429",
1008
- # "buy": "5807238573",
1009
- # "pos": null,
1010
- # "pair": null,
1011
- # "sell": "5840654423",
1012
- # "time": "2018-03-15T03:20:14.010Z",
1013
- # "type": "sell",
1014
- # "user": "up106404164",
1015
- # "order": "5840654423",
1016
- # "price": 730,
1017
- # "amount": "0.00000000",
1018
- # "symbol": "EUR",
1019
- # "balance": "5597.44000000",
1020
- # "symbol2": "BCH",
1021
- # "fee_amount": "0.01"}
1022
- # --
1023
- # trade which should have an amount of exactly 0.002BTC
1024
- # {
1025
- # "a": "16.70000000",
1026
- # "c": "user:up106404164:a:GBP",
1027
- # "d": "order:9927386681:a:GBP",
1028
- # "cs": "86.90",
1029
- # "ds": 0,
1030
- # "id": "9927401610",
1031
- # "buy": "9927401601",
1032
- # "pos": null,
1033
- # "pair": null,
1034
- # "sell": "9927386681",
1035
- # "time": "2019-08-21T15:25:37.777Z",
1036
- # "type": "sell",
1037
- # "user": "up106404164",
1038
- # "order": "9927386681",
1039
- # "price": 8365,
1040
- # "amount": "16.70000000",
1041
- # "office": "UK",
1042
- # "symbol": "GBP",
1043
- # "balance": "86.90000000",
1044
- # "symbol2": "BTC",
1045
- # "fee_amount": "0.03"
1046
- # }
1047
- tradeTimestamp = self.parse8601(self.safe_string(item, 'time'))
1048
- tradeAmount = self.safe_string(item, 'amount')
1049
- feeCost = self.safe_string(item, 'fee_amount')
1050
- absTradeAmount = Precise.string_abs(tradeAmount)
1051
- tradeCost = None
1052
- if tradeSide == 'sell':
1053
- tradeCost = absTradeAmount
1054
- absTradeAmount = Precise.string_div(Precise.string_add(feeCost, tradeCost), tradePrice)
1055
- else:
1056
- tradeCost = Precise.string_mul(absTradeAmount, tradePrice)
1057
- trades.append({
1058
- 'id': self.safe_string(item, 'id'),
1059
- 'timestamp': tradeTimestamp,
1060
- 'datetime': self.iso8601(tradeTimestamp),
1061
- 'order': orderId,
1062
- 'symbol': symbol,
1063
- 'price': self.parse_number(tradePrice),
1064
- 'amount': self.parse_number(absTradeAmount),
1065
- 'cost': self.parse_number(tradeCost),
1066
- 'side': tradeSide,
1067
- 'fee': {
1068
- 'cost': self.parse_number(feeCost),
1069
- 'currency': market['quote'],
1070
- },
1071
- 'info': item,
1072
- 'type': None,
1073
- 'takerOrMaker': None,
1074
- })
1075
- return self.safe_order({
1076
- 'info': order,
1077
- 'id': orderId,
1078
- 'clientOrderId': None,
1079
- 'datetime': self.iso8601(timestamp),
1080
- 'timestamp': timestamp,
1081
- 'lastTradeTimestamp': None,
1082
- 'status': status,
1083
- 'symbol': symbol,
1084
- 'type': 'market' if (price is None) else 'limit',
1085
- 'timeInForce': None,
1086
- 'postOnly': None,
1087
- 'side': side,
1088
- 'price': price,
1089
- 'stopPrice': None,
1090
- 'triggerPrice': None,
1091
- 'cost': cost,
1092
- 'amount': amount,
1093
- 'filled': filled,
1094
- 'remaining': remaining,
1095
- 'trades': trades,
1096
- 'fee': fee,
1097
- 'average': None,
1098
- })
1279
+ data = self.safe_dict(response, 'data', {})
1280
+ ids = self.safe_list(data, 'clientOrderIds', [])
1281
+ orders = []
1282
+ for i in range(0, len(ids)):
1283
+ id = ids[i]
1284
+ orders.append({'clientOrderId': id})
1285
+ return self.parse_orders(orders)
1099
1286
 
1100
- async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1287
+ async def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[LedgerEntry]:
1101
1288
  """
1102
- :see: https://docs.cex.io/#open-orders
1103
- fetch all unfilled currently open orders
1104
- :param str symbol: unified market symbol
1105
- :param int [since]: the earliest time in ms to fetch open orders for
1106
- :param int [limit]: the maximum number of open orders structures to retrieve
1289
+ fetch the history of changes, actions done by the user or operations that altered the balance of the user
1290
+
1291
+ https://trade.cex.io/docs/#rest-private-api-calls-transaction-history
1292
+
1293
+ :param str [code]: unified currency code
1294
+ :param int [since]: timestamp in ms of the earliest ledger entry
1295
+ :param int [limit]: max number of ledger entries to return
1107
1296
  :param dict [params]: extra parameters specific to the exchange API endpoint
1108
- :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1297
+ :param int [params.until]: timestamp in ms of the latest ledger entry
1298
+ :returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger>`
1109
1299
  """
1110
1300
  await self.load_markets()
1111
- request = {}
1112
- market = None
1113
- orders = None
1114
- if symbol is not None:
1115
- market = self.market(symbol)
1116
- request['pair'] = market['id']
1117
- orders = await self.privatePostOpenOrdersPair(self.extend(request, params))
1301
+ currency = None
1302
+ request: dict = {}
1303
+ if code is not None:
1304
+ currency = self.currency(code)
1305
+ request['currency'] = currency['id']
1306
+ if since is not None:
1307
+ request['dateFrom'] = since
1308
+ if limit is not None:
1309
+ request['pageSize'] = limit
1310
+ until = None
1311
+ until, params = self.handle_param_integer_2(params, 'until', 'till')
1312
+ if until is not None:
1313
+ request['dateTo'] = until
1314
+ response = await self.privatePostGetMyTransactionHistory(self.extend(request, params))
1315
+ #
1316
+ # {
1317
+ # "ok": "ok",
1318
+ # "data": [
1319
+ # {
1320
+ # "transactionId": "30367722",
1321
+ # "timestamp": "2024-10-14T14:08:49.987Z",
1322
+ # "accountId": "",
1323
+ # "type": "withdraw",
1324
+ # "amount": "-12.39060600",
1325
+ # "details": "Withdraw fundingId=1235039 clientId=up421412345 walletTxId=76337154166",
1326
+ # "currency": "USDT"
1327
+ # },
1328
+ # ...
1329
+ #
1330
+ data = self.safe_list(response, 'data', [])
1331
+ return self.parse_ledger(data, currency, since, limit)
1332
+
1333
+ def parse_ledger_entry(self, item: dict, currency: Currency = None) -> LedgerEntry:
1334
+ amount = self.safe_string(item, 'amount')
1335
+ direction = None
1336
+ if Precise.string_le(amount, '0'):
1337
+ direction = 'out'
1338
+ amount = Precise.string_mul('-1', amount)
1118
1339
  else:
1119
- orders = await self.privatePostOpenOrders(self.extend(request, params))
1120
- for i in range(0, len(orders)):
1121
- orders[i] = self.extend(orders[i], {'status': 'open'})
1122
- return self.parse_orders(orders, market, since, limit)
1340
+ direction = 'in'
1341
+ currencyId = self.safe_string(item, 'currency')
1342
+ currency = self.safe_currency(currencyId, currency)
1343
+ code = self.safe_currency_code(currencyId, currency)
1344
+ timestampString = self.safe_string(item, 'timestamp')
1345
+ timestamp = self.parse8601(timestampString)
1346
+ type = self.safe_string(item, 'type')
1347
+ return self.safe_ledger_entry({
1348
+ 'info': item,
1349
+ 'id': self.safe_string(item, 'transactionId'),
1350
+ 'direction': direction,
1351
+ 'account': self.safe_string(item, 'accountId', ''),
1352
+ 'referenceAccount': None,
1353
+ 'referenceId': None,
1354
+ 'type': self.parse_ledger_entry_type(type),
1355
+ 'currency': code,
1356
+ 'amount': self.parse_number(amount),
1357
+ 'timestamp': timestamp,
1358
+ 'datetime': self.iso8601(timestamp),
1359
+ 'before': None,
1360
+ 'after': None,
1361
+ 'status': None,
1362
+ 'fee': None,
1363
+ }, currency)
1123
1364
 
1124
- async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1365
+ def parse_ledger_entry_type(self, type):
1366
+ ledgerType: dict = {
1367
+ 'deposit': 'deposit',
1368
+ 'withdraw': 'withdrawal',
1369
+ 'commission': 'fee',
1370
+ }
1371
+ return self.safe_string(ledgerType, type, type)
1372
+
1373
+ async def fetch_deposits_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
1125
1374
  """
1126
- :see: https://docs.cex.io/#archived-orders
1127
- fetches information on multiple closed orders made by the user
1128
- :param str symbol: unified market symbol of the market orders were made in
1129
- :param int [since]: the earliest time in ms to fetch orders for
1130
- :param int [limit]: the maximum number of order structures to retrieve
1375
+ fetch history of deposits and withdrawals
1376
+
1377
+ https://trade.cex.io/docs/#rest-private-api-calls-funding-history
1378
+
1379
+ :param str [code]: unified currency code for the currency of the deposit/withdrawals, default is None
1380
+ :param int [since]: timestamp in ms of the earliest deposit/withdrawal, default is None
1381
+ :param int [limit]: max number of deposit/withdrawals to return, default is None
1131
1382
  :param dict [params]: extra parameters specific to the exchange API endpoint
1132
- :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1383
+ :returns dict: a list of `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
1133
1384
  """
1134
- if symbol is None:
1135
- raise ArgumentsRequired(self.id + ' fetchClosedOrders() requires a symbol argument')
1136
1385
  await self.load_markets()
1137
- market = self.market(symbol)
1138
- request = {'pair': market['id']}
1139
- response = await self.privatePostArchivedOrdersPair(self.extend(request, params))
1140
- return self.parse_orders(response, market, since, limit)
1386
+ request: dict = {}
1387
+ currency = None
1388
+ if code is not None:
1389
+ currency = self.currency(code)
1390
+ if since is not None:
1391
+ request['dateFrom'] = since
1392
+ if limit is not None:
1393
+ request['pageSize'] = limit
1394
+ until = None
1395
+ until, params = self.handle_param_integer_2(params, 'until', 'till')
1396
+ if until is not None:
1397
+ request['dateTo'] = until
1398
+ response = await self.privatePostGetMyFundingHistory(self.extend(request, params))
1399
+ #
1400
+ # {
1401
+ # "ok": "ok",
1402
+ # "data": [
1403
+ # {
1404
+ # "clientId": "up421412345",
1405
+ # "accountId": "",
1406
+ # "currency": "USDT",
1407
+ # "direction": "withdraw",
1408
+ # "amount": "12.39060600",
1409
+ # "commissionAmount": "0.00000000",
1410
+ # "status": "approved",
1411
+ # "updatedAt": "2024-10-14T14:08:50.013Z",
1412
+ # "txId": "30367718",
1413
+ # "details": {}
1414
+ # },
1415
+ # ...
1416
+ #
1417
+ data = self.safe_list(response, 'data', [])
1418
+ return self.parse_transactions(data, currency, since, limit)
1141
1419
 
1142
- async def fetch_order(self, id: str, symbol: Str = None, params={}):
1420
+ def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
1421
+ currencyId = self.safe_string(transaction, 'currency')
1422
+ direction = self.safe_string(transaction, 'direction')
1423
+ type = 'withdrawal' if (direction == 'withdraw') else 'deposit'
1424
+ code = self.safe_currency_code(currencyId, currency)
1425
+ updatedAt = self.safe_string(transaction, 'updatedAt')
1426
+ timestamp = self.parse8601(updatedAt)
1427
+ return {
1428
+ 'info': transaction,
1429
+ 'id': self.safe_string(transaction, 'txId'),
1430
+ 'txid': None,
1431
+ 'type': type,
1432
+ 'currency': code,
1433
+ 'network': None,
1434
+ 'amount': self.safe_number(transaction, 'amount'),
1435
+ 'status': self.parse_transaction_status(self.safe_string(transaction, 'status')),
1436
+ 'timestamp': timestamp,
1437
+ 'datetime': self.iso8601(timestamp),
1438
+ 'address': None,
1439
+ 'addressFrom': None,
1440
+ 'addressTo': None,
1441
+ 'tag': None,
1442
+ 'tagFrom': None,
1443
+ 'tagTo': None,
1444
+ 'updated': None,
1445
+ 'comment': None,
1446
+ 'fee': {
1447
+ 'currency': code,
1448
+ 'cost': self.safe_number(transaction, 'commissionAmount'),
1449
+ },
1450
+ 'internal': None,
1451
+ }
1452
+
1453
+ def parse_transaction_status(self, status: Str):
1454
+ statuses: dict = {
1455
+ 'rejected': 'rejected',
1456
+ 'pending': 'pending',
1457
+ 'approved': 'ok',
1458
+ }
1459
+ return self.safe_string(statuses, status, status)
1460
+
1461
+ async def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
1143
1462
  """
1144
- :see: https://docs.cex.io/?python#get-order-details
1145
- fetches information on an order made by the user
1146
- :param str symbol: not used by cex fetchOrder
1463
+ transfer currency internally between wallets on the same account
1464
+
1465
+ https://trade.cex.io/docs/#rest-private-api-calls-internal-transfer
1466
+
1467
+ :param str code: unified currency code
1468
+ :param float amount: amount to transfer
1469
+ :param str fromAccount: 'SPOT', 'FUND', or 'CONTRACT'
1470
+ :param str toAccount: 'SPOT', 'FUND', or 'CONTRACT'
1147
1471
  :param dict [params]: extra parameters specific to the exchange API endpoint
1148
- :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1472
+ :returns dict: a `transfer structure <https://docs.ccxt.com/#/?id=transfer-structure>`
1149
1473
  """
1474
+ transfer = None
1475
+ if toAccount != '' and fromAccount != '':
1476
+ transfer = await self.transfer_between_sub_accounts(code, amount, fromAccount, toAccount, params)
1477
+ else:
1478
+ transfer = await self.transfer_between_main_and_sub_account(code, amount, fromAccount, toAccount, params)
1479
+ fillResponseFromRequest = self.handle_option('transfer', 'fillResponseFromRequest', True)
1480
+ if fillResponseFromRequest:
1481
+ transfer['fromAccount'] = fromAccount
1482
+ transfer['toAccount'] = toAccount
1483
+ return transfer
1484
+
1485
+ async def transfer_between_main_and_sub_account(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
1150
1486
  await self.load_markets()
1151
- request = {
1152
- 'id': str(id),
1487
+ currency = self.currency(code)
1488
+ fromMain = (fromAccount == '')
1489
+ targetAccount = toAccount if fromMain else fromAccount
1490
+ guid = self.safe_string(params, 'guid', self.uuid())
1491
+ request: dict = {
1492
+ 'currency': currency['id'],
1493
+ 'amount': self.currency_to_precision(code, amount),
1494
+ 'accountId': targetAccount,
1495
+ 'clientTxId': guid,
1153
1496
  }
1154
- response = await self.privatePostGetOrderTx(self.extend(request, params))
1155
- data = self.safe_value(response, 'data', {})
1497
+ response = None
1498
+ if fromMain:
1499
+ response = await self.privatePostDoDepositFundsFromWallet(self.extend(request, params))
1500
+ else:
1501
+ response = await self.privatePostDoWithdrawalFundsToWallet(self.extend(request, params))
1502
+ # both endpoints return the same structure, the only difference is that
1503
+ # the "accountId" is filled with the "subAccount"
1156
1504
  #
1157
1505
  # {
1158
- # "id": "5442731603",
1159
- # "type": "sell",
1160
- # "time": 1516132358071,
1161
- # "lastTxTime": 1516132378452,
1162
- # "lastTx": "5442734452",
1163
- # "pos": null,
1164
- # "user": "up106404164",
1165
- # "status": "d",
1166
- # "symbol1": "ETH",
1167
- # "symbol2": "EUR",
1168
- # "amount": "0.50000000",
1169
- # "kind": "api",
1170
- # "price": "923.3386",
1171
- # "tfacf": "1",
1172
- # "fa:EUR": "0.55",
1173
- # "ta:EUR": "369.77",
1174
- # "remains": "0.00000000",
1175
- # "tfa:EUR": "0.22",
1176
- # "tta:EUR": "91.95",
1177
- # "a:ETH:cds": "0.50000000",
1178
- # "a:EUR:cds": "461.72",
1179
- # "f:EUR:cds": "0.77",
1180
- # "tradingFeeMaker": "0.15",
1181
- # "tradingFeeTaker": "0.23",
1182
- # "tradingFeeStrategy": "userVolumeAmount",
1183
- # "tradingFeeUserVolumeAmount": "2896912572",
1184
- # "orderId": "5442731603",
1185
- # "next": False,
1186
- # "vtx": [
1187
- # {
1188
- # "id": "5442734452",
1189
- # "type": "sell",
1190
- # "time": "2018-01-16T19:52:58.452Z",
1191
- # "user": "up106404164",
1192
- # "c": "user:up106404164:a:EUR",
1193
- # "d": "order:5442731603:a:EUR",
1194
- # "a": "104.53000000",
1195
- # "amount": "104.53000000",
1196
- # "balance": "932.71000000",
1197
- # "symbol": "EUR",
1198
- # "order": "5442731603",
1199
- # "buy": "5442734443",
1200
- # "sell": "5442731603",
1201
- # "pair": null,
1202
- # "pos": null,
1203
- # "office": null,
1204
- # "cs": "932.71",
1205
- # "ds": 0,
1206
- # "price": 923.3386,
1207
- # "symbol2": "ETH",
1208
- # "fee_amount": "0.16"
1209
- # },
1210
- # {
1211
- # "id": "5442731609",
1212
- # "type": "sell",
1213
- # "time": "2018-01-16T19:52:38.071Z",
1214
- # "user": "up106404164",
1215
- # "c": "user:up106404164:a:EUR",
1216
- # "d": "order:5442731603:a:EUR",
1217
- # "a": "91.73000000",
1218
- # "amount": "91.73000000",
1219
- # "balance": "563.49000000",
1220
- # "symbol": "EUR",
1221
- # "order": "5442731603",
1222
- # "buy": "5442618127",
1223
- # "sell": "5442731603",
1224
- # "pair": null,
1225
- # "pos": null,
1226
- # "office": null,
1227
- # "cs": "563.49",
1228
- # "ds": 0,
1229
- # "price": 924.0092,
1230
- # "symbol2": "ETH",
1231
- # "fee_amount": "0.22"
1232
- # },
1233
- # {
1234
- # "id": "5442731604",
1235
- # "type": "sell",
1236
- # "time": "2018-01-16T19:52:38.071Z",
1237
- # "user": "up106404164",
1238
- # "c": "order:5442731603:a:ETH",
1239
- # "d": "user:up106404164:a:ETH",
1240
- # "a": "0.50000000",
1241
- # "amount": "-0.50000000",
1242
- # "balance": "15.80995000",
1243
- # "symbol": "ETH",
1244
- # "order": "5442731603",
1245
- # "buy": null,
1246
- # "sell": null,
1247
- # "pair": null,
1248
- # "pos": null,
1249
- # "office": null,
1250
- # "cs": "0.50000000",
1251
- # "ds": "15.80995000"
1252
- # }
1253
- # ]
1506
+ # "ok": "ok",
1507
+ # "data": {
1508
+ # "accountId": "sub1",
1509
+ # "clientTxId": "27ba8284-67cf-4386-9ec7-80b3871abd45",
1510
+ # "currency": "USDT",
1511
+ # "status": "approved"
1512
+ # }
1254
1513
  # }
1255
1514
  #
1256
- return self.parse_order(data)
1515
+ data = self.safe_dict(response, 'data', {})
1516
+ return self.parse_transfer(data, currency)
1257
1517
 
1258
- async def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1259
- """
1260
- :see: https://docs.cex.io/#archived-orders
1261
- fetches information on multiple orders made by the user
1262
- :param str symbol: unified market symbol of the market orders were made in
1263
- :param int [since]: the earliest time in ms to fetch orders for
1264
- :param int [limit]: the maximum number of order structures to retrieve
1265
- :param dict [params]: extra parameters specific to the exchange API endpoint
1266
- :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1267
- """
1518
+ async def transfer_between_sub_accounts(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
1268
1519
  await self.load_markets()
1269
- market = self.market(symbol)
1270
- request = {
1271
- 'limit': limit,
1272
- 'pair': market['id'],
1273
- 'dateFrom': since,
1520
+ currency = self.currency(code)
1521
+ request: dict = {
1522
+ 'currency': currency['id'],
1523
+ 'amount': self.currency_to_precision(code, amount),
1524
+ 'fromAccountId': fromAccount,
1525
+ 'toAccountId': toAccount,
1274
1526
  }
1275
- response = await self.privatePostArchivedOrdersPair(self.extend(request, params))
1276
- results = []
1277
- for i in range(0, len(response)):
1278
- # cancelled(unfilled):
1279
- # {"id": "4005785516",
1280
- # "type": "sell",
1281
- # "time": "2017-07-18T19:08:34.223Z",
1282
- # "lastTxTime": "2017-07-18T19:08:34.396Z",
1283
- # "lastTx": "4005785522",
1284
- # "pos": null,
1285
- # "status": "c",
1286
- # "symbol1": "ETH",
1287
- # "symbol2": "GBP",
1288
- # "amount": "0.20000000",
1289
- # "price": "200.5625",
1290
- # "remains": "0.20000000",
1291
- # 'a:ETH:cds': "0.20000000",
1292
- # "tradingFeeMaker": "0",
1293
- # "tradingFeeTaker": "0.16",
1294
- # "tradingFeeUserVolumeAmount": "10155061217",
1295
- # "orderId": "4005785516"}
1296
- # --
1297
- # cancelled(partially filled buy):
1298
- # {"id": "4084911657",
1299
- # "type": "buy",
1300
- # "time": "2017-08-05T03:18:39.596Z",
1301
- # "lastTxTime": "2019-03-19T17:37:46.404Z",
1302
- # "lastTx": "8459265833",
1303
- # "pos": null,
1304
- # "status": "cd",
1305
- # "symbol1": "BTC",
1306
- # "symbol2": "GBP",
1307
- # "amount": "0.05000000",
1308
- # "price": "2241.4692",
1309
- # "tfacf": "1",
1310
- # "remains": "0.03910535",
1311
- # 'tfa:GBP': "0.04",
1312
- # 'tta:GBP': "24.39",
1313
- # 'a:BTC:cds': "0.01089465",
1314
- # 'a:GBP:cds': "112.26",
1315
- # 'f:GBP:cds': "0.04",
1316
- # "tradingFeeMaker": "0",
1317
- # "tradingFeeTaker": "0.16",
1318
- # "tradingFeeUserVolumeAmount": "13336396963",
1319
- # "orderId": "4084911657"}
1320
- # --
1321
- # cancelled(partially filled sell):
1322
- # {"id": "4426728375",
1323
- # "type": "sell",
1324
- # "time": "2017-09-22T00:24:20.126Z",
1325
- # "lastTxTime": "2017-09-22T00:24:30.476Z",
1326
- # "lastTx": "4426729543",
1327
- # "pos": null,
1328
- # "status": "cd",
1329
- # "symbol1": "BCH",
1330
- # "symbol2": "BTC",
1331
- # "amount": "0.10000000",
1332
- # "price": "0.11757182",
1333
- # "tfacf": "1",
1334
- # "remains": "0.09935956",
1335
- # 'tfa:BTC': "0.00000014",
1336
- # 'tta:BTC': "0.00007537",
1337
- # 'a:BCH:cds': "0.10000000",
1338
- # 'a:BTC:cds': "0.00007537",
1339
- # 'f:BTC:cds': "0.00000014",
1340
- # "tradingFeeMaker": "0",
1341
- # "tradingFeeTaker": "0.18",
1342
- # "tradingFeeUserVolumeAmount": "3466715450",
1343
- # "orderId": "4426728375"}
1344
- # --
1345
- # filled:
1346
- # {"id": "5342275378",
1347
- # "type": "sell",
1348
- # "time": "2018-01-04T00:28:12.992Z",
1349
- # "lastTxTime": "2018-01-04T00:28:12.992Z",
1350
- # "lastTx": "5342275393",
1351
- # "pos": null,
1352
- # "status": "d",
1353
- # "symbol1": "BCH",
1354
- # "symbol2": "BTC",
1355
- # "amount": "0.10000000",
1356
- # "kind": "api",
1357
- # "price": "0.17",
1358
- # "remains": "0.00000000",
1359
- # 'tfa:BTC': "0.00003902",
1360
- # 'tta:BTC': "0.01699999",
1361
- # 'a:BCH:cds': "0.10000000",
1362
- # 'a:BTC:cds': "0.01699999",
1363
- # 'f:BTC:cds': "0.00003902",
1364
- # "tradingFeeMaker": "0.15",
1365
- # "tradingFeeTaker": "0.23",
1366
- # "tradingFeeUserVolumeAmount": "1525951128",
1367
- # "orderId": "5342275378"}
1368
- # --
1369
- # market order(buy):
1370
- # {"id": "6281946200",
1371
- # "pos": null,
1372
- # "time": "2018-05-23T11:55:43.467Z",
1373
- # "type": "buy",
1374
- # "amount": "0.00000000",
1375
- # "lastTx": "6281946210",
1376
- # "status": "d",
1377
- # "amount2": "20.00",
1378
- # "orderId": "6281946200",
1379
- # "remains": "0.00000000",
1380
- # "symbol1": "ETH",
1381
- # "symbol2": "EUR",
1382
- # "tfa:EUR": "0.05",
1383
- # "tta:EUR": "19.94",
1384
- # "a:ETH:cds": "0.03764100",
1385
- # "a:EUR:cds": "20.00",
1386
- # "f:EUR:cds": "0.05",
1387
- # "lastTxTime": "2018-05-23T11:55:43.467Z",
1388
- # "tradingFeeTaker": "0.25",
1389
- # "tradingFeeUserVolumeAmount": "55998097"}
1390
- # --
1391
- # market order(sell):
1392
- # {"id": "6282200948",
1393
- # "pos": null,
1394
- # "time": "2018-05-23T12:42:58.315Z",
1395
- # "type": "sell",
1396
- # "amount": "-0.05000000",
1397
- # "lastTx": "6282200958",
1398
- # "status": "d",
1399
- # "orderId": "6282200948",
1400
- # "remains": "0.00000000",
1401
- # "symbol1": "ETH",
1402
- # "symbol2": "EUR",
1403
- # "tfa:EUR": "0.07",
1404
- # "tta:EUR": "26.49",
1405
- # "a:ETH:cds": "0.05000000",
1406
- # "a:EUR:cds": "26.49",
1407
- # "f:EUR:cds": "0.07",
1408
- # "lastTxTime": "2018-05-23T12:42:58.315Z",
1409
- # "tradingFeeTaker": "0.25",
1410
- # "tradingFeeUserVolumeAmount": "56294576"}
1411
- order = response[i]
1412
- status = self.parse_order_status(self.safe_string(order, 'status'))
1413
- baseId = self.safe_string(order, 'symbol1')
1414
- quoteId = self.safe_string(order, 'symbol2')
1415
- base = self.safe_currency_code(baseId)
1416
- quote = self.safe_currency_code(quoteId)
1417
- symbolInner = base + '/' + quote
1418
- side = self.safe_string(order, 'type')
1419
- baseAmount = self.safe_number(order, 'a:' + baseId + ':cds')
1420
- quoteAmount = self.safe_number(order, 'a:' + quoteId + ':cds')
1421
- fee = self.safe_number(order, 'f:' + quoteId + ':cds')
1422
- amount = self.safe_string(order, 'amount')
1423
- price = self.safe_string(order, 'price')
1424
- remaining = self.safe_string(order, 'remains')
1425
- filled = Precise.string_sub(amount, remaining)
1426
- orderAmount = None
1427
- cost = None
1428
- average = None
1429
- type = None
1430
- if not price:
1431
- type = 'market'
1432
- orderAmount = baseAmount
1433
- cost = quoteAmount
1434
- average = Precise.string_div(orderAmount, cost)
1435
- else:
1436
- ta = self.safe_string(order, 'ta:' + quoteId, '0')
1437
- tta = self.safe_string(order, 'tta:' + quoteId, '0')
1438
- fa = self.safe_string(order, 'fa:' + quoteId, '0')
1439
- tfa = self.safe_string(order, 'tfa:' + quoteId, '0')
1440
- if side == 'sell':
1441
- cost = Precise.string_add(Precise.string_add(ta, tta), Precise.string_add(fa, tfa))
1442
- else:
1443
- cost = Precise.string_sub(Precise.string_add(ta, tta), Precise.string_add(fa, tfa))
1444
- type = 'limit'
1445
- orderAmount = amount
1446
- average = Precise.string_div(cost, filled)
1447
- time = self.safe_string(order, 'time')
1448
- lastTxTime = self.safe_string(order, 'lastTxTime')
1449
- timestamp = self.parse8601(time)
1450
- safeOrder = self.safe_order({
1451
- 'info': order,
1452
- 'id': self.safe_string(order, 'id'),
1453
- 'timestamp': timestamp,
1454
- 'datetime': self.iso8601(timestamp),
1455
- 'lastUpdated': self.parse8601(lastTxTime),
1456
- 'status': status,
1457
- 'symbol': symbolInner,
1458
- 'side': side,
1459
- 'price': price,
1460
- 'amount': orderAmount,
1461
- 'average': average,
1462
- 'type': type,
1463
- 'filled': filled,
1464
- 'cost': cost,
1465
- 'remaining': remaining,
1466
- 'fee': {
1467
- 'cost': fee,
1468
- 'currency': quote,
1469
- },
1470
- })
1471
- results.append(safeOrder)
1472
- return results
1473
-
1474
- def parse_order_status(self, status):
1475
- return self.safe_string(self.options['order']['status'], status, status)
1527
+ response = await self.privatePostDoMyInternalTransfer(self.extend(request, params))
1528
+ #
1529
+ # {
1530
+ # "ok": "ok",
1531
+ # "data": {
1532
+ # "transactionId": "30225415"
1533
+ # }
1534
+ # }
1535
+ #
1536
+ data = self.safe_dict(response, 'data', {})
1537
+ return self.parse_transfer(data, currency)
1476
1538
 
1477
- async def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
1478
- """
1479
- edit a trade order
1480
- :see: https://docs.cex.io/#cancel-replace-order
1481
- :param str id: order id
1482
- :param str symbol: unified symbol of the market to create an order in
1483
- :param str type: 'market' or 'limit'
1484
- :param str side: 'buy' or 'sell'
1485
- :param float amount: how much of the currency you want to trade in units of the base currency
1486
- :param float|None [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
1487
- :param dict [params]: extra parameters specific to the cex api endpoint
1488
- :returns dict: an `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
1489
- """
1490
- if amount is None:
1491
- raise ArgumentsRequired(self.id + ' editOrder() requires a amount argument')
1492
- if price is None:
1493
- raise ArgumentsRequired(self.id + ' editOrder() requires a price argument')
1494
- await self.load_markets()
1495
- market = self.market(symbol)
1496
- # see: https://cex.io/rest-api#/definitions/CancelReplaceOrderRequest
1497
- request = {
1498
- 'pair': market['id'],
1499
- 'type': side,
1500
- 'amount': amount,
1501
- 'price': price,
1502
- 'order_id': id,
1539
+ def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
1540
+ #
1541
+ # transferBetweenSubAccounts
1542
+ #
1543
+ # {
1544
+ # "ok": "ok",
1545
+ # "data": {
1546
+ # "transactionId": "30225415"
1547
+ # }
1548
+ # }
1549
+ #
1550
+ # transfer between main/sub
1551
+ #
1552
+ # {
1553
+ # "ok": "ok",
1554
+ # "data": {
1555
+ # "accountId": "sub1",
1556
+ # "clientTxId": "27ba8284-67cf-4386-9ec7-80b3871abd45",
1557
+ # "currency": "USDT",
1558
+ # "status": "approved"
1559
+ # }
1560
+ # }
1561
+ #
1562
+ currencyId = self.safe_string(transfer, 'currency')
1563
+ currencyCode = self.safe_currency_code(currencyId, currency)
1564
+ return {
1565
+ 'info': transfer,
1566
+ 'id': self.safe_string_2(transfer, 'transactionId', 'clientTxId'),
1567
+ 'timestamp': None,
1568
+ 'datetime': None,
1569
+ 'currency': currencyCode,
1570
+ 'amount': None,
1571
+ 'fromAccount': None,
1572
+ 'toAccount': None,
1573
+ 'status': self.parse_transaction_status(self.safe_string(transfer, 'status')),
1503
1574
  }
1504
- response = await self.privatePostCancelReplaceOrderPair(self.extend(request, params))
1505
- return self.parse_order(response, market)
1506
1575
 
1507
- async def fetch_deposit_address(self, code: str, params={}):
1576
+ async def fetch_deposit_address(self, code: str, params={}) -> DepositAddress:
1508
1577
  """
1509
- :see: https://docs.cex.io/#get-crypto-address
1510
1578
  fetch the deposit address for a currency associated with self account
1579
+
1580
+ https://trade.cex.io/docs/#rest-private-api-calls-deposit-address
1581
+
1511
1582
  :param str code: unified currency code
1512
1583
  :param dict [params]: extra parameters specific to the exchange API endpoint
1584
+ :param str [params.accountId]: account-id(default to empty string) to refer to(at self moment, only sub-accounts allowed by exchange)
1513
1585
  :returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
1514
1586
  """
1587
+ accountId = None
1588
+ accountId, params = self.handle_option_and_params(params, 'createOrder', 'accountId')
1589
+ if accountId is None:
1590
+ raise ArgumentsRequired(self.id + ' fetchDepositAddress() : main account is not allowed to fetch deposit address from api, set params["accountId"] or .options["createOrder"]["accountId"] to the name of your sub-account')
1515
1591
  await self.load_markets()
1592
+ networkCode = None
1593
+ networkCode, params = self.handle_network_code_and_params(params)
1516
1594
  currency = self.currency(code)
1517
- request = {
1518
- 'currency': currency['id'],
1595
+ request: dict = {
1596
+ 'accountId': accountId,
1597
+ 'currency': currency['id'], # documentation is wrong about self param
1598
+ 'blockchain': self.network_code_to_id(networkCode),
1519
1599
  }
1520
- networkCode, query = self.handle_network_code_and_params(params)
1521
- # atm, cex doesn't support network in the request
1522
- response = await self.privatePostGetCryptoAddress(self.extend(request, query))
1600
+ response = await self.privatePostGetDepositAddress(self.extend(request, params))
1523
1601
  #
1524
1602
  # {
1525
- # "e": "get_crypto_address",
1526
- # "ok": "ok",
1527
- # "data": {
1528
- # "name": "BTC",
1529
- # "addresses": [
1530
- # {
1531
- # "blockchain": "Bitcoin",
1532
- # "address": "2BvKwe1UwrdTjq2nzhscFYXwqCjCaaHCeq"
1533
- #
1534
- # # for others coins(i.e. XRP, XLM) other keys are present:
1535
- # # "destination": "rF1sdh25BJX3qFwneeTBwaq3zPEWYcwjp2",
1536
- # # "destinationTag": "7519113655",
1537
- # # "memo": "XLM-memo12345",
1538
- # }
1539
- # ]
1540
- # }
1541
- # }
1603
+ # "ok": "ok",
1604
+ # "data": {
1605
+ # "address": "TCr..................1AE",
1606
+ # "accountId": "sub1",
1607
+ # "currency": "USDT",
1608
+ # "blockchain": "tron"
1609
+ # }
1610
+ # }
1542
1611
  #
1543
- data = self.safe_value(response, 'data', {})
1544
- addresses = self.safe_value(data, 'addresses', [])
1545
- chainsIndexedById = self.index_by(addresses, 'blockchain')
1546
- selectedNetworkId = self.select_network_id_from_raw_networks(code, networkCode, chainsIndexedById)
1547
- addressObject = self.safe_value(chainsIndexedById, selectedNetworkId, {})
1548
- address = self.safe_string_2(addressObject, 'address', 'destination')
1612
+ data = self.safe_dict(response, 'data', {})
1613
+ return self.parse_deposit_address(data, currency)
1614
+
1615
+ def parse_deposit_address(self, depositAddress, currency: Currency = None) -> DepositAddress:
1616
+ address = self.safe_string(depositAddress, 'address')
1617
+ currencyId = self.safe_string(depositAddress, 'currency')
1618
+ currency = self.safe_currency(currencyId, currency)
1549
1619
  self.check_address(address)
1550
1620
  return {
1551
- 'currency': code,
1621
+ 'info': depositAddress,
1622
+ 'currency': currency['code'],
1623
+ 'network': self.network_id_to_code(self.safe_string(depositAddress, 'blockchain')),
1552
1624
  'address': address,
1553
- 'tag': self.safe_string_2(addressObject, 'destinationTag', 'memo'),
1554
- 'network': self.network_id_to_code(selectedNetworkId),
1555
- 'info': data,
1625
+ 'tag': None,
1556
1626
  }
1557
1627
 
1558
- def nonce(self):
1559
- return self.milliseconds()
1560
-
1561
1628
  def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
1562
- url = self.urls['api']['rest'] + '/' + self.implode_params(path, params)
1629
+ url = self.urls['api'][api] + '/' + self.implode_params(path, params)
1563
1630
  query = self.omit(params, self.extract_params(path))
1564
1631
  if api == 'public':
1565
- if query:
1566
- url += '?' + self.urlencode(query)
1632
+ if method == 'GET':
1633
+ if query:
1634
+ url += '?' + self.urlencode(query)
1635
+ else:
1636
+ body = self.json(query)
1637
+ headers = {
1638
+ 'Content-Type': 'application/json',
1639
+ }
1567
1640
  else:
1568
1641
  self.check_required_credentials()
1569
- nonce = str(self.nonce())
1570
- auth = nonce + self.uid + self.apiKey
1571
- signature = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha256)
1572
- body = self.json(self.extend({
1573
- 'key': self.apiKey,
1574
- 'signature': signature.upper(),
1575
- 'nonce': nonce,
1576
- }, query))
1642
+ seconds = str(self.seconds())
1643
+ body = self.json(query)
1644
+ auth = path + seconds + body
1645
+ signature = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha256, 'base64')
1577
1646
  headers = {
1578
1647
  'Content-Type': 'application/json',
1648
+ 'X-AGGR-KEY': self.apiKey,
1649
+ 'X-AGGR-TIMESTAMP': seconds,
1650
+ 'X-AGGR-SIGNATURE': signature,
1579
1651
  }
1580
1652
  return {'url': url, 'method': method, 'body': body, 'headers': headers}
1581
1653
 
1582
- def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
1583
- if isinstance(response, list):
1584
- return response # public endpoints may return []-arrays
1585
- if body == 'true':
1586
- return None
1654
+ def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
1655
+ # in some cases, like from createOrder, exchange returns nested escaped JSON string:
1656
+ # {"ok":"ok","data":{"messageType":"executionReport", "orderRejectReason":"{\"code\":405}"}}
1657
+ # and because of `.parseJson` bug, we need extra fix
1587
1658
  if response is None:
1588
- raise NullResponse(self.id + ' returned ' + self.json(response))
1589
- if 'e' in response:
1590
- if 'ok' in response:
1591
- if response['ok'] == 'ok':
1592
- return None
1593
- if 'error' in response:
1594
- message = self.safe_string(response, 'error')
1659
+ if body is None:
1660
+ raise NullResponse(self.id + ' returned empty response')
1661
+ elif body[0] == '{':
1662
+ fixed = self.fix_stringified_json_members(body)
1663
+ response = self.parse_json(fixed)
1664
+ else:
1665
+ raise NullResponse(self.id + ' returned unparsed response: ' + body)
1666
+ error = self.safe_string(response, 'error')
1667
+ if error is not None:
1595
1668
  feedback = self.id + ' ' + body
1596
- self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
1597
- self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
1669
+ self.throw_exactly_matched_exception(self.exceptions['exact'], error, feedback)
1670
+ self.throw_broadly_matched_exception(self.exceptions['broad'], error, feedback)
1598
1671
  raise ExchangeError(feedback)
1672
+ # check errors in order-engine(the responses are not standard, so we parse here)
1673
+ if url.find('do_my_new_order') >= 0:
1674
+ data = self.safe_dict(response, 'data', {})
1675
+ rejectReason = self.safe_string(data, 'rejectReason')
1676
+ if rejectReason is not None:
1677
+ self.throw_broadly_matched_exception(self.exceptions['broad'], rejectReason, rejectReason)
1678
+ raise ExchangeError(self.id + ' createOrder() ' + rejectReason)
1599
1679
  return None