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/cex.py CHANGED
@@ -6,20 +6,15 @@
6
6
  from ccxt.base.exchange import Exchange
7
7
  from ccxt.abstract.cex import ImplicitAPI
8
8
  import hashlib
9
- import json
10
- from ccxt.base.types import Balances, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade
9
+ 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
10
  from typing import List
12
11
  from ccxt.base.errors import ExchangeError
12
+ from ccxt.base.errors import AuthenticationError
13
+ from ccxt.base.errors import PermissionDenied
13
14
  from ccxt.base.errors import ArgumentsRequired
14
- from ccxt.base.errors import BadSymbol
15
- from ccxt.base.errors import NullResponse
15
+ from ccxt.base.errors import BadRequest
16
16
  from ccxt.base.errors import InsufficientFunds
17
- from ccxt.base.errors import InvalidOrder
18
- from ccxt.base.errors import OrderNotFound
19
- from ccxt.base.errors import DDoSProtection
20
- from ccxt.base.errors import RateLimitExceeded
21
- from ccxt.base.errors import InvalidNonce
22
- from ccxt.base.errors import AuthenticationError
17
+ from ccxt.base.errors import NullResponse
23
18
  from ccxt.base.decimal_to_precision import TICK_SIZE
24
19
  from ccxt.base.precise import Precise
25
20
 
@@ -31,1569 +26,1653 @@ class cex(Exchange, ImplicitAPI):
31
26
  'id': 'cex',
32
27
  'name': 'CEX.IO',
33
28
  'countries': ['GB', 'EU', 'CY', 'RU'],
34
- 'rateLimit': 1500,
29
+ 'rateLimit': 300, # 200 req/min
35
30
  'pro': True,
36
31
  'has': {
37
32
  'CORS': None,
38
33
  'spot': True,
39
- 'margin': False, # has but not through api
34
+ 'margin': False, # has, but not through api
40
35
  'swap': False,
41
36
  'future': False,
42
37
  'option': False,
43
- 'addMargin': False,
44
38
  'cancelAllOrders': True,
45
39
  'cancelOrder': True,
46
- 'cancelOrders': False,
47
- 'createDepositAddress': False,
48
- 'createMarketBuyOrderWithCost': True,
49
- 'createMarketOrderWithCost': False,
50
- 'createMarketSellOrderWithCost': False,
51
40
  'createOrder': True,
52
- 'createStopLimitOrder': False,
53
- 'createStopMarketOrder': False,
54
- 'createStopOrder': False,
55
- 'editOrder': True,
41
+ 'createStopOrder': True,
42
+ 'createTriggerOrder': True,
43
+ 'fetchAccounts': True,
56
44
  'fetchBalance': True,
45
+ 'fetchClosedOrder': True,
57
46
  'fetchClosedOrders': True,
58
47
  'fetchCurrencies': True,
59
- 'fetchDeposit': False,
60
48
  'fetchDepositAddress': True,
61
- 'fetchDepositAddresses': False,
62
- 'fetchDeposits': False,
63
- 'fetchDepositsWithdrawals': False,
49
+ 'fetchDepositsWithdrawals': True,
64
50
  'fetchFundingHistory': False,
65
51
  'fetchFundingRate': False,
66
52
  'fetchFundingRateHistory': False,
67
53
  'fetchFundingRates': False,
68
- 'fetchIndexOHLCV': False,
69
- 'fetchMarginMode': False,
54
+ 'fetchLedger': True,
70
55
  'fetchMarkets': True,
71
- 'fetchMarkOHLCV': False,
72
56
  'fetchOHLCV': True,
73
- 'fetchOpenInterestHistory': False,
57
+ 'fetchOpenOrder': True,
74
58
  'fetchOpenOrders': True,
75
- 'fetchOrder': True,
76
59
  'fetchOrderBook': True,
77
- 'fetchOrders': True,
78
- 'fetchPositionMode': False,
79
- 'fetchPremiumIndexOHLCV': False,
80
60
  'fetchTicker': True,
81
61
  'fetchTickers': True,
62
+ 'fetchTime': True,
82
63
  'fetchTrades': True,
83
- 'fetchTradingFee': False,
84
64
  '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',
65
+ 'transfer': True,
102
66
  },
103
67
  'urls': {
104
68
  'logo': 'https://user-images.githubusercontent.com/1294454/27766442-8ddc33b0-5ed8-11e7-8b98-f786aef0f3c9.jpg',
105
69
  'api': {
106
- 'rest': 'https://cex.io/api',
70
+ 'public': 'https://trade.cex.io/api/spot/rest-public',
71
+ 'private': 'https://trade.cex.io/api/spot/rest',
107
72
  },
108
73
  'www': 'https://cex.io',
109
- 'doc': 'https://cex.io/cex-api',
74
+ 'doc': 'https://trade.cex.io/docs/',
110
75
  'fees': [
111
76
  'https://cex.io/fee-schedule',
112
77
  'https://cex.io/limits-commissions',
113
78
  ],
114
79
  'referral': 'https://cex.io/r/0/up105393824/0/',
115
80
  },
116
- 'requiredCredentials': {
117
- 'apiKey': True,
118
- 'secret': True,
119
- 'uid': True,
120
- },
121
81
  'api': {
122
82
  '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
- ],
83
+ 'get': {},
84
+ 'post': {
85
+ 'get_server_time': 1,
86
+ 'get_pairs_info': 1,
87
+ 'get_currencies_info': 1,
88
+ 'get_processing_info': 10,
89
+ 'get_ticker': 1,
90
+ 'get_trade_history': 1,
91
+ 'get_order_book': 1,
92
+ 'get_candles': 1,
93
+ },
138
94
  },
139
95
  '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
- ],
96
+ 'get': {},
97
+ 'post': {
98
+ 'get_my_current_fee': 5,
99
+ 'get_fee_strategy': 1,
100
+ 'get_my_volume': 5,
101
+ 'do_create_account': 1,
102
+ 'get_my_account_status_v3': 5,
103
+ 'get_my_wallet_balance': 5,
104
+ 'get_my_orders': 5,
105
+ 'do_my_new_order': 1,
106
+ 'do_cancel_my_order': 1,
107
+ 'do_cancel_all_orders': 5,
108
+ 'get_order_book': 1,
109
+ 'get_candles': 1,
110
+ 'get_trade_history': 1,
111
+ 'get_my_transaction_history': 1,
112
+ 'get_my_funding_history': 5,
113
+ 'do_my_internal_transfer': 1,
114
+ 'get_processing_info': 10,
115
+ 'get_deposit_address': 5,
116
+ 'do_deposit_funds_from_wallet': 1,
117
+ 'do_withdrawal_funds_to_wallet': 1,
118
+ },
160
119
  },
161
120
  },
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,
121
+ 'features': {
122
+ 'spot': {
123
+ 'sandbox': False,
124
+ 'createOrder': {
125
+ 'marginMode': False,
126
+ 'triggerPrice': True,
127
+ 'triggerPriceType': None,
128
+ 'triggerDirection': False,
129
+ 'stopLossPrice': False, # todo
130
+ 'takeProfitPrice': False, # todo
131
+ 'attachedStopLossTakeProfit': None,
132
+ 'timeInForce': {
133
+ 'IOC': True,
134
+ 'FOK': True,
135
+ 'PO': False, # todo check
136
+ 'GTD': True,
137
+ },
138
+ 'hedged': False,
139
+ 'leverage': False,
140
+ 'marketBuyRequiresPrice': False,
141
+ 'marketBuyByCost': True, # todo check
142
+ 'selfTradePrevention': False,
143
+ 'trailing': False,
144
+ 'iceberg': False,
145
+ },
146
+ 'createOrders': None,
147
+ 'fetchMyTrades': None,
148
+ 'fetchOrder': None,
149
+ 'fetchOpenOrders': {
150
+ 'marginMode': False,
151
+ 'limit': 1000,
152
+ 'trigger': False,
153
+ 'trailing': False,
182
154
  },
155
+ 'fetchOrders': None,
156
+ 'fetchClosedOrders': {
157
+ 'marginMode': False,
158
+ 'limit': 1000,
159
+ 'daysBack': 100000,
160
+ 'daysBackCanceled': 1,
161
+ 'untilDays': 100000,
162
+ 'trigger': False,
163
+ 'trailing': False,
164
+ },
165
+ 'fetchOHLCV': {
166
+ 'limit': 1000,
167
+ },
168
+ },
169
+ 'swap': {
170
+ 'linear': None,
171
+ 'inverse': None,
172
+ },
173
+ 'future': {
174
+ 'linear': None,
175
+ 'inverse': None,
183
176
  },
184
177
  },
185
178
  'precisionMode': TICK_SIZE,
186
179
  'exceptions': {
187
180
  'exact': {},
188
181
  'broad': {
182
+ 'You have negative balance on following accounts': InsufficientFunds,
183
+ 'Mandatory parameter side should be one of BUY,SELL': BadRequest,
184
+ 'API orders from Main account are not allowed': BadRequest,
185
+ 'check failed': BadRequest,
189
186
  '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}
187
+ 'Get deposit address for main account is not allowed': PermissionDenied,
188
+ 'Market Trigger orders are not allowed': BadRequest, # for some reason, triggerPrice does not work for market orders
189
+ 'key not passed or incorrect': AuthenticationError,
199
190
  },
200
191
  },
192
+ 'timeframes': {
193
+ '1m': '1m',
194
+ '5m': '5m',
195
+ '15m': '15m',
196
+ '30m': '30m',
197
+ '1h': '1h',
198
+ '2h': '2h',
199
+ '4h': '4h',
200
+ '1d': '1d',
201
+ },
201
202
  '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
203
  'networks': {
217
- 'ERC20': 'Ethereum',
218
- 'BTC': 'BTC',
219
- 'BEP20': 'Binance Smart Chain',
220
- 'TRC20': 'Tron',
204
+ 'BTC': 'bitcoin',
205
+ 'ERC20': 'ERC20',
206
+ 'BSC20': 'binancesmartchain',
207
+ 'DOGE': 'dogecoin',
208
+ 'ALGO': 'algorand',
209
+ 'XLM': 'stellar',
210
+ 'ATOM': 'cosmos',
211
+ 'LTC': 'litecoin',
212
+ 'XRP': 'ripple',
213
+ 'FTM': 'fantom',
214
+ 'MINA': 'mina',
215
+ 'THETA': 'theta',
216
+ 'XTZ': 'tezos',
217
+ 'TIA': 'celestia',
218
+ 'CRONOS': 'cronos', # CRC20
219
+ 'MATIC': 'polygon',
220
+ 'TON': 'ton',
221
+ 'TRC20': 'tron',
222
+ 'SOLANA': 'solana',
223
+ 'SGB': 'songbird',
224
+ 'DYDX': 'dydx',
225
+ 'DASH': 'dash',
226
+ 'ZIL': 'zilliqa',
227
+ 'EOS': 'eos',
228
+ 'AVALANCHEC': 'avalanche',
229
+ 'ETHPOW': 'ethereumpow',
230
+ 'NEAR': 'near',
231
+ 'ARB': 'arbitrum',
232
+ 'DOT': 'polkadot',
233
+ 'OPT': 'optimism',
234
+ 'INJ': 'injective',
235
+ 'ADA': 'cardano',
236
+ 'ONT': 'ontology',
237
+ 'ICP': 'icp',
238
+ 'KAVA': 'kava',
239
+ 'KSM': 'kusama',
240
+ 'SEI': 'sei',
241
+ # 'OSM': 'osmosis',
242
+ 'NEO': 'neo',
243
+ 'NEO3': 'neo3',
244
+ # 'TERRAOLD': 'terra', # tbd
245
+ # 'TERRA': 'terra2', # tbd
246
+ # 'EVER': 'everscale', # tbd
247
+ 'XDC': 'xdc',
221
248
  },
222
249
  },
223
250
  })
224
251
 
225
- 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 = 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
- def fetch_currencies(self, params={}):
252
+ def fetch_currencies(self, params={}) -> Currencies:
241
253
  """
242
254
  fetches all available currencies on an exchange
255
+
256
+ https://trade.cex.io/docs/#rest-public-api-calls-currencies-info
257
+
243
258
  :param dict [params]: extra parameters specific to the exchange API endpoint
244
259
  :returns dict: an associative dictionary of currencies
245
260
  """
246
- response = self.fetch_currencies_from_cache(params)
247
- self.options['currencies'] = {
248
- 'timestamp': self.milliseconds(),
249
- 'response': response,
250
- }
261
+ promises = []
262
+ promises.append(self.publicPostGetCurrenciesInfo(params))
251
263
  #
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
- # }
264
+ # {
265
+ # "ok": "ok",
266
+ # "data": [
267
+ # {
268
+ # "currency": "ZAP",
269
+ # "fiat": False,
270
+ # "precision": "8",
271
+ # "walletPrecision": "6",
272
+ # "walletDeposit": True,
273
+ # "walletWithdrawal": True
274
+ # },
275
+ # ...
276
+ #
277
+ promises.append(self.publicPostGetProcessingInfo(params))
278
+ #
279
+ # {
280
+ # "ok": "ok",
281
+ # "data": {
282
+ # "ADA": {
283
+ # "name": "Cardano",
284
+ # "blockchains": {
285
+ # "cardano": {
286
+ # "type": "coin",
287
+ # "deposit": "enabled",
288
+ # "minDeposit": "1",
289
+ # "withdrawal": "enabled",
290
+ # "minWithdrawal": "5",
291
+ # "withdrawalFee": "1",
292
+ # "withdrawalFeePercent": "0",
293
+ # "depositConfirmations": "15"
294
+ # }
295
+ # }
296
+ # },
297
+ # ...
311
298
  #
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,
299
+ responses = promises
300
+ dataCurrencies = self.safe_list(responses[0], 'data', [])
301
+ dataNetworks = self.safe_dict(responses[1], 'data', {})
302
+ currenciesIndexed = self.index_by(dataCurrencies, 'currency')
303
+ data = self.deep_extend(currenciesIndexed, dataNetworks)
304
+ return self.parse_currencies(self.to_array(data))
305
+
306
+ def parse_currency(self, rawCurrency: dict) -> Currency:
307
+ id = self.safe_string(rawCurrency, 'currency')
308
+ code = self.safe_currency_code(id)
309
+ type = 'fiat' if self.safe_bool(rawCurrency, 'fiat') else 'crypto'
310
+ currencyDepositEnabled = self.safe_bool(rawCurrency, 'walletDeposit')
311
+ currencyWithdrawEnabled = self.safe_bool(rawCurrency, 'walletWithdrawal')
312
+ currencyPrecision = self.parse_number(self.parse_precision(self.safe_string(rawCurrency, 'precision')))
313
+ networks: dict = {}
314
+ rawNetworks = self.safe_dict(rawCurrency, 'blockchains', {})
315
+ keys = list(rawNetworks.keys())
316
+ for j in range(0, len(keys)):
317
+ networkId = keys[j]
318
+ rawNetwork = rawNetworks[networkId]
319
+ networkCode = self.network_id_to_code(networkId)
320
+ deposit = self.safe_string(rawNetwork, 'deposit') == 'enabled'
321
+ withdraw = self.safe_string(rawNetwork, 'withdrawal') == 'enabled'
322
+ networks[networkCode] = {
323
+ 'id': networkId,
324
+ 'network': networkCode,
325
+ 'margin': None,
326
+ 'deposit': deposit,
327
+ 'withdraw': withdraw,
328
+ 'fee': self.safe_number(rawNetwork, 'withdrawalFee'),
329
+ 'precision': currencyPrecision,
329
330
  'limits': {
330
- 'amount': {
331
- 'min': self.safe_number(currency, 'minimumCurrencyAmount'),
331
+ 'deposit': {
332
+ 'min': self.safe_number(rawNetwork, 'minDeposit'),
332
333
  'max': None,
333
334
  },
334
335
  'withdraw': {
335
- 'min': self.safe_number(currency, 'minimalWithdrawalAmount'),
336
+ 'min': self.safe_number(rawNetwork, 'minWithdrawal'),
336
337
  'max': None,
337
338
  },
338
339
  },
339
- 'info': currency,
340
+ 'info': rawNetwork,
340
341
  }
341
- return result
342
+ return self.safe_currency_structure({
343
+ 'id': id,
344
+ 'code': code,
345
+ 'name': None,
346
+ 'type': type,
347
+ 'active': None,
348
+ 'deposit': currencyDepositEnabled,
349
+ 'withdraw': currencyWithdrawEnabled,
350
+ 'fee': None,
351
+ 'precision': currencyPrecision,
352
+ 'limits': {
353
+ 'amount': {
354
+ 'min': None,
355
+ 'max': None,
356
+ },
357
+ 'withdraw': {
358
+ 'min': None,
359
+ 'max': None,
360
+ },
361
+ },
362
+ 'networks': networks,
363
+ 'info': rawCurrency,
364
+ })
342
365
 
343
- def fetch_markets(self, params={}):
366
+ def fetch_markets(self, params={}) -> List[Market]:
344
367
  """
345
- retrieves data on all markets for cex
368
+ retrieves data on all markets for ace
369
+
370
+ https://trade.cex.io/docs/#rest-public-api-calls-pairs-info
371
+
346
372
  :param dict [params]: extra parameters specific to the exchange API endpoint
347
373
  :returns dict[]: an array of objects representing market data
348
374
  """
349
- currenciesResponse = 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 = self.publicGetCurrencyLimits(params)
375
+ response = self.publicPostGetPairsInfo(params)
355
376
  #
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
- # }
377
+ # {
378
+ # "ok": "ok",
379
+ # "data": [
380
+ # {
381
+ # "base": "AI",
382
+ # "quote": "USD",
383
+ # "baseMin": "30",
384
+ # "baseMax": "2516000",
385
+ # "baseLotSize": "0.000001",
386
+ # "quoteMin": "10",
387
+ # "quoteMax": "1000000",
388
+ # "quoteLotSize": "0.01000000",
389
+ # "basePrecision": "6",
390
+ # "quotePrecision": "8",
391
+ # "pricePrecision": "4",
392
+ # "minPrice": "0.0377",
393
+ # "maxPrice": "19.5000"
394
+ # },
395
+ # ...
382
396
  #
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)),
397
+ data = self.safe_list(response, 'data', [])
398
+ return self.parse_markets(data)
399
+
400
+ def parse_market(self, market: dict) -> Market:
401
+ baseId = self.safe_string(market, 'base')
402
+ base = self.safe_currency_code(baseId)
403
+ quoteId = self.safe_string(market, 'quote')
404
+ quote = self.safe_currency_code(quoteId)
405
+ id = base + '-' + quote # not actual id, but for self exchange we can use self abbreviation, because e.g. tickers have hyphen in between
406
+ symbol = base + '/' + quote
407
+ return self.safe_market_structure({
408
+ 'id': id,
409
+ 'symbol': symbol,
410
+ 'base': base,
411
+ 'baseId': baseId,
412
+ 'quote': quote,
413
+ 'quoteId': quoteId,
414
+ 'settle': None,
415
+ 'settleId': None,
416
+ 'type': 'spot',
417
+ 'spot': True,
418
+ 'margin': False,
419
+ 'swap': False,
420
+ 'future': False,
421
+ 'option': False,
422
+ 'contract': False,
423
+ 'linear': None,
424
+ 'inverse': None,
425
+ 'contractSize': None,
426
+ 'expiry': None,
427
+ 'expiryDatetime': None,
428
+ 'strike': None,
429
+ 'optionType': None,
430
+ 'limits': {
431
+ 'amount': {
432
+ 'min': self.safe_number(market, 'baseMin'),
433
+ 'max': self.safe_number(market, 'baseMax'),
429
434
  },
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
- },
435
+ 'price': {
436
+ 'min': self.safe_number(market, 'minPrice'),
437
+ 'max': self.safe_number(market, 'maxPrice'),
447
438
  },
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)
439
+ 'cost': {
440
+ 'min': self.safe_number(market, 'quoteMin'),
441
+ 'max': self.safe_number(market, 'quoteMax'),
442
+ },
443
+ 'leverage': {
444
+ 'min': None,
445
+ 'max': None,
446
+ },
447
+ },
448
+ 'precision': {
449
+ 'amount': self.safe_string(market, 'baseLotSize'),
450
+ 'price': self.parse_number(self.parse_precision(self.safe_string(market, 'pricePrecision'))),
451
+ # 'cost': self.parse_number(self.parse_precision(self.safe_string(market, 'quoteLotSize'))), # buggy, doesn't reflect their documentation
452
+ 'base': self.parse_number(self.parse_precision(self.safe_string(market, 'basePrecision'))),
453
+ 'quote': self.parse_number(self.parse_precision(self.safe_string(market, 'quotePrecision'))),
454
+ },
455
+ 'active': None,
456
+ 'created': None,
457
+ 'info': market,
458
+ })
468
459
 
469
- def fetch_balance(self, params={}) -> Balances:
460
+ def fetch_time(self, params={}):
470
461
  """
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
462
+ fetches the current integer timestamp in milliseconds from the exchange server
473
463
  :param dict [params]: extra parameters specific to the exchange API endpoint
474
- :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
464
+ :returns int: the current integer timestamp in milliseconds from the exchange server
475
465
  """
476
- self.load_markets()
477
- response = self.privatePostBalance(params)
478
- return self.parse_balance(response)
466
+ response = self.publicPostGetServerTime(params)
467
+ #
468
+ # {
469
+ # "ok": "ok",
470
+ # "data": {
471
+ # "timestamp": "1728472063472",
472
+ # "ISODate": "2024-10-09T11:07:43.472Z"
473
+ # }
474
+ # }
475
+ #
476
+ data = self.safe_dict(response, 'data')
477
+ timestamp = self.safe_integer(data, 'timestamp')
478
+ return timestamp
479
479
 
480
- def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
480
+ def fetch_ticker(self, symbol: str, params={}) -> Ticker:
481
481
  """
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
482
+ fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
483
+
484
+ https://trade.cex.io/docs/#rest-public-api-calls-ticker
485
+
486
+ :param str symbol:
486
487
  :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
488
+ :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
488
489
  """
489
490
  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 = self.publicGetOrderBookPair(self.extend(request, params))
497
- timestamp = self.safe_timestamp(response, 'timestamp')
498
- return self.parse_order_book(response, market['symbol'], timestamp)
491
+ response = self.fetch_tickers([symbol], params)
492
+ return self.safe_dict(response, symbol, {})
499
493
 
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
- def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
494
+ def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
521
495
  """
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
496
+ fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
497
+
498
+ https://trade.cex.io/docs/#rest-public-api-calls-ticker
499
+
500
+ :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
528
501
  :param dict [params]: extra parameters specific to the exchange API endpoint
529
- :returns int[][]: A list of candles ordered, open, high, low, close, volume
502
+ :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
530
503
  """
531
504
  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 = 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
505
+ request = {}
506
+ if symbols is not None:
507
+ request['pairs'] = self.market_ids(symbols)
508
+ response = self.publicPostGetTicker(self.extend(request, params))
509
+ #
510
+ # {
511
+ # "ok": "ok",
512
+ # "data": {
513
+ # "AI-USD": {
514
+ # "bestBid": "0.3917",
515
+ # "bestAsk": "0.3949",
516
+ # "bestBidChange": "0.0035",
517
+ # "bestBidChangePercentage": "0.90",
518
+ # "bestAskChange": "0.0038",
519
+ # "bestAskChangePercentage": "0.97",
520
+ # "low": "0.3787",
521
+ # "high": "0.3925",
522
+ # "volume30d": "2945.722277",
523
+ # "lastTradeDateISO": "2024-10-11T06:18:42.077Z",
524
+ # "volume": "120.736000",
525
+ # "quoteVolume": "46.65654070",
526
+ # "lastTradeVolume": "67.914000",
527
+ # "volumeUSD": "46.65",
528
+ # "last": "0.3949",
529
+ # "lastTradePrice": "0.3925",
530
+ # "priceChange": "0.0038",
531
+ # "priceChangePercentage": "0.97"
532
+ # },
533
+ # ...
534
+ #
535
+ data = self.safe_dict(response, 'data', {})
536
+ return self.parse_tickers(data, symbols)
558
537
 
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)
538
+ def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
539
+ marketId = self.safe_string(ticker, 'id')
540
+ symbol = self.safe_symbol(marketId, market)
568
541
  return self.safe_ticker({
569
542
  'symbol': symbol,
570
- 'timestamp': timestamp,
571
- 'datetime': self.iso8601(timestamp),
572
- 'high': high,
573
- 'low': low,
574
- 'bid': bid,
543
+ 'timestamp': None,
544
+ 'datetime': None,
545
+ 'high': self.safe_number(ticker, 'high'),
546
+ 'low': self.safe_number(ticker, 'low'),
547
+ 'bid': self.safe_number(ticker, 'bestBid'),
575
548
  'bidVolume': None,
576
- 'ask': ask,
549
+ 'ask': self.safe_number(ticker, 'bestAsk'),
577
550
  'askVolume': None,
578
551
  'vwap': None,
579
552
  'open': None,
580
- 'close': last,
581
- 'last': last,
553
+ 'close': self.safe_string(ticker, 'lastTradePrice'),
582
554
  'previousClose': None,
583
- 'change': None,
584
- 'percentage': None,
555
+ 'change': self.safe_number(ticker, 'priceChange'),
556
+ 'percentage': self.safe_number(ticker, 'priceChangePercentage'),
585
557
  'average': None,
586
- 'baseVolume': volume,
587
- 'quoteVolume': None,
558
+ 'baseVolume': self.safe_string(ticker, 'volume'),
559
+ 'quoteVolume': self.safe_string(ticker, 'quoteVolume'),
588
560
  'info': ticker,
589
561
  }, market)
590
562
 
591
- 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>`
563
+ def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
597
564
  """
598
- self.load_markets()
599
- symbols = self.market_symbols(symbols)
600
- currencies = list(self.currencies.keys())
601
- request = {
602
- 'currencies': '/'.join(currencies),
603
- }
604
- response = 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)
565
+ get the list of most recent trades for a particular symbol
614
566
 
615
- 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
567
+ https://trade.cex.io/docs/#rest-public-api-calls-trade-history
568
+
569
+ :param str symbol: unified symbol of the market to fetch trades for
570
+ :param int [since]: timestamp in ms of the earliest trade to fetch
571
+ :param int [limit]: the maximum amount of trades to fetch
620
572
  :param dict [params]: extra parameters specific to the exchange API endpoint
621
- :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
573
+ :param int [params.until]: timestamp in ms of the latest entry
574
+ :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
622
575
  """
623
576
  self.load_markets()
624
577
  market = self.market(symbol)
625
- request = {
578
+ request: dict = {
626
579
  'pair': market['id'],
627
580
  }
628
- ticker = self.publicGetTickerPair(self.extend(request, params))
629
- return self.parse_ticker(ticker, market)
581
+ if since is not None:
582
+ request['fromDateISO'] = self.iso8601(since)
583
+ until = None
584
+ until, params = self.handle_param_integer_2(params, 'until', 'till')
585
+ if until is not None:
586
+ request['toDateISO'] = self.iso8601(until)
587
+ if limit is not None:
588
+ request['pageSize'] = min(limit, 10000) # has a bug, still returns more trades
589
+ response = self.publicPostGetTradeHistory(self.extend(request, params))
590
+ #
591
+ # {
592
+ # "ok": "ok",
593
+ # "data": {
594
+ # "pageSize": "10",
595
+ # "trades": [
596
+ # {
597
+ # "tradeId": "1728630559823-0",
598
+ # "dateISO": "2024-10-11T07:09:19.823Z",
599
+ # "side": "SELL",
600
+ # "price": "60879.5",
601
+ # "amount": "0.00165962"
602
+ # },
603
+ # ... followed by older trades
604
+ #
605
+ data = self.safe_dict(response, 'data', {})
606
+ trades = self.safe_list(data, 'trades', [])
607
+ return self.parse_trades(trades, market, since, limit)
630
608
 
631
- def parse_trade(self, trade, market: Market = None) -> Trade:
609
+ def parse_trade(self, trade: dict, market: Market = None) -> Trade:
632
610
  #
633
- # fetchTrades(public)
611
+ # public fetchTrades
634
612
  #
635
- # {
636
- # "type": "sell",
637
- # "date": "1638401878",
638
- # "amount": "0.401000",
639
- # "price": "249",
640
- # "tid": "11922"
641
- # }
613
+ # {
614
+ # "tradeId": "1728630559823-0",
615
+ # "dateISO": "2024-10-11T07:09:19.823Z",
616
+ # "side": "SELL",
617
+ # "price": "60879.5",
618
+ # "amount": "0.00165962"
619
+ # },
642
620
  #
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')
621
+ dateStr = self.safe_string(trade, 'dateISO')
622
+ timestamp = self.parse8601(dateStr)
649
623
  market = self.safe_market(None, market)
650
624
  return self.safe_trade({
651
625
  'info': trade,
652
- 'id': id,
653
626
  'timestamp': timestamp,
654
627
  'datetime': self.iso8601(timestamp),
655
628
  'symbol': market['symbol'],
656
- 'type': type,
657
- 'side': side,
629
+ 'id': self.safe_string(trade, 'tradeId'),
658
630
  'order': None,
631
+ 'type': None,
659
632
  'takerOrMaker': None,
660
- 'price': priceString,
661
- 'amount': amountString,
633
+ 'side': self.safe_string_lower(trade, 'side'),
634
+ 'price': self.safe_string(trade, 'price'),
635
+ 'amount': self.safe_string(trade, 'amount'),
662
636
  'cost': None,
663
637
  'fee': None,
664
638
  }, market)
665
639
 
666
- def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
640
+ def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
667
641
  """
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
642
+ fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
643
+
644
+ https://trade.cex.io/docs/#rest-public-api-calls-order-book
645
+
646
+ :param str symbol: unified symbol of the market to fetch the order book for
647
+ :param int [limit]: the maximum amount of order book entries to return
673
648
  :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>`
649
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
650
+ """
651
+ self.load_markets()
652
+ market = self.market(symbol)
653
+ request: dict = {
654
+ 'pair': market['id'],
655
+ }
656
+ response = self.publicPostGetOrderBook(self.extend(request, params))
657
+ #
658
+ # {
659
+ # "ok": "ok",
660
+ # "data": {
661
+ # "timestamp": "1728636922648",
662
+ # "currency1": "BTC",
663
+ # "currency2": "USDT",
664
+ # "bids": [
665
+ # [
666
+ # "60694.1",
667
+ # "13.12849761"
668
+ # ],
669
+ # [
670
+ # "60694.0",
671
+ # "0.71829244"
672
+ # ],
673
+ # ...
674
+ #
675
+ orderBook = self.safe_dict(response, 'data', {})
676
+ timestamp = self.safe_integer(orderBook, 'timestamp')
677
+ return self.parse_order_book(orderBook, market['symbol'], timestamp)
678
+
679
+ def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
675
680
  """
681
+ fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
682
+
683
+ https://trade.cex.io/docs/#rest-public-api-calls-candles
684
+
685
+ :param str symbol: unified symbol of the market to fetch OHLCV data for
686
+ :param str timeframe: the length of time each candle represents
687
+ :param int [since]: timestamp in ms of the earliest candle to fetch
688
+ :param int [limit]: the maximum amount of candles to fetch
689
+ :param dict [params]: extra parameters specific to the exchange API endpoint
690
+ :param int [params.until]: timestamp in ms of the latest entry
691
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
692
+ """
693
+ dataType = None
694
+ dataType, params = self.handle_option_and_params(params, 'fetchOHLCV', 'dataType')
695
+ if dataType is None:
696
+ raise ArgumentsRequired(self.id + ' fetchOHLCV requires a parameter "dataType" to be either "bestBid" or "bestAsk"')
676
697
  self.load_markets()
677
698
  market = self.market(symbol)
678
- request = {
699
+ request: dict = {
679
700
  'pair': market['id'],
701
+ 'resolution': self.timeframes[timeframe],
702
+ 'dataType': dataType,
680
703
  }
681
- response = self.publicGetTradeHistoryPair(self.extend(request, params))
682
- return self.parse_trades(response, market, since, limit)
704
+ if since is not None:
705
+ request['fromISO'] = self.iso8601(since)
706
+ until = None
707
+ until, params = self.handle_param_integer_2(params, 'until', 'till')
708
+ if until is not None:
709
+ request['toISO'] = self.iso8601(until)
710
+ elif since is None:
711
+ # exchange still requires that we provide one of them
712
+ request['toISO'] = self.iso8601(self.milliseconds())
713
+ if since is not None and until is not None and limit is not None:
714
+ raise ArgumentsRequired(self.id + ' fetchOHLCV does not support fetching candles with both a limit and since/until')
715
+ elif (since is not None or until is not None) and limit is None:
716
+ raise ArgumentsRequired(self.id + ' fetchOHLCV requires a limit parameter when fetching candles with since or until')
717
+ if limit is not None:
718
+ request['limit'] = limit
719
+ response = self.publicPostGetCandles(self.extend(request, params))
720
+ #
721
+ # {
722
+ # "ok": "ok",
723
+ # "data": [
724
+ # {
725
+ # "timestamp": "1728643320000",
726
+ # "open": "61061",
727
+ # "high": "61095.1",
728
+ # "low": "61048.5",
729
+ # "close": "61087.8",
730
+ # "volume": "0",
731
+ # "resolution": "1m",
732
+ # "isClosed": True,
733
+ # "timestampISO": "2024-10-11T10:42:00.000Z"
734
+ # },
735
+ # ...
736
+ #
737
+ data = self.safe_list(response, 'data', [])
738
+ return self.parse_ohlcvs(data, market, timeframe, since, limit)
739
+
740
+ def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
741
+ return [
742
+ self.safe_integer(ohlcv, 'timestamp'),
743
+ self.safe_number(ohlcv, 'open'),
744
+ self.safe_number(ohlcv, 'high'),
745
+ self.safe_number(ohlcv, 'low'),
746
+ self.safe_number(ohlcv, 'close'),
747
+ self.safe_number(ohlcv, 'volume'),
748
+ ]
683
749
 
684
- def fetch_trading_fees(self, params={}):
750
+ def fetch_trading_fees(self, params={}) -> TradingFees:
685
751
  """
686
- :see: https://docs.cex.io/#get-my-fee
687
752
  fetch the trading fees for multiple markets
753
+
754
+ https://trade.cex.io/docs/#rest-public-api-calls-candles
755
+
688
756
  :param dict [params]: extra parameters specific to the exchange API endpoint
689
757
  :returns dict: a dictionary of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>` indexed by market symbols
690
758
  """
691
759
  self.load_markets()
692
- response = self.privatePostGetMyfee(params)
760
+ response = self.privatePostGetMyCurrentFee(params)
693
761
  #
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
- # }
762
+ # {
763
+ # "ok": "ok",
764
+ # "data": {
765
+ # "tradingFee": {
766
+ # "AI-USD": {
767
+ # "percent": "0.25"
768
+ # },
769
+ # ...
703
770
  #
704
- data = self.safe_value(response, 'data', {})
705
- result = {}
771
+ data = self.safe_dict(response, 'data', {})
772
+ fees = self.safe_dict(data, 'tradingFee', {})
773
+ return self.parse_trading_fees(fees, True)
774
+
775
+ def parse_trading_fees(self, response, useKeyAsId=False) -> TradingFees:
776
+ result: dict = {}
777
+ keys = list(response.keys())
778
+ for i in range(0, len(keys)):
779
+ key = keys[i]
780
+ market = None
781
+ if useKeyAsId:
782
+ market = self.safe_market(key)
783
+ parsed = self.parse_trading_fee(response[key], market)
784
+ result[parsed['symbol']] = parsed
706
785
  for i in range(0, len(self.symbols)):
707
786
  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
- }
787
+ if not (symbol in result):
788
+ market = self.market(symbol)
789
+ result[symbol] = self.parse_trading_fee(response, market)
721
790
  return result
722
791
 
792
+ def parse_trading_fee(self, fee: dict, market: Market = None) -> TradingFeeInterface:
793
+ return {
794
+ 'info': fee,
795
+ 'symbol': self.safe_string(market, 'symbol'),
796
+ 'maker': self.safe_number(fee, 'percent'),
797
+ 'taker': self.safe_number(fee, 'percent'),
798
+ 'percentage': None,
799
+ 'tierBased': None,
800
+ }
801
+
802
+ def fetch_accounts(self, params={}) -> List[Account]:
803
+ self.load_markets()
804
+ response = self.privatePostGetMyAccountStatusV3(params)
805
+ #
806
+ # {
807
+ # "ok": "ok",
808
+ # "data": {
809
+ # "convertedCurrency": "USD",
810
+ # "balancesPerAccounts": {
811
+ # "": {
812
+ # "AI": {
813
+ # "balance": "0.000000",
814
+ # "balanceOnHold": "0.000000"
815
+ # },
816
+ # "USDT": {
817
+ # "balance": "0.00000000",
818
+ # "balanceOnHold": "0.00000000"
819
+ # }
820
+ # }
821
+ # }
822
+ # }
823
+ # }
824
+ #
825
+ data = self.safe_dict(response, 'data', {})
826
+ balances = self.safe_dict(data, 'balancesPerAccounts', {})
827
+ arrays = self.to_array(balances)
828
+ return self.parse_accounts(arrays, params)
829
+
830
+ def parse_account(self, account: dict) -> Account:
831
+ return {
832
+ 'id': None,
833
+ 'type': None,
834
+ 'code': None,
835
+ 'info': account,
836
+ }
837
+
838
+ def fetch_balance(self, params={}) -> Balances:
839
+ """
840
+ query for balance and get the amount of funds available for trading or funds locked in orders
841
+
842
+ https://trade.cex.io/docs/#rest-private-api-calls-account-status-v3
843
+
844
+ :param dict [params]: extra parameters specific to the exchange API endpoint
845
+ :param dict [params.method]: 'privatePostGetMyWalletBalance' or 'privatePostGetMyAccountStatusV3'
846
+ :param dict [params.account]: in case 'privatePostGetMyAccountStatusV3' is chosen, self can specify the account name(default is empty string)
847
+ :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
848
+ """
849
+ accountName = None
850
+ accountName, params = self.handle_param_string(params, 'account', '') # default is empty string
851
+ method = None
852
+ method, params = self.handle_param_string(params, 'method', 'privatePostGetMyWalletBalance')
853
+ accountBalance = None
854
+ if method == 'privatePostGetMyAccountStatusV3':
855
+ response = self.privatePostGetMyAccountStatusV3(params)
856
+ #
857
+ # {
858
+ # "ok": "ok",
859
+ # "data": {
860
+ # "convertedCurrency": "USD",
861
+ # "balancesPerAccounts": {
862
+ # "": {
863
+ # "AI": {
864
+ # "balance": "0.000000",
865
+ # "balanceOnHold": "0.000000"
866
+ # },
867
+ # ....
868
+ #
869
+ data = self.safe_dict(response, 'data', {})
870
+ balances = self.safe_dict(data, 'balancesPerAccounts', {})
871
+ accountBalance = self.safe_dict(balances, accountName, {})
872
+ else:
873
+ response = self.privatePostGetMyWalletBalance(params)
874
+ #
875
+ # {
876
+ # "ok": "ok",
877
+ # "data": {
878
+ # "AI": {
879
+ # "balance": "25.606429"
880
+ # },
881
+ # "USDT": {
882
+ # "balance": "7.935449"
883
+ # },
884
+ # ...
885
+ #
886
+ accountBalance = self.safe_dict(response, 'data', {})
887
+ return self.parse_balance(accountBalance)
888
+
889
+ def parse_balance(self, response) -> Balances:
890
+ result: dict = {
891
+ 'info': response,
892
+ }
893
+ keys = list(response.keys())
894
+ for i in range(0, len(keys)):
895
+ key = keys[i]
896
+ balance = self.safe_dict(response, key, {})
897
+ code = self.safe_currency_code(key)
898
+ account: dict = {
899
+ 'used': self.safe_string(balance, 'balanceOnHold'),
900
+ 'total': self.safe_string(balance, 'balance'),
901
+ }
902
+ result[code] = account
903
+ return self.safe_balance(result)
904
+
905
+ def fetch_orders_by_status(self, status: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
906
+ """
907
+ fetches information on multiple orders made by the user
908
+
909
+ https://trade.cex.io/docs/#rest-private-api-calls-orders
910
+
911
+ :param str status: order status to fetch for
912
+ :param str symbol: unified market symbol of the market orders were made in
913
+ :param int [since]: the earliest time in ms to fetch orders for
914
+ :param int [limit]: the maximum number of order structures to retrieve
915
+ :param dict [params]: extra parameters specific to the exchange API endpoint
916
+ :param int [params.until]: timestamp in ms of the latest entry
917
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
918
+ """
919
+ self.load_markets()
920
+ request: dict = {}
921
+ isClosedOrders = (status == 'closed')
922
+ if isClosedOrders:
923
+ request['archived'] = True
924
+ market = None
925
+ if symbol is not None:
926
+ market = self.market(symbol)
927
+ request['pair'] = market['id']
928
+ if limit is not None:
929
+ request['pageSize'] = limit
930
+ if since is not None:
931
+ request['serverCreateTimestampFrom'] = since
932
+ elif isClosedOrders:
933
+ # exchange requires a `since` parameter for closed orders, so set default to allowed 365
934
+ request['serverCreateTimestampFrom'] = self.milliseconds() - 364 * 24 * 60 * 60 * 1000
935
+ until = None
936
+ until, params = self.handle_param_integer_2(params, 'until', 'till')
937
+ if until is not None:
938
+ request['serverCreateTimestampTo'] = until
939
+ response = self.privatePostGetMyOrders(self.extend(request, params))
940
+ #
941
+ # if called without `pair`
942
+ #
943
+ # {
944
+ # "ok": "ok",
945
+ # "data": [
946
+ # {
947
+ # "orderId": "1313003",
948
+ # "clientOrderId": "037F0AFEB93A",
949
+ # "clientId": "up421412345",
950
+ # "accountId": null,
951
+ # "status": "FILLED",
952
+ # "statusIsFinal": True,
953
+ # "currency1": "AI",
954
+ # "currency2": "USDT",
955
+ # "side": "BUY",
956
+ # "orderType": "Market",
957
+ # "timeInForce": "IOC",
958
+ # "comment": null,
959
+ # "rejectCode": null,
960
+ # "rejectReason": null,
961
+ # "initialOnHoldAmountCcy1": null,
962
+ # "initialOnHoldAmountCcy2": "10.23456700",
963
+ # "executedAmountCcy1": "25.606429",
964
+ # "executedAmountCcy2": "10.20904439",
965
+ # "requestedAmountCcy1": null,
966
+ # "requestedAmountCcy2": "10.20904439",
967
+ # "originalAmountCcy2": "10.23456700",
968
+ # "feeAmount": "0.02552261",
969
+ # "feeCurrency": "USDT",
970
+ # "price": null,
971
+ # "averagePrice": "0.3986",
972
+ # "clientCreateTimestamp": "1728474625320",
973
+ # "serverCreateTimestamp": "1728474624956",
974
+ # "lastUpdateTimestamp": "1728474628015",
975
+ # "expireTime": null,
976
+ # "effectiveTime": null
977
+ # },
978
+ # ...
979
+ #
980
+ data = self.safe_list(response, 'data', [])
981
+ return self.parse_orders(data, market, since, limit)
982
+
983
+ def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
984
+ """
985
+
986
+ https://trade.cex.io/docs/#rest-private-api-calls-orders
987
+
988
+ fetches information on multiple canceled orders made by the user
989
+ :param str symbol: unified market symbol of the market orders were made in
990
+ :param int [since]: timestamp in ms of the earliest order, default is None
991
+ :param int [limit]: max number of orders to return, default is None
992
+ :param dict [params]: extra parameters specific to the exchange API endpoint
993
+ :returns dict: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
994
+ """
995
+ return self.fetch_orders_by_status('closed', symbol, since, limit, params)
996
+
997
+ def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
998
+ """
999
+
1000
+ https://trade.cex.io/docs/#rest-private-api-calls-orders
1001
+
1002
+ fetches information on multiple canceled orders made by the user
1003
+ :param str symbol: unified market symbol of the market orders were made in
1004
+ :param int [since]: timestamp in ms of the earliest order, default is None
1005
+ :param int [limit]: max number of orders to return, default is None
1006
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1007
+ :returns dict: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1008
+ """
1009
+ return self.fetch_orders_by_status('open', symbol, since, limit, params)
1010
+
1011
+ def fetch_open_order(self, id: str, symbol: Str = None, params={}):
1012
+ """
1013
+ fetches information on an open order made by the user
1014
+
1015
+ https://trade.cex.io/docs/#rest-private-api-calls-orders
1016
+
1017
+ :param str id: order id
1018
+ :param str [symbol]: unified symbol of the market the order was made in
1019
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1020
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1021
+ """
1022
+ self.load_markets()
1023
+ request: dict = {
1024
+ 'orderId': int(id),
1025
+ }
1026
+ result = self.fetch_open_orders(symbol, None, None, self.extend(request, params))
1027
+ return result[0]
1028
+
1029
+ def fetch_closed_order(self, id: str, symbol: Str = None, params={}):
1030
+ """
1031
+ fetches information on an closed order made by the user
1032
+
1033
+ https://trade.cex.io/docs/#rest-private-api-calls-orders
1034
+
1035
+ :param str id: order id
1036
+ :param str [symbol]: unified symbol of the market the order was made in
1037
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1038
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1039
+ """
1040
+ self.load_markets()
1041
+ request: dict = {
1042
+ 'orderId': int(id),
1043
+ }
1044
+ result = self.fetch_closed_orders(symbol, None, None, self.extend(request, params))
1045
+ return result[0]
1046
+
1047
+ def parse_order_status(self, status: Str):
1048
+ statuses: dict = {
1049
+ 'PENDING_NEW': 'open',
1050
+ 'NEW': 'open',
1051
+ 'PARTIALLY_FILLED': 'open',
1052
+ 'FILLED': 'closed',
1053
+ 'EXPIRED': 'expired',
1054
+ 'REJECTED': 'rejected',
1055
+ 'PENDING_CANCEL': 'canceling',
1056
+ 'CANCELLED': 'canceled',
1057
+ }
1058
+ return self.safe_string(statuses, status, status)
1059
+
1060
+ def parse_order(self, order: dict, market: Market = None) -> Order:
1061
+ #
1062
+ # "orderId": "1313003",
1063
+ # "clientOrderId": "037F0AFEB93A",
1064
+ # "clientId": "up421412345",
1065
+ # "accountId": null,
1066
+ # "status": "FILLED",
1067
+ # "statusIsFinal": True,
1068
+ # "currency1": "AI",
1069
+ # "currency2": "USDT",
1070
+ # "side": "BUY",
1071
+ # "orderType": "Market",
1072
+ # "timeInForce": "IOC",
1073
+ # "comment": null,
1074
+ # "rejectCode": null,
1075
+ # "rejectReason": null,
1076
+ # "initialOnHoldAmountCcy1": null,
1077
+ # "initialOnHoldAmountCcy2": "10.23456700",
1078
+ # "executedAmountCcy1": "25.606429",
1079
+ # "executedAmountCcy2": "10.20904439",
1080
+ # "requestedAmountCcy1": null,
1081
+ # "requestedAmountCcy2": "10.20904439",
1082
+ # "originalAmountCcy2": "10.23456700",
1083
+ # "feeAmount": "0.02552261",
1084
+ # "feeCurrency": "USDT",
1085
+ # "price": null,
1086
+ # "averagePrice": "0.3986",
1087
+ # "clientCreateTimestamp": "1728474625320",
1088
+ # "serverCreateTimestamp": "1728474624956",
1089
+ # "lastUpdateTimestamp": "1728474628015",
1090
+ # "expireTime": null,
1091
+ # "effectiveTime": null
1092
+ #
1093
+ currency1 = self.safe_string(order, 'currency1')
1094
+ currency2 = self.safe_string(order, 'currency2')
1095
+ marketId = None
1096
+ if currency1 is not None and currency2 is not None:
1097
+ marketId = currency1 + '-' + currency2
1098
+ market = self.safe_market(marketId, market)
1099
+ symbol = market['symbol']
1100
+ status = self.parse_order_status(self.safe_string(order, 'status'))
1101
+ fee = {}
1102
+ feeAmount = self.safe_number(order, 'feeAmount')
1103
+ if feeAmount is not None:
1104
+ currencyId = self.safe_string(order, 'feeCurrency')
1105
+ feeCode = self.safe_currency_code(currencyId)
1106
+ fee['currency'] = feeCode
1107
+ fee['cost'] = feeAmount
1108
+ timestamp = self.safe_integer(order, 'serverCreateTimestamp')
1109
+ requestedBase = self.safe_number(order, 'requestedAmountCcy1')
1110
+ executedBase = self.safe_number(order, 'executedAmountCcy1')
1111
+ # requestedQuote = self.safe_number(order, 'requestedAmountCcy2')
1112
+ executedQuote = self.safe_number(order, 'executedAmountCcy2')
1113
+ return self.safe_order({
1114
+ 'id': self.safe_string(order, 'orderId'),
1115
+ 'clientOrderId': self.safe_string(order, 'clientOrderId'),
1116
+ 'timestamp': timestamp,
1117
+ 'datetime': self.iso8601(timestamp),
1118
+ 'lastUpdateTimestamp': self.safe_integer(order, 'lastUpdateTimestamp'),
1119
+ 'lastTradeTimestamp': None,
1120
+ 'symbol': symbol,
1121
+ 'type': self.safe_string_lower(order, 'orderType'),
1122
+ 'timeInForce': self.safe_string(order, 'timeInForce'),
1123
+ 'postOnly': None,
1124
+ 'side': self.safe_string_lower(order, 'side'),
1125
+ 'price': self.safe_number(order, 'price'),
1126
+ 'triggerPrice': self.safe_number(order, 'stopPrice'),
1127
+ 'amount': requestedBase,
1128
+ 'cost': executedQuote,
1129
+ 'average': self.safe_number(order, 'averagePrice'),
1130
+ 'filled': executedBase,
1131
+ 'remaining': None,
1132
+ 'status': status,
1133
+ 'fee': fee,
1134
+ 'trades': None,
1135
+ 'info': order,
1136
+ }, market)
1137
+
723
1138
  def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
724
1139
  """
725
- :see: https://docs.cex.io/#place-order
726
1140
  create a trade order
727
- :see: https://cex.io/rest-api#place-order
1141
+
1142
+ https://trade.cex.io/docs/#rest-private-api-calls-new-order
1143
+
728
1144
  :param str symbol: unified symbol of the market to create an order in
729
1145
  :param str type: 'market' or 'limit'
730
1146
  :param str side: 'buy' or 'sell'
731
1147
  :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
1148
+ :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
733
1149
  :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
1150
+ :param str [params.accountId]: account-id to use(default is empty string)
1151
+ :param float [params.triggerPrice]: the price at which a trigger order is triggered at
735
1152
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
736
1153
  """
1154
+ accountId = None
1155
+ accountId, params = self.handle_option_and_params(params, 'createOrder', 'accountId')
1156
+ if accountId is None:
1157
+ 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
1158
  self.load_markets()
738
1159
  market = self.market(symbol)
739
- request = {
740
- 'pair': market['id'],
741
- 'type': side,
1160
+ request: dict = {
1161
+ 'clientOrderId': self.uuid(),
1162
+ 'currency1': market['baseId'],
1163
+ 'currency2': market['quoteId'],
1164
+ 'accountId': accountId,
1165
+ 'orderType': self.capitalize(type.lower()),
1166
+ 'side': side.upper(),
1167
+ 'timestamp': self.milliseconds(),
1168
+ 'amountCcy1': self.amount_to_precision(symbol, amount),
742
1169
  }
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)
1170
+ timeInForce = None
1171
+ timeInForce, params = self.handle_option_and_params(params, 'createOrder', 'timeInForce', 'GTC')
765
1172
  if type == 'limit':
766
- request['price'] = self.number_to_string(price)
767
- else:
768
- request['order_type'] = type
769
- response = self.privatePostPlaceOrderPair(self.extend(request, params))
1173
+ request['price'] = self.price_to_precision(symbol, price)
1174
+ request['timeInForce'] = timeInForce
1175
+ triggerPrice = None
1176
+ triggerPrice, params = self.handle_param_string(params, 'triggerPrice')
1177
+ if triggerPrice is not None:
1178
+ request['type'] = 'Stop Limit'
1179
+ request['stopPrice'] = triggerPrice
1180
+ response = self.privatePostDoMyNewOrder(self.extend(request, params))
770
1181
  #
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
- # }
1182
+ # on success
780
1183
  #
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
- })
1184
+ # {
1185
+ # "ok": "ok",
1186
+ # "data": {
1187
+ # "messageType": "executionReport",
1188
+ # "clientId": "up132245425",
1189
+ # "orderId": "1318485",
1190
+ # "clientOrderId": "b5b6cd40-154c-4c1c-bd51-4a442f3d50b9",
1191
+ # "accountId": "sub1",
1192
+ # "status": "FILLED",
1193
+ # "currency1": "LTC",
1194
+ # "currency2": "USDT",
1195
+ # "side": "BUY",
1196
+ # "executedAmountCcy1": "0.23000000",
1197
+ # "executedAmountCcy2": "15.09030000",
1198
+ # "requestedAmountCcy1": "0.23000000",
1199
+ # "requestedAmountCcy2": null,
1200
+ # "orderType": "Market",
1201
+ # "timeInForce": null,
1202
+ # "comment": null,
1203
+ # "executionType": "Trade",
1204
+ # "executionId": "1726747124624_101_41116",
1205
+ # "transactTime": "2024-10-15T15:08:12.794Z",
1206
+ # "expireTime": null,
1207
+ # "effectiveTime": null,
1208
+ # "averagePrice": "65.61",
1209
+ # "lastQuantity": "0.23000000",
1210
+ # "lastAmountCcy1": "0.23000000",
1211
+ # "lastAmountCcy2": "15.09030000",
1212
+ # "lastPrice": "65.61",
1213
+ # "feeAmount": "0.03772575",
1214
+ # "feeCurrency": "USDT",
1215
+ # "clientCreateTimestamp": "1729004892014",
1216
+ # "serverCreateTimestamp": "1729004891628",
1217
+ # "lastUpdateTimestamp": "1729004892786"
1218
+ # }
1219
+ # }
1220
+ #
1221
+ # on failure, there are extra fields
1222
+ #
1223
+ # "status": "REJECTED",
1224
+ # "requestedAmountCcy1": null,
1225
+ # "orderRejectReason": "{\\" code \\ ":405,\\" reason \\ ":\\" Either AmountCcy1(OrderQty)or AmountCcy2(CashOrderQty)should be specified for market order not both \\ "}",
1226
+ # "rejectCode": 405,
1227
+ # "rejectReason": "Either AmountCcy1(OrderQty) or AmountCcy2(CashOrderQty) should be specified for market order not both",
1228
+ #
1229
+ data = self.safe_dict(response, 'data')
1230
+ return self.parse_order(data, market)
809
1231
 
810
1232
  def cancel_order(self, id: str, symbol: Str = None, params={}):
811
1233
  """
812
- :see: https://docs.cex.io/#cancel-order
813
1234
  cancels an open order
1235
+
1236
+ https://trade.cex.io/docs/#rest-private-api-calls-cancel-order
1237
+
814
1238
  :param str id: order id
815
- :param str symbol: not used by cex cancelOrder()
1239
+ :param str symbol: unified symbol of the market the order was made in
816
1240
  :param dict [params]: extra parameters specific to the exchange API endpoint
817
1241
  :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
818
1242
  """
819
1243
  self.load_markets()
820
- request = {
821
- 'id': id,
1244
+ request: dict = {
1245
+ 'orderId': int(id),
1246
+ 'cancelRequestId': 'c_' + str((self.milliseconds())),
1247
+ 'timestamp': self.milliseconds(),
822
1248
  }
823
- response = self.privatePostCancelOrder(self.extend(request, params))
824
- # 'true'
825
- return self.extend(self.parse_order({}), {'info': response, 'type': None, 'id': id, 'status': 'canceled'})
1249
+ response = self.privatePostDoCancelMyOrder(self.extend(request, params))
1250
+ #
1251
+ # {"ok":"ok","data":{}}
1252
+ #
1253
+ data = self.safe_dict(response, 'data', {})
1254
+ return self.parse_order(data)
826
1255
 
827
1256
  def cancel_all_orders(self, symbol: Str = None, params={}):
828
1257
  """
829
- :see: https://docs.cex.io/#cancel-all-orders-for-given-pair
830
1258
  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
1259
+
1260
+ https://trade.cex.io/docs/#rest-private-api-calls-cancel-all-orders
1261
+
1262
+ :param str symbol: alpaca cancelAllOrders cannot setting symbol, it will cancel all open orders
1263
+ :param dict [params]: extra parameters specific to the exchange API endpoint
834
1264
  :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
835
1265
  """
836
- if symbol is None:
837
- raise ArgumentsRequired(self.id + ' cancelAllOrders requires a symbol.')
838
1266
  self.load_markets()
839
- market = self.market(symbol)
840
- request = {
841
- 'pair': market['id'],
842
- }
843
- orders = self.privatePostCancelOrdersPair(self.extend(request, params))
1267
+ response = self.privatePostDoCancelAllOrders(params)
844
1268
  #
845
- # {
846
- # "e":"cancel_orders",
847
- # "ok":"ok",
848
- # "data":[
849
- # ]
850
- # }
1269
+ # {
1270
+ # "ok": "ok",
1271
+ # "data": {
1272
+ # "clientOrderIds": [
1273
+ # "3AF77B67109F"
1274
+ # ]
1275
+ # }
1276
+ # }
851
1277
  #
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
- })
1278
+ data = self.safe_dict(response, 'data', {})
1279
+ ids = self.safe_list(data, 'clientOrderIds', [])
1280
+ orders = []
1281
+ for i in range(0, len(ids)):
1282
+ id = ids[i]
1283
+ orders.append({'clientOrderId': id})
1284
+ return self.parse_orders(orders)
1099
1285
 
1100
- def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1286
+ def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[LedgerEntry]:
1101
1287
  """
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
1288
+ fetch the history of changes, actions done by the user or operations that altered the balance of the user
1289
+
1290
+ https://trade.cex.io/docs/#rest-private-api-calls-transaction-history
1291
+
1292
+ :param str [code]: unified currency code
1293
+ :param int [since]: timestamp in ms of the earliest ledger entry
1294
+ :param int [limit]: max number of ledger entries to return
1107
1295
  :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>`
1296
+ :param int [params.until]: timestamp in ms of the latest ledger entry
1297
+ :returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger>`
1109
1298
  """
1110
1299
  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 = self.privatePostOpenOrdersPair(self.extend(request, params))
1300
+ currency = None
1301
+ request: dict = {}
1302
+ if code is not None:
1303
+ currency = self.currency(code)
1304
+ request['currency'] = currency['id']
1305
+ if since is not None:
1306
+ request['dateFrom'] = since
1307
+ if limit is not None:
1308
+ request['pageSize'] = limit
1309
+ until = None
1310
+ until, params = self.handle_param_integer_2(params, 'until', 'till')
1311
+ if until is not None:
1312
+ request['dateTo'] = until
1313
+ response = self.privatePostGetMyTransactionHistory(self.extend(request, params))
1314
+ #
1315
+ # {
1316
+ # "ok": "ok",
1317
+ # "data": [
1318
+ # {
1319
+ # "transactionId": "30367722",
1320
+ # "timestamp": "2024-10-14T14:08:49.987Z",
1321
+ # "accountId": "",
1322
+ # "type": "withdraw",
1323
+ # "amount": "-12.39060600",
1324
+ # "details": "Withdraw fundingId=1235039 clientId=up421412345 walletTxId=76337154166",
1325
+ # "currency": "USDT"
1326
+ # },
1327
+ # ...
1328
+ #
1329
+ data = self.safe_list(response, 'data', [])
1330
+ return self.parse_ledger(data, currency, since, limit)
1331
+
1332
+ def parse_ledger_entry(self, item: dict, currency: Currency = None) -> LedgerEntry:
1333
+ amount = self.safe_string(item, 'amount')
1334
+ direction = None
1335
+ if Precise.string_le(amount, '0'):
1336
+ direction = 'out'
1337
+ amount = Precise.string_mul('-1', amount)
1118
1338
  else:
1119
- orders = 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)
1339
+ direction = 'in'
1340
+ currencyId = self.safe_string(item, 'currency')
1341
+ currency = self.safe_currency(currencyId, currency)
1342
+ code = self.safe_currency_code(currencyId, currency)
1343
+ timestampString = self.safe_string(item, 'timestamp')
1344
+ timestamp = self.parse8601(timestampString)
1345
+ type = self.safe_string(item, 'type')
1346
+ return self.safe_ledger_entry({
1347
+ 'info': item,
1348
+ 'id': self.safe_string(item, 'transactionId'),
1349
+ 'direction': direction,
1350
+ 'account': self.safe_string(item, 'accountId', ''),
1351
+ 'referenceAccount': None,
1352
+ 'referenceId': None,
1353
+ 'type': self.parse_ledger_entry_type(type),
1354
+ 'currency': code,
1355
+ 'amount': self.parse_number(amount),
1356
+ 'timestamp': timestamp,
1357
+ 'datetime': self.iso8601(timestamp),
1358
+ 'before': None,
1359
+ 'after': None,
1360
+ 'status': None,
1361
+ 'fee': None,
1362
+ }, currency)
1123
1363
 
1124
- def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1364
+ def parse_ledger_entry_type(self, type):
1365
+ ledgerType: dict = {
1366
+ 'deposit': 'deposit',
1367
+ 'withdraw': 'withdrawal',
1368
+ 'commission': 'fee',
1369
+ }
1370
+ return self.safe_string(ledgerType, type, type)
1371
+
1372
+ def fetch_deposits_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
1125
1373
  """
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
1374
+ fetch history of deposits and withdrawals
1375
+
1376
+ https://trade.cex.io/docs/#rest-private-api-calls-funding-history
1377
+
1378
+ :param str [code]: unified currency code for the currency of the deposit/withdrawals, default is None
1379
+ :param int [since]: timestamp in ms of the earliest deposit/withdrawal, default is None
1380
+ :param int [limit]: max number of deposit/withdrawals to return, default is None
1131
1381
  :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>`
1382
+ :returns dict: a list of `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
1133
1383
  """
1134
- if symbol is None:
1135
- raise ArgumentsRequired(self.id + ' fetchClosedOrders() requires a symbol argument')
1136
1384
  self.load_markets()
1137
- market = self.market(symbol)
1138
- request = {'pair': market['id']}
1139
- response = self.privatePostArchivedOrdersPair(self.extend(request, params))
1140
- return self.parse_orders(response, market, since, limit)
1385
+ request: dict = {}
1386
+ currency = None
1387
+ if code is not None:
1388
+ currency = self.currency(code)
1389
+ if since is not None:
1390
+ request['dateFrom'] = since
1391
+ if limit is not None:
1392
+ request['pageSize'] = limit
1393
+ until = None
1394
+ until, params = self.handle_param_integer_2(params, 'until', 'till')
1395
+ if until is not None:
1396
+ request['dateTo'] = until
1397
+ response = self.privatePostGetMyFundingHistory(self.extend(request, params))
1398
+ #
1399
+ # {
1400
+ # "ok": "ok",
1401
+ # "data": [
1402
+ # {
1403
+ # "clientId": "up421412345",
1404
+ # "accountId": "",
1405
+ # "currency": "USDT",
1406
+ # "direction": "withdraw",
1407
+ # "amount": "12.39060600",
1408
+ # "commissionAmount": "0.00000000",
1409
+ # "status": "approved",
1410
+ # "updatedAt": "2024-10-14T14:08:50.013Z",
1411
+ # "txId": "30367718",
1412
+ # "details": {}
1413
+ # },
1414
+ # ...
1415
+ #
1416
+ data = self.safe_list(response, 'data', [])
1417
+ return self.parse_transactions(data, currency, since, limit)
1141
1418
 
1142
- def fetch_order(self, id: str, symbol: Str = None, params={}):
1419
+ def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
1420
+ currencyId = self.safe_string(transaction, 'currency')
1421
+ direction = self.safe_string(transaction, 'direction')
1422
+ type = 'withdrawal' if (direction == 'withdraw') else 'deposit'
1423
+ code = self.safe_currency_code(currencyId, currency)
1424
+ updatedAt = self.safe_string(transaction, 'updatedAt')
1425
+ timestamp = self.parse8601(updatedAt)
1426
+ return {
1427
+ 'info': transaction,
1428
+ 'id': self.safe_string(transaction, 'txId'),
1429
+ 'txid': None,
1430
+ 'type': type,
1431
+ 'currency': code,
1432
+ 'network': None,
1433
+ 'amount': self.safe_number(transaction, 'amount'),
1434
+ 'status': self.parse_transaction_status(self.safe_string(transaction, 'status')),
1435
+ 'timestamp': timestamp,
1436
+ 'datetime': self.iso8601(timestamp),
1437
+ 'address': None,
1438
+ 'addressFrom': None,
1439
+ 'addressTo': None,
1440
+ 'tag': None,
1441
+ 'tagFrom': None,
1442
+ 'tagTo': None,
1443
+ 'updated': None,
1444
+ 'comment': None,
1445
+ 'fee': {
1446
+ 'currency': code,
1447
+ 'cost': self.safe_number(transaction, 'commissionAmount'),
1448
+ },
1449
+ 'internal': None,
1450
+ }
1451
+
1452
+ def parse_transaction_status(self, status: Str):
1453
+ statuses: dict = {
1454
+ 'rejected': 'rejected',
1455
+ 'pending': 'pending',
1456
+ 'approved': 'ok',
1457
+ }
1458
+ return self.safe_string(statuses, status, status)
1459
+
1460
+ def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
1143
1461
  """
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
1462
+ transfer currency internally between wallets on the same account
1463
+
1464
+ https://trade.cex.io/docs/#rest-private-api-calls-internal-transfer
1465
+
1466
+ :param str code: unified currency code
1467
+ :param float amount: amount to transfer
1468
+ :param str fromAccount: 'SPOT', 'FUND', or 'CONTRACT'
1469
+ :param str toAccount: 'SPOT', 'FUND', or 'CONTRACT'
1147
1470
  :param dict [params]: extra parameters specific to the exchange API endpoint
1148
- :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1471
+ :returns dict: a `transfer structure <https://docs.ccxt.com/#/?id=transfer-structure>`
1149
1472
  """
1473
+ transfer = None
1474
+ if toAccount != '' and fromAccount != '':
1475
+ transfer = self.transfer_between_sub_accounts(code, amount, fromAccount, toAccount, params)
1476
+ else:
1477
+ transfer = self.transfer_between_main_and_sub_account(code, amount, fromAccount, toAccount, params)
1478
+ fillResponseFromRequest = self.handle_option('transfer', 'fillResponseFromRequest', True)
1479
+ if fillResponseFromRequest:
1480
+ transfer['fromAccount'] = fromAccount
1481
+ transfer['toAccount'] = toAccount
1482
+ return transfer
1483
+
1484
+ def transfer_between_main_and_sub_account(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
1150
1485
  self.load_markets()
1151
- request = {
1152
- 'id': str(id),
1486
+ currency = self.currency(code)
1487
+ fromMain = (fromAccount == '')
1488
+ targetAccount = toAccount if fromMain else fromAccount
1489
+ guid = self.safe_string(params, 'guid', self.uuid())
1490
+ request: dict = {
1491
+ 'currency': currency['id'],
1492
+ 'amount': self.currency_to_precision(code, amount),
1493
+ 'accountId': targetAccount,
1494
+ 'clientTxId': guid,
1153
1495
  }
1154
- response = self.privatePostGetOrderTx(self.extend(request, params))
1155
- data = self.safe_value(response, 'data', {})
1496
+ response = None
1497
+ if fromMain:
1498
+ response = self.privatePostDoDepositFundsFromWallet(self.extend(request, params))
1499
+ else:
1500
+ response = self.privatePostDoWithdrawalFundsToWallet(self.extend(request, params))
1501
+ # both endpoints return the same structure, the only difference is that
1502
+ # the "accountId" is filled with the "subAccount"
1156
1503
  #
1157
1504
  # {
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
- # ]
1505
+ # "ok": "ok",
1506
+ # "data": {
1507
+ # "accountId": "sub1",
1508
+ # "clientTxId": "27ba8284-67cf-4386-9ec7-80b3871abd45",
1509
+ # "currency": "USDT",
1510
+ # "status": "approved"
1511
+ # }
1254
1512
  # }
1255
1513
  #
1256
- return self.parse_order(data)
1514
+ data = self.safe_dict(response, 'data', {})
1515
+ return self.parse_transfer(data, currency)
1257
1516
 
1258
- 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
- """
1517
+ def transfer_between_sub_accounts(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
1268
1518
  self.load_markets()
1269
- market = self.market(symbol)
1270
- request = {
1271
- 'limit': limit,
1272
- 'pair': market['id'],
1273
- 'dateFrom': since,
1519
+ currency = self.currency(code)
1520
+ request: dict = {
1521
+ 'currency': currency['id'],
1522
+ 'amount': self.currency_to_precision(code, amount),
1523
+ 'fromAccountId': fromAccount,
1524
+ 'toAccountId': toAccount,
1274
1525
  }
1275
- response = 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)
1526
+ response = self.privatePostDoMyInternalTransfer(self.extend(request, params))
1527
+ #
1528
+ # {
1529
+ # "ok": "ok",
1530
+ # "data": {
1531
+ # "transactionId": "30225415"
1532
+ # }
1533
+ # }
1534
+ #
1535
+ data = self.safe_dict(response, 'data', {})
1536
+ return self.parse_transfer(data, currency)
1476
1537
 
1477
- 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
- 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,
1538
+ def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
1539
+ #
1540
+ # transferBetweenSubAccounts
1541
+ #
1542
+ # {
1543
+ # "ok": "ok",
1544
+ # "data": {
1545
+ # "transactionId": "30225415"
1546
+ # }
1547
+ # }
1548
+ #
1549
+ # transfer between main/sub
1550
+ #
1551
+ # {
1552
+ # "ok": "ok",
1553
+ # "data": {
1554
+ # "accountId": "sub1",
1555
+ # "clientTxId": "27ba8284-67cf-4386-9ec7-80b3871abd45",
1556
+ # "currency": "USDT",
1557
+ # "status": "approved"
1558
+ # }
1559
+ # }
1560
+ #
1561
+ currencyId = self.safe_string(transfer, 'currency')
1562
+ currencyCode = self.safe_currency_code(currencyId, currency)
1563
+ return {
1564
+ 'info': transfer,
1565
+ 'id': self.safe_string_2(transfer, 'transactionId', 'clientTxId'),
1566
+ 'timestamp': None,
1567
+ 'datetime': None,
1568
+ 'currency': currencyCode,
1569
+ 'amount': None,
1570
+ 'fromAccount': None,
1571
+ 'toAccount': None,
1572
+ 'status': self.parse_transaction_status(self.safe_string(transfer, 'status')),
1503
1573
  }
1504
- response = self.privatePostCancelReplaceOrderPair(self.extend(request, params))
1505
- return self.parse_order(response, market)
1506
1574
 
1507
- def fetch_deposit_address(self, code: str, params={}):
1575
+ def fetch_deposit_address(self, code: str, params={}) -> DepositAddress:
1508
1576
  """
1509
- :see: https://docs.cex.io/#get-crypto-address
1510
1577
  fetch the deposit address for a currency associated with self account
1578
+
1579
+ https://trade.cex.io/docs/#rest-private-api-calls-deposit-address
1580
+
1511
1581
  :param str code: unified currency code
1512
1582
  :param dict [params]: extra parameters specific to the exchange API endpoint
1583
+ :param str [params.accountId]: account-id(default to empty string) to refer to(at self moment, only sub-accounts allowed by exchange)
1513
1584
  :returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
1514
1585
  """
1586
+ accountId = None
1587
+ accountId, params = self.handle_option_and_params(params, 'createOrder', 'accountId')
1588
+ if accountId is None:
1589
+ 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
1590
  self.load_markets()
1591
+ networkCode = None
1592
+ networkCode, params = self.handle_network_code_and_params(params)
1516
1593
  currency = self.currency(code)
1517
- request = {
1518
- 'currency': currency['id'],
1594
+ request: dict = {
1595
+ 'accountId': accountId,
1596
+ 'currency': currency['id'], # documentation is wrong about self param
1597
+ 'blockchain': self.network_code_to_id(networkCode),
1519
1598
  }
1520
- networkCode, query = self.handle_network_code_and_params(params)
1521
- # atm, cex doesn't support network in the request
1522
- response = self.privatePostGetCryptoAddress(self.extend(request, query))
1599
+ response = self.privatePostGetDepositAddress(self.extend(request, params))
1523
1600
  #
1524
1601
  # {
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
- # }
1602
+ # "ok": "ok",
1603
+ # "data": {
1604
+ # "address": "TCr..................1AE",
1605
+ # "accountId": "sub1",
1606
+ # "currency": "USDT",
1607
+ # "blockchain": "tron"
1608
+ # }
1609
+ # }
1542
1610
  #
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')
1611
+ data = self.safe_dict(response, 'data', {})
1612
+ return self.parse_deposit_address(data, currency)
1613
+
1614
+ def parse_deposit_address(self, depositAddress, currency: Currency = None) -> DepositAddress:
1615
+ address = self.safe_string(depositAddress, 'address')
1616
+ currencyId = self.safe_string(depositAddress, 'currency')
1617
+ currency = self.safe_currency(currencyId, currency)
1549
1618
  self.check_address(address)
1550
1619
  return {
1551
- 'currency': code,
1620
+ 'info': depositAddress,
1621
+ 'currency': currency['code'],
1622
+ 'network': self.network_id_to_code(self.safe_string(depositAddress, 'blockchain')),
1552
1623
  'address': address,
1553
- 'tag': self.safe_string_2(addressObject, 'destinationTag', 'memo'),
1554
- 'network': self.network_id_to_code(selectedNetworkId),
1555
- 'info': data,
1624
+ 'tag': None,
1556
1625
  }
1557
1626
 
1558
- def nonce(self):
1559
- return self.milliseconds()
1560
-
1561
1627
  def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
1562
- url = self.urls['api']['rest'] + '/' + self.implode_params(path, params)
1628
+ url = self.urls['api'][api] + '/' + self.implode_params(path, params)
1563
1629
  query = self.omit(params, self.extract_params(path))
1564
1630
  if api == 'public':
1565
- if query:
1566
- url += '?' + self.urlencode(query)
1631
+ if method == 'GET':
1632
+ if query:
1633
+ url += '?' + self.urlencode(query)
1634
+ else:
1635
+ body = self.json(query)
1636
+ headers = {
1637
+ 'Content-Type': 'application/json',
1638
+ }
1567
1639
  else:
1568
1640
  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))
1641
+ seconds = str(self.seconds())
1642
+ body = self.json(query)
1643
+ auth = path + seconds + body
1644
+ signature = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha256, 'base64')
1577
1645
  headers = {
1578
1646
  'Content-Type': 'application/json',
1647
+ 'X-AGGR-KEY': self.apiKey,
1648
+ 'X-AGGR-TIMESTAMP': seconds,
1649
+ 'X-AGGR-SIGNATURE': signature,
1579
1650
  }
1580
1651
  return {'url': url, 'method': method, 'body': body, 'headers': headers}
1581
1652
 
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
1653
+ def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
1654
+ # in some cases, like from createOrder, exchange returns nested escaped JSON string:
1655
+ # {"ok":"ok","data":{"messageType":"executionReport", "orderRejectReason":"{\"code\":405}"}}
1656
+ # and because of `.parseJson` bug, we need extra fix
1587
1657
  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')
1658
+ if body is None:
1659
+ raise NullResponse(self.id + ' returned empty response')
1660
+ elif body[0] == '{':
1661
+ fixed = self.fix_stringified_json_members(body)
1662
+ response = self.parse_json(fixed)
1663
+ else:
1664
+ raise NullResponse(self.id + ' returned unparsed response: ' + body)
1665
+ error = self.safe_string(response, 'error')
1666
+ if error is not None:
1595
1667
  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)
1668
+ self.throw_exactly_matched_exception(self.exceptions['exact'], error, feedback)
1669
+ self.throw_broadly_matched_exception(self.exceptions['broad'], error, feedback)
1598
1670
  raise ExchangeError(feedback)
1671
+ # check errors in order-engine(the responses are not standard, so we parse here)
1672
+ if url.find('do_my_new_order') >= 0:
1673
+ data = self.safe_dict(response, 'data', {})
1674
+ rejectReason = self.safe_string(data, 'rejectReason')
1675
+ if rejectReason is not None:
1676
+ self.throw_broadly_matched_exception(self.exceptions['broad'], rejectReason, rejectReason)
1677
+ raise ExchangeError(self.id + ' createOrder() ' + rejectReason)
1599
1678
  return None