ccxt 4.2.77__py2.py3-none-any.whl → 4.4.49__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 +3205 -937
  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 +1525 -573
  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 +223 -97
  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 +639 -323
  126. ccxt/async_support/digifinex.py +465 -233
  127. ccxt/async_support/ellipx.py +1887 -0
  128. ccxt/async_support/exmo.py +317 -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 +433 -178
  133. ccxt/async_support/hollaex.py +207 -83
  134. ccxt/async_support/htx.py +1095 -563
  135. ccxt/async_support/huobijp.py +178 -56
  136. ccxt/async_support/hyperliquid.py +1678 -292
  137. ccxt/async_support/idex.py +219 -95
  138. ccxt/async_support/independentreserve.py +300 -31
  139. ccxt/async_support/indodax.py +226 -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 +198 -107
  145. ccxt/async_support/latoken.py +199 -79
  146. ccxt/async_support/lbank.py +360 -113
  147. ccxt/async_support/luno.py +185 -62
  148. ccxt/async_support/lykke.py +168 -55
  149. ccxt/async_support/mercado.py +101 -29
  150. ccxt/async_support/mexc.py +995 -429
  151. ccxt/async_support/myokx.py +53 -0
  152. ccxt/async_support/ndax.py +234 -82
  153. ccxt/async_support/novadax.py +195 -75
  154. ccxt/async_support/oceanex.py +244 -59
  155. ccxt/async_support/okcoin.py +301 -165
  156. ccxt/async_support/okx.py +1776 -454
  157. ccxt/async_support/onetrading.py +198 -414
  158. ccxt/async_support/oxfun.py +2898 -0
  159. ccxt/async_support/p2b.py +142 -52
  160. ccxt/async_support/paradex.py +2085 -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 +3205 -937
  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 +1525 -573
  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 +223 -97
  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 +639 -323
  234. ccxt/digifinex.py +465 -233
  235. ccxt/ellipx.py +1887 -0
  236. ccxt/exmo.py +317 -128
  237. ccxt/gate.py +1472 -463
  238. ccxt/gemini.py +206 -84
  239. ccxt/hashkey.py +4164 -0
  240. ccxt/hitbtc.py +433 -178
  241. ccxt/hollaex.py +207 -83
  242. ccxt/htx.py +1095 -563
  243. ccxt/huobijp.py +178 -56
  244. ccxt/hyperliquid.py +1677 -292
  245. ccxt/idex.py +219 -95
  246. ccxt/independentreserve.py +299 -31
  247. ccxt/indodax.py +226 -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 +198 -107
  253. ccxt/latoken.py +199 -79
  254. ccxt/lbank.py +360 -113
  255. ccxt/luno.py +185 -62
  256. ccxt/lykke.py +168 -55
  257. ccxt/mercado.py +101 -29
  258. ccxt/mexc.py +994 -429
  259. ccxt/myokx.py +53 -0
  260. ccxt/ndax.py +234 -82
  261. ccxt/novadax.py +195 -75
  262. ccxt/oceanex.py +244 -59
  263. ccxt/okcoin.py +301 -165
  264. ccxt/okx.py +1776 -454
  265. ccxt/onetrading.py +198 -414
  266. ccxt/oxfun.py +2897 -0
  267. ccxt/p2b.py +142 -52
  268. ccxt/paradex.py +2085 -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 +143 -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.49.dist-info/LICENSE.txt +21 -0
  496. ccxt-4.4.49.dist-info/METADATA +646 -0
  497. ccxt-4.4.49.dist-info/RECORD +669 -0
  498. {ccxt-4.2.77.dist-info → ccxt-4.4.49.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.49.dist-info}/top_level.txt +0 -0
@@ -5,10 +5,14 @@
5
5
 
6
6
  from ccxt.async_support.base.exchange import Exchange
7
7
  from ccxt.abstract.hyperliquid import ImplicitAPI
8
- from ccxt.base.types import Balances, Int, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Str, Strings, Trade, TransferEntry
8
+ import asyncio
9
+ import math
10
+ from ccxt.base.types import Balances, Currencies, Currency, Int, LedgerEntry, MarginModification, Market, Num, Order, OrderBook, OrderRequest, CancellationRequest, OrderSide, OrderType, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade, TradingFeeInterface, Transaction, TransferEntry
9
11
  from typing import List
10
12
  from ccxt.base.errors import ExchangeError
11
13
  from ccxt.base.errors import ArgumentsRequired
14
+ from ccxt.base.errors import BadRequest
15
+ from ccxt.base.errors import InsufficientFunds
12
16
  from ccxt.base.errors import InvalidOrder
13
17
  from ccxt.base.errors import OrderNotFound
14
18
  from ccxt.base.errors import NotSupported
@@ -28,11 +32,12 @@ class hyperliquid(Exchange, ImplicitAPI):
28
32
  'countries': [],
29
33
  'version': 'v1',
30
34
  'rateLimit': 50, # 1200 requests per minute, 20 request per second
31
- 'certified': False,
35
+ 'certified': True,
32
36
  'pro': True,
37
+ 'dex': True,
33
38
  'has': {
34
39
  'CORS': None,
35
- 'spot': False,
40
+ 'spot': True,
36
41
  'margin': False,
37
42
  'swap': True,
38
43
  'future': True,
@@ -41,8 +46,10 @@ class hyperliquid(Exchange, ImplicitAPI):
41
46
  'borrowCrossMargin': False,
42
47
  'borrowIsolatedMargin': False,
43
48
  'cancelAllOrders': False,
49
+ 'cancelAllOrdersAfter': True,
44
50
  'cancelOrder': True,
45
51
  'cancelOrders': True,
52
+ 'cancelOrdersForSymbols': True,
46
53
  'closeAllPositions': False,
47
54
  'closePosition': False,
48
55
  'createMarketBuyOrderWithCost': False,
@@ -50,31 +57,34 @@ class hyperliquid(Exchange, ImplicitAPI):
50
57
  'createMarketSellOrderWithCost': False,
51
58
  'createOrder': True,
52
59
  'createOrders': True,
53
- 'createReduceOnlyOrder': False,
60
+ 'createReduceOnlyOrder': True,
61
+ 'createStopOrder': True,
62
+ 'createTriggerOrder': True,
54
63
  'editOrder': True,
55
64
  'fetchAccounts': False,
56
65
  'fetchBalance': True,
57
66
  'fetchBorrowInterest': False,
58
67
  'fetchBorrowRateHistories': False,
59
68
  'fetchBorrowRateHistory': False,
60
- 'fetchCanceledOrders': False,
69
+ 'fetchCanceledAndClosedOrders': True,
70
+ 'fetchCanceledOrders': True,
61
71
  'fetchClosedOrders': True,
62
72
  'fetchCrossBorrowRate': False,
63
73
  'fetchCrossBorrowRates': False,
64
74
  'fetchCurrencies': True,
65
75
  'fetchDepositAddress': False,
66
76
  'fetchDepositAddresses': False,
67
- 'fetchDeposits': False,
77
+ 'fetchDeposits': True,
68
78
  'fetchDepositWithdrawFee': 'emulated',
69
79
  'fetchDepositWithdrawFees': False,
70
80
  'fetchFundingHistory': False,
71
81
  'fetchFundingRate': False,
72
82
  'fetchFundingRateHistory': True,
73
- 'fetchFundingRates': False,
83
+ 'fetchFundingRates': True,
74
84
  'fetchIndexOHLCV': False,
75
85
  'fetchIsolatedBorrowRate': False,
76
86
  'fetchIsolatedBorrowRates': False,
77
- 'fetchLedger': False,
87
+ 'fetchLedger': True,
78
88
  'fetchLeverage': False,
79
89
  'fetchLeverageTiers': False,
80
90
  'fetchLiquidations': False,
@@ -85,31 +95,33 @@ class hyperliquid(Exchange, ImplicitAPI):
85
95
  'fetchMyLiquidations': False,
86
96
  'fetchMyTrades': True,
87
97
  'fetchOHLCV': True,
88
- 'fetchOpenInterest': False,
98
+ 'fetchOpenInterest': True,
89
99
  'fetchOpenInterestHistory': False,
100
+ 'fetchOpenInterests': True,
90
101
  'fetchOpenOrders': True,
91
102
  'fetchOrder': True,
92
103
  'fetchOrderBook': True,
93
- 'fetchOrders': False,
104
+ 'fetchOrders': True,
94
105
  'fetchOrderTrades': False,
95
106
  'fetchPosition': True,
96
107
  'fetchPositionMode': False,
97
108
  'fetchPositions': True,
98
109
  'fetchPositionsRisk': False,
99
110
  'fetchPremiumIndexOHLCV': False,
100
- 'fetchTicker': False,
101
- 'fetchTickers': False,
111
+ 'fetchTicker': 'emulated',
112
+ 'fetchTickers': True,
102
113
  'fetchTime': False,
103
114
  'fetchTrades': True,
104
- 'fetchTradingFee': False,
115
+ 'fetchTradingFee': True,
105
116
  'fetchTradingFees': False,
106
117
  'fetchTransfer': False,
107
118
  'fetchTransfers': False,
108
119
  'fetchWithdrawal': False,
109
- 'fetchWithdrawals': False,
120
+ 'fetchWithdrawals': True,
110
121
  'reduceMargin': True,
111
122
  'repayCrossMargin': False,
112
123
  'repayIsolatedMargin': False,
124
+ 'sandbox': True,
113
125
  'setLeverage': True,
114
126
  'setMarginMode': True,
115
127
  'setPositionMode': False,
@@ -125,12 +137,12 @@ class hyperliquid(Exchange, ImplicitAPI):
125
137
  '1h': '1h',
126
138
  '2h': '2h',
127
139
  '4h': '4h',
128
- '6h': '6h',
140
+ '8h': '8h',
129
141
  '12h': '12h',
130
142
  '1d': '1d',
131
143
  '3d': '3d',
132
144
  '1w': '1w',
133
- '1M': '1m',
145
+ '1M': '1M',
134
146
  },
135
147
  'hostname': 'hyperliquid.xyz',
136
148
  'urls': {
@@ -151,7 +163,17 @@ class hyperliquid(Exchange, ImplicitAPI):
151
163
  'api': {
152
164
  'public': {
153
165
  'post': {
154
- 'info': 1,
166
+ 'info': {
167
+ 'cost': 20,
168
+ 'byType': {
169
+ 'l2Book': 2,
170
+ 'allMids': 2,
171
+ 'clearinghouseState': 2,
172
+ 'orderStatus': 2,
173
+ 'spotClearinghouseState': 2,
174
+ 'exchangeStatus': 2,
175
+ },
176
+ },
155
177
  },
156
178
  },
157
179
  'private': {
@@ -165,6 +187,10 @@ class hyperliquid(Exchange, ImplicitAPI):
165
187
  'taker': self.parse_number('0.00035'),
166
188
  'maker': self.parse_number('0.0001'),
167
189
  },
190
+ 'spot': {
191
+ 'taker': self.parse_number('0.00035'),
192
+ 'maker': self.parse_number('0.0001'),
193
+ },
168
194
  },
169
195
  'requiredCredentials': {
170
196
  'apiKey': False,
@@ -186,30 +212,133 @@ class hyperliquid(Exchange, ImplicitAPI):
186
212
  'No liquidity available for market order.': InvalidOrder,
187
213
  'Order was never placed, already canceled, or filled.': OrderNotFound,
188
214
  'User or API Wallet ': InvalidOrder,
215
+ 'Order has invalid size': InvalidOrder,
216
+ 'Order price cannot be more than 80% away from the reference price': InvalidOrder,
217
+ 'Order has zero size.': InvalidOrder,
218
+ 'Insufficient spot balance asset': InsufficientFunds,
219
+ 'Insufficient balance for withdrawal': InsufficientFunds,
220
+ 'Insufficient balance for token transfer': InsufficientFunds,
189
221
  },
190
222
  },
191
223
  'precisionMode': TICK_SIZE,
192
224
  'commonCurrencies': {
193
225
  },
194
226
  'options': {
227
+ 'defaultType': 'swap',
195
228
  'sandboxMode': False,
196
229
  'defaultSlippage': 0.05,
197
230
  'zeroAddress': '0x0000000000000000000000000000000000000000',
198
231
  },
232
+ 'features': {
233
+ 'default': {
234
+ 'sandbox': True,
235
+ 'createOrder': {
236
+ 'marginMode': False,
237
+ 'triggerPrice': False,
238
+ 'triggerPriceType': None,
239
+ 'triggerDirection': False,
240
+ 'stopLossPrice': False,
241
+ 'takeProfitPrice': False,
242
+ 'attachedStopLossTakeProfit': None,
243
+ 'timeInForce': {
244
+ 'IOC': True,
245
+ 'FOK': False,
246
+ 'PO': True,
247
+ 'GTD': False,
248
+ },
249
+ 'hedged': False,
250
+ 'trailing': False,
251
+ 'leverage': False,
252
+ 'marketBuyByCost': False,
253
+ 'marketBuyRequiresPrice': False,
254
+ 'selfTradePrevention': False,
255
+ 'iceberg': False,
256
+ },
257
+ 'createOrders': {
258
+ 'max': 1000,
259
+ },
260
+ 'fetchMyTrades': {
261
+ 'marginMode': False,
262
+ 'limit': 2000,
263
+ 'daysBack': None,
264
+ 'untilDays': None,
265
+ },
266
+ 'fetchOrder': {
267
+ 'marginMode': False,
268
+ 'trigger': False,
269
+ 'trailing': False,
270
+ },
271
+ 'fetchOpenOrders': {
272
+ 'marginMode': False,
273
+ 'limit': 2000,
274
+ 'trigger': False,
275
+ 'trailing': False,
276
+ },
277
+ 'fetchOrders': {
278
+ 'marginMode': False,
279
+ 'limit': 2000,
280
+ 'daysBack': None,
281
+ 'untilDays': None,
282
+ 'trigger': False,
283
+ 'trailing': False,
284
+ },
285
+ 'fetchClosedOrders': {
286
+ 'marginMode': False,
287
+ 'limit': 2000,
288
+ 'daysBack': None,
289
+ 'daysBackCanceled': None,
290
+ 'untilDays': None,
291
+ 'trigger': False,
292
+ 'trailing': False,
293
+ },
294
+ 'fetchOHLCV': {
295
+ 'limit': 5000,
296
+ },
297
+ },
298
+ 'spot': {
299
+ 'extends': 'default',
300
+ },
301
+ 'forPerps': {
302
+ 'extends': 'default',
303
+ 'createOrder': {
304
+ 'stopLossPrice': True,
305
+ 'takeProfitPrice': True,
306
+ 'attachedStopLossTakeProfit': None, # todo, in two orders
307
+ },
308
+ },
309
+ 'swap': {
310
+ 'linear': {
311
+ 'extends': 'forPerps',
312
+ },
313
+ 'inverse': {
314
+ 'extends': 'forPerps',
315
+ },
316
+ },
317
+ 'future': {
318
+ 'linear': {
319
+ 'extends': 'forPerps',
320
+ },
321
+ 'inverse': {
322
+ 'extends': 'forPerps',
323
+ },
324
+ },
325
+ },
199
326
  })
200
327
 
201
328
  def set_sandbox_mode(self, enabled):
202
329
  super(hyperliquid, self).set_sandbox_mode(enabled)
203
330
  self.options['sandboxMode'] = enabled
204
331
 
205
- async def fetch_currencies(self, params={}):
332
+ async def fetch_currencies(self, params={}) -> Currencies:
206
333
  """
207
334
  fetches all available currencies on an exchange
208
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-exchange-metadata
335
+
336
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-metadata
337
+
209
338
  :param dict [params]: extra parameters specific to the exchange API endpoint
210
339
  :returns dict: an associative dictionary of currencies
211
340
  """
212
- request = {
341
+ request: dict = {
213
342
  'type': 'meta',
214
343
  }
215
344
  response = await self.publicPostInfo(self.extend(request, params))
@@ -228,7 +357,7 @@ class hyperliquid(Exchange, ImplicitAPI):
228
357
  # ]
229
358
  #
230
359
  meta = self.safe_list(response, 'universe', [])
231
- result = {}
360
+ result: dict = {}
232
361
  for i in range(0, len(meta)):
233
362
  data = self.safe_dict(meta, i, {})
234
363
  id = i
@@ -245,19 +374,48 @@ class hyperliquid(Exchange, ImplicitAPI):
245
374
  'withdraw': None,
246
375
  'networks': None,
247
376
  'fee': None,
248
- # 'fees': fees,
249
- 'limits': None,
377
+ 'limits': {
378
+ 'amount': {
379
+ 'min': None,
380
+ 'max': None,
381
+ },
382
+ 'withdraw': {
383
+ 'min': None,
384
+ 'max': None,
385
+ },
386
+ },
250
387
  }
251
388
  return result
252
389
 
253
- async def fetch_markets(self, params={}):
390
+ async def fetch_markets(self, params={}) -> List[Market]:
254
391
  """
255
392
  retrieves data on all markets for hyperliquid
256
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-asset-contexts-includes-mark-price-current-funding-open-interest-etc
393
+
394
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-asset-contexts-includes-mark-price-current-funding-open-interest-etc
395
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-spot-asset-contexts
396
+
257
397
  :param dict [params]: extra parameters specific to the exchange API endpoint
258
398
  :returns dict[]: an array of objects representing market data
259
399
  """
260
- request = {
400
+ rawPromises = [
401
+ self.fetch_swap_markets(params),
402
+ self.fetch_spot_markets(params),
403
+ ]
404
+ promises = await asyncio.gather(*rawPromises)
405
+ swapMarkets = promises[0]
406
+ spotMarkets = promises[1]
407
+ return self.array_concat(swapMarkets, spotMarkets)
408
+
409
+ async def fetch_swap_markets(self, params={}) -> List[Market]:
410
+ """
411
+ retrieves data on all swap markets for hyperliquid
412
+
413
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-asset-contexts-includes-mark-price-current-funding-open-interest-etc
414
+
415
+ :param dict [params]: extra parameters specific to the exchange API endpoint
416
+ :returns dict[]: an array of objects representing market data
417
+ """
418
+ request: dict = {
261
419
  'type': 'metaAndAssetCtxs',
262
420
  }
263
421
  response = await self.publicPostInfo(self.extend(request, params))
@@ -291,20 +449,213 @@ class hyperliquid(Exchange, ImplicitAPI):
291
449
  # ]
292
450
  # ]
293
451
  #
452
+ #
294
453
  meta = self.safe_dict(response, 0, {})
295
- meta = self.safe_list(meta, 'universe', [])
296
- assetCtxs = self.safe_dict(response, 1, {})
454
+ universe = self.safe_list(meta, 'universe', [])
455
+ assetCtxs = self.safe_list(response, 1, [])
297
456
  result = []
298
- for i in range(0, len(meta)):
457
+ for i in range(0, len(universe)):
299
458
  data = self.extend(
300
- self.safe_dict(meta, i, {}),
459
+ self.safe_dict(universe, i, {}),
301
460
  self.safe_dict(assetCtxs, i, {})
302
461
  )
303
462
  data['baseId'] = i
304
463
  result.append(data)
305
464
  return self.parse_markets(result)
306
465
 
307
- def parse_market(self, market) -> Market:
466
+ def calculate_price_precision(self, price: float, amountPrecision: float, maxDecimals: float):
467
+ """
468
+ Helper function to calculate the Hyperliquid DECIMAL_PLACES price precision
469
+ :param float price: the price to use in the calculation
470
+ :param int amountPrecision: the amountPrecision to use in the calculation
471
+ :param int maxDecimals: the maxDecimals to use in the calculation
472
+ :returns int: The calculated price precision
473
+ """
474
+ pricePrecision = 0
475
+ priceStr = self.number_to_string(price)
476
+ if priceStr is None:
477
+ return 0
478
+ priceSplitted = priceStr.split('.')
479
+ if Precise.string_eq(priceStr, '0'):
480
+ # Significant digits is always hasattr(self, 5) case
481
+ significantDigits = 5
482
+ # Integer digits is always hasattr(self, 0) case(0 doesn't count)
483
+ integerDigits = 0
484
+ # Calculate the price precision
485
+ pricePrecision = min(maxDecimals - amountPrecision, significantDigits - integerDigits)
486
+ elif Precise.string_gt(priceStr, '0') and Precise.string_lt(priceStr, '1'):
487
+ # Significant digits, always hasattr(self, 5) case
488
+ significantDigits = 5
489
+ # Get the part after the decimal separator
490
+ decimalPart = self.safe_string(priceSplitted, 1, '')
491
+ # Count the number of leading zeros in the decimal part
492
+ leadingZeros = 0
493
+ while((leadingZeros <= len(decimalPart)) and (decimalPart[leadingZeros] == '0')):
494
+ leadingZeros = leadingZeros + 1
495
+ # Calculate price precision based on leading zeros and significant digits
496
+ pricePrecision = leadingZeros + significantDigits
497
+ # Calculate the price precision based on maxDecimals - szDecimals and the calculated price precision from the previous step
498
+ pricePrecision = min(maxDecimals - amountPrecision, pricePrecision)
499
+ else:
500
+ # Count the numbers before the decimal separator
501
+ integerPart = self.safe_string(priceSplitted, 0, '')
502
+ # Get significant digits, take the max() of 5 and the integer digits count
503
+ significantDigits = max(5, len(integerPart))
504
+ # Calculate price precision based on maxDecimals - szDecimals and significantDigits - len(integerPart)
505
+ pricePrecision = min(maxDecimals - amountPrecision, significantDigits - len(integerPart))
506
+ return self.parse_to_int(pricePrecision)
507
+
508
+ async def fetch_spot_markets(self, params={}) -> List[Market]:
509
+ """
510
+ retrieves data on all spot markets for hyperliquid
511
+
512
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-spot-asset-contexts
513
+
514
+ :param dict [params]: extra parameters specific to the exchange API endpoint
515
+ :returns dict[]: an array of objects representing market data
516
+ """
517
+ request: dict = {
518
+ 'type': 'spotMetaAndAssetCtxs',
519
+ }
520
+ response = await self.publicPostInfo(self.extend(request, params))
521
+ #
522
+ # [
523
+ # {
524
+ # "tokens": [
525
+ # {
526
+ # "name": "USDC",
527
+ # "szDecimals": 8,
528
+ # "weiDecimals" 8,
529
+ # "index": 0,
530
+ # "tokenId": "0x6d1e7cde53ba9467b783cb7c530ce054",
531
+ # "isCanonical": True,
532
+ # "evmContract":null,
533
+ # "fullName":null
534
+ # },
535
+ # {
536
+ # "name": "PURR",
537
+ # "szDecimals": 0,
538
+ # "weiDecimals": 5,
539
+ # "index": 1,
540
+ # "tokenId": "0xc1fb593aeffbeb02f85e0308e9956a90",
541
+ # "isCanonical": True,
542
+ # "evmContract":null,
543
+ # "fullName":null
544
+ # }
545
+ # ],
546
+ # "universe": [
547
+ # {
548
+ # "name": "PURR/USDC",
549
+ # "tokens": [1, 0],
550
+ # "index": 0,
551
+ # "isCanonical": True
552
+ # }
553
+ # ]
554
+ # },
555
+ # [
556
+ # {
557
+ # "dayNtlVlm":"8906.0",
558
+ # "markPx":"0.14",
559
+ # "midPx":"0.209265",
560
+ # "prevDayPx":"0.20432"
561
+ # }
562
+ # ]
563
+ # ]
564
+ #
565
+ first = self.safe_dict(response, 0, {})
566
+ second = self.safe_list(response, 1, [])
567
+ meta = self.safe_list(first, 'universe', [])
568
+ tokens = self.safe_list(first, 'tokens', [])
569
+ markets = []
570
+ for i in range(0, len(meta)):
571
+ market = self.safe_dict(meta, i, {})
572
+ index = self.safe_integer(market, 'index')
573
+ extraData = self.safe_dict(second, index, {})
574
+ marketName = self.safe_string(market, 'name')
575
+ # if marketName.find('/') < 0:
576
+ # # there are some weird spot markets in testnet, eg @2
577
+ # continue
578
+ # }
579
+ # marketParts = marketName.split('/')
580
+ # baseName = self.safe_string(marketParts, 0)
581
+ # quoteId = self.safe_string(marketParts, 1)
582
+ fees = self.safe_dict(self.fees, 'spot', {})
583
+ taker = self.safe_number(fees, 'taker')
584
+ maker = self.safe_number(fees, 'maker')
585
+ tokensPos = self.safe_list(market, 'tokens', [])
586
+ baseTokenPos = self.safe_integer(tokensPos, 0)
587
+ quoteTokenPos = self.safe_integer(tokensPos, 1)
588
+ baseTokenInfo = self.safe_dict(tokens, baseTokenPos, {})
589
+ quoteTokenInfo = self.safe_dict(tokens, quoteTokenPos, {})
590
+ baseName = self.safe_string(baseTokenInfo, 'name')
591
+ quoteId = self.safe_string(quoteTokenInfo, 'name')
592
+ base = self.safe_currency_code(baseName)
593
+ quote = self.safe_currency_code(quoteId)
594
+ symbol = base + '/' + quote
595
+ innerBaseTokenInfo = self.safe_dict(baseTokenInfo, 'spec', baseTokenInfo)
596
+ # innerQuoteTokenInfo = self.safe_dict(quoteTokenInfo, 'spec', quoteTokenInfo)
597
+ amountPrecisionStr = self.safe_string(innerBaseTokenInfo, 'szDecimals')
598
+ amountPrecision = int(amountPrecisionStr)
599
+ price = self.safe_number(extraData, 'midPx')
600
+ pricePrecision = self.calculate_price_precision(price, amountPrecision, 8)
601
+ pricePrecisionStr = self.number_to_string(pricePrecision)
602
+ # quotePrecision = self.parse_number(self.parse_precision(self.safe_string(innerQuoteTokenInfo, 'szDecimals')))
603
+ baseId = self.number_to_string(index + 10000)
604
+ markets.append(self.safe_market_structure({
605
+ 'id': marketName,
606
+ 'symbol': symbol,
607
+ 'base': base,
608
+ 'quote': quote,
609
+ 'settle': None,
610
+ 'baseId': baseId,
611
+ 'quoteId': quoteId,
612
+ 'settleId': None,
613
+ 'type': 'spot',
614
+ 'spot': True,
615
+ 'subType': None,
616
+ 'margin': None,
617
+ 'swap': False,
618
+ 'future': False,
619
+ 'option': False,
620
+ 'active': True,
621
+ 'contract': False,
622
+ 'linear': None,
623
+ 'inverse': None,
624
+ 'taker': taker,
625
+ 'maker': maker,
626
+ 'contractSize': None,
627
+ 'expiry': None,
628
+ 'expiryDatetime': None,
629
+ 'strike': None,
630
+ 'optionType': None,
631
+ 'precision': {
632
+ 'amount': self.parse_number(self.parse_precision(amountPrecisionStr)),
633
+ 'price': self.parse_number(self.parse_precision(pricePrecisionStr)),
634
+ },
635
+ 'limits': {
636
+ 'leverage': {
637
+ 'min': None,
638
+ 'max': None,
639
+ },
640
+ 'amount': {
641
+ 'min': None,
642
+ 'max': None,
643
+ },
644
+ 'price': {
645
+ 'min': None,
646
+ 'max': None,
647
+ },
648
+ 'cost': {
649
+ 'min': self.parse_number('10'),
650
+ 'max': None,
651
+ },
652
+ },
653
+ 'created': None,
654
+ 'info': self.extend(extraData, market),
655
+ }))
656
+ return markets
657
+
658
+ def parse_market(self, market: dict) -> Market:
308
659
  #
309
660
  # {
310
661
  # "maxLeverage": "50",
@@ -340,7 +691,16 @@ class hyperliquid(Exchange, ImplicitAPI):
340
691
  fees = self.safe_dict(self.fees, 'swap', {})
341
692
  taker = self.safe_number(fees, 'taker')
342
693
  maker = self.safe_number(fees, 'maker')
343
- return {
694
+ amountPrecisionStr = self.safe_string(market, 'szDecimals')
695
+ amountPrecision = int(amountPrecisionStr)
696
+ price = self.safe_number(market, 'markPx', 0)
697
+ pricePrecision = self.calculate_price_precision(price, amountPrecision, 6)
698
+ pricePrecisionStr = self.number_to_string(pricePrecision)
699
+ isDelisted = self.safe_bool(market, 'isDelisted')
700
+ active = True
701
+ if isDelisted is not None:
702
+ active = not isDelisted
703
+ return self.safe_market_structure({
344
704
  'id': baseId,
345
705
  'symbol': symbol,
346
706
  'base': base,
@@ -355,7 +715,7 @@ class hyperliquid(Exchange, ImplicitAPI):
355
715
  'swap': swap,
356
716
  'future': False,
357
717
  'option': False,
358
- 'active': True,
718
+ 'active': active,
359
719
  'contract': contract,
360
720
  'linear': True,
361
721
  'inverse': False,
@@ -367,13 +727,13 @@ class hyperliquid(Exchange, ImplicitAPI):
367
727
  'strike': None,
368
728
  'optionType': None,
369
729
  'precision': {
370
- 'amount': self.parse_number(self.parse_precision(self.safe_string(market, 'szDecimals'))), # decimal places
371
- 'price': 5, # significant digits
730
+ 'amount': self.parse_number(self.parse_precision(amountPrecisionStr)),
731
+ 'price': self.parse_number(self.parse_precision(pricePrecisionStr)),
372
732
  },
373
733
  'limits': {
374
734
  'leverage': {
375
735
  'min': None,
376
- 'max': None,
736
+ 'max': self.safe_integer(market, 'maxLeverage'),
377
737
  },
378
738
  'amount': {
379
739
  'min': None,
@@ -384,26 +744,34 @@ class hyperliquid(Exchange, ImplicitAPI):
384
744
  'max': None,
385
745
  },
386
746
  'cost': {
387
- 'min': None,
747
+ 'min': self.parse_number('10'),
388
748
  'max': None,
389
749
  },
390
750
  },
391
751
  'created': None,
392
752
  'info': market,
393
- }
753
+ })
394
754
 
395
755
  async def fetch_balance(self, params={}) -> Balances:
396
756
  """
397
757
  query for balance and get the amount of funds available for trading or funds locked in orders
398
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-state
758
+
759
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-a-users-token-balances
760
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-users-perpetuals-account-summary
761
+
399
762
  :param dict [params]: extra parameters specific to the exchange API endpoint
400
763
  :param str [params.user]: user address, will default to self.walletAddress if not provided
764
+ :param str [params.type]: wallet type, ['spot', 'swap'], defaults to swap
401
765
  :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
402
766
  """
403
767
  userAddress = None
404
768
  userAddress, params = self.handle_public_address('fetchBalance', params)
405
- request = {
406
- 'type': 'clearinghouseState',
769
+ type = None
770
+ type, params = self.handle_market_type_and_params('fetchBalance', None, params)
771
+ isSpot = (type == 'spot')
772
+ reqType = 'spotClearinghouseState' if (isSpot) else 'clearinghouseState'
773
+ request: dict = {
774
+ 'type': reqType,
407
775
  'user': userAddress,
408
776
  }
409
777
  response = await self.publicPostInfo(self.extend(request, params))
@@ -426,13 +794,41 @@ class hyperliquid(Exchange, ImplicitAPI):
426
794
  # "time": "1704261007014",
427
795
  # "withdrawable": "100.0"
428
796
  # }
797
+ # spot
429
798
  #
799
+ # {
800
+ # "balances":[
801
+ # {
802
+ # "coin":"USDC",
803
+ # "hold":"0.0",
804
+ # "total":"1481.844"
805
+ # },
806
+ # {
807
+ # "coin":"PURR",
808
+ # "hold":"0.0",
809
+ # "total":"999.65004"
810
+ # }
811
+ # }
812
+ #
813
+ balances = self.safe_list(response, 'balances')
814
+ if balances is not None:
815
+ spotBalances: dict = {'info': response}
816
+ for i in range(0, len(balances)):
817
+ balance = balances[i]
818
+ code = self.safe_currency_code(self.safe_string(balance, 'coin'))
819
+ account = self.account()
820
+ total = self.safe_string(balance, 'total')
821
+ used = self.safe_string(balance, 'hold')
822
+ account['total'] = total
823
+ account['used'] = used
824
+ spotBalances[code] = account
825
+ return self.safe_balance(spotBalances)
430
826
  data = self.safe_dict(response, 'marginSummary', {})
431
- result = {
827
+ result: dict = {
432
828
  'info': response,
433
829
  'USDC': {
434
- 'total': self.safe_float(data, 'accountValue'),
435
- 'used': self.safe_float(data, 'totalMarginUsed'),
830
+ 'total': self.safe_number(data, 'accountValue'),
831
+ 'free': self.safe_number(response, 'withdrawable'),
436
832
  },
437
833
  }
438
834
  timestamp = self.safe_integer(response, 'time')
@@ -443,7 +839,9 @@ class hyperliquid(Exchange, ImplicitAPI):
443
839
  async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
444
840
  """
445
841
  fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
446
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#info
842
+
843
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#l2-book-snapshot
844
+
447
845
  :param str symbol: unified symbol of the market to fetch the order book for
448
846
  :param int [limit]: the maximum amount of order book entries to return
449
847
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -451,9 +849,9 @@ class hyperliquid(Exchange, ImplicitAPI):
451
849
  """
452
850
  await self.load_markets()
453
851
  market = self.market(symbol)
454
- request = {
852
+ request: dict = {
455
853
  'type': 'l2Book',
456
- 'coin': market['base'],
854
+ 'coin': market['base'] if market['swap'] else market['id'],
457
855
  }
458
856
  response = await self.publicPostInfo(self.extend(request, params))
459
857
  #
@@ -479,17 +877,188 @@ class hyperliquid(Exchange, ImplicitAPI):
479
877
  # }
480
878
  #
481
879
  data = self.safe_list(response, 'levels', [])
482
- result = {
880
+ result: dict = {
483
881
  'bids': self.safe_list(data, 0, []),
484
882
  'asks': self.safe_list(data, 1, []),
485
883
  }
486
884
  timestamp = self.safe_integer(response, 'time')
487
885
  return self.parse_order_book(result, market['symbol'], timestamp, 'bids', 'asks', 'px', 'sz')
488
886
 
887
+ async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
888
+ """
889
+ fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
890
+
891
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-asset-contexts-includes-mark-price-current-funding-open-interest-etc
892
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-spot-asset-contexts
893
+
894
+ :param str[] [symbols]: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
895
+ :param dict [params]: extra parameters specific to the exchange API endpoint
896
+ :param str [params.type]: 'spot' or 'swap', by default fetches both
897
+ :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
898
+ """
899
+ await self.load_markets()
900
+ symbols = self.market_symbols(symbols)
901
+ # at self stage, to get tickers data, we use fetchMarkets endpoints
902
+ response = []
903
+ type = self.safe_string(params, 'type')
904
+ params = self.omit(params, 'type')
905
+ if type == 'spot':
906
+ response = await self.fetch_spot_markets(params)
907
+ elif type == 'swap':
908
+ response = await self.fetch_swap_markets(params)
909
+ else:
910
+ response = await self.fetch_markets(params)
911
+ # same response "fetchMarkets"
912
+ result: dict = {}
913
+ for i in range(0, len(response)):
914
+ market = response[i]
915
+ info = market['info']
916
+ ticker = self.parse_ticker(info, market)
917
+ symbol = self.safe_string(ticker, 'symbol')
918
+ result[symbol] = ticker
919
+ return self.filter_by_array_tickers(result, 'symbol', symbols)
920
+
921
+ async def fetch_funding_rates(self, symbols: Strings = None, params={}) -> FundingRates:
922
+ """
923
+ retrieves data on all swap markets for hyperliquid
924
+
925
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-asset-contexts-includes-mark-price-current-funding-open-interest-etc
926
+
927
+ :param str[] [symbols]: list of unified market symbols
928
+ :param dict [params]: extra parameters specific to the exchange API endpoint
929
+ :returns dict[]: an array of objects representing market data
930
+ """
931
+ request: dict = {
932
+ 'type': 'metaAndAssetCtxs',
933
+ }
934
+ response = await self.publicPostInfo(self.extend(request, params))
935
+ #
936
+ # [
937
+ # {
938
+ # "universe": [
939
+ # {
940
+ # "maxLeverage": 50,
941
+ # "name": "SOL",
942
+ # "onlyIsolated": False,
943
+ # "szDecimals": 2
944
+ # }
945
+ # ]
946
+ # },
947
+ # [
948
+ # {
949
+ # "dayNtlVlm": "9450588.2273",
950
+ # "funding": "0.0000198",
951
+ # "impactPxs": [
952
+ # "108.04",
953
+ # "108.06"
954
+ # ],
955
+ # "markPx": "108.04",
956
+ # "midPx": "108.05",
957
+ # "openInterest": "10764.48",
958
+ # "oraclePx": "107.99",
959
+ # "premium": "0.00055561",
960
+ # "prevDayPx": "111.81"
961
+ # }
962
+ # ]
963
+ # ]
964
+ #
965
+ #
966
+ meta = self.safe_dict(response, 0, {})
967
+ universe = self.safe_list(meta, 'universe', [])
968
+ assetCtxs = self.safe_list(response, 1, [])
969
+ result = []
970
+ for i in range(0, len(universe)):
971
+ data = self.extend(
972
+ self.safe_dict(universe, i, {}),
973
+ self.safe_dict(assetCtxs, i, {})
974
+ )
975
+ result.append(data)
976
+ return self.parse_funding_rates(result, symbols)
977
+
978
+ def parse_funding_rate(self, info, market: Market = None) -> FundingRate:
979
+ #
980
+ # {
981
+ # "maxLeverage": "50",
982
+ # "name": "ETH",
983
+ # "onlyIsolated": False,
984
+ # "szDecimals": "4",
985
+ # "dayNtlVlm": "1709813.11535",
986
+ # "funding": "0.00004807",
987
+ # "impactPxs": [
988
+ # "2369.3",
989
+ # "2369.6"
990
+ # ],
991
+ # "markPx": "2369.6",
992
+ # "midPx": "2369.45",
993
+ # "openInterest": "1815.4712",
994
+ # "oraclePx": "2367.3",
995
+ # "premium": "0.00090821",
996
+ # "prevDayPx": "2381.5"
997
+ # }
998
+ #
999
+ base = self.safe_string(info, 'name')
1000
+ marketId = self.coin_to_market_id(base)
1001
+ symbol = self.safe_symbol(marketId, market)
1002
+ funding = self.safe_number(info, 'funding')
1003
+ markPx = self.safe_number(info, 'markPx')
1004
+ oraclePx = self.safe_number(info, 'oraclePx')
1005
+ fundingTimestamp = (int(math.floor(self.milliseconds()) / 60 / 60 / 1000) + 1) * 60 * 60 * 1000
1006
+ return {
1007
+ 'info': info,
1008
+ 'symbol': symbol,
1009
+ 'markPrice': markPx,
1010
+ 'indexPrice': oraclePx,
1011
+ 'interestRate': None,
1012
+ 'estimatedSettlePrice': None,
1013
+ 'timestamp': None,
1014
+ 'datetime': None,
1015
+ 'fundingRate': funding,
1016
+ 'fundingTimestamp': fundingTimestamp,
1017
+ 'fundingDatetime': self.iso8601(fundingTimestamp),
1018
+ 'nextFundingRate': None,
1019
+ 'nextFundingTimestamp': None,
1020
+ 'nextFundingDatetime': None,
1021
+ 'previousFundingRate': None,
1022
+ 'previousFundingTimestamp': None,
1023
+ 'previousFundingDatetime': None,
1024
+ 'interval': '1h',
1025
+ }
1026
+
1027
+ def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
1028
+ #
1029
+ # {
1030
+ # "prevDayPx": "3400.5",
1031
+ # "dayNtlVlm": "511297257.47936022",
1032
+ # "markPx": "3464.7",
1033
+ # "midPx": "3465.05",
1034
+ # "oraclePx": "3460.1", # only in swap
1035
+ # "openInterest": "64638.1108", # only in swap
1036
+ # "premium": "0.00141614", # only in swap
1037
+ # "funding": "0.00008727", # only in swap
1038
+ # "impactPxs": ["3465.0", "3465.1"], # only in swap
1039
+ # "coin": "PURR", # only in spot
1040
+ # "circulatingSupply": "998949190.03400207", # only in spot
1041
+ # },
1042
+ #
1043
+ bidAsk = self.safe_list(ticker, 'impactPxs')
1044
+ return self.safe_ticker({
1045
+ 'symbol': market['symbol'],
1046
+ 'timestamp': None,
1047
+ 'datetime': None,
1048
+ 'previousClose': self.safe_number(ticker, 'prevDayPx'),
1049
+ 'close': self.safe_number(ticker, 'midPx'),
1050
+ 'bid': self.safe_number(bidAsk, 0),
1051
+ 'ask': self.safe_number(bidAsk, 1),
1052
+ 'quoteVolume': self.safe_number(ticker, 'dayNtlVlm'),
1053
+ 'info': ticker,
1054
+ }, market)
1055
+
489
1056
  async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
490
1057
  """
491
1058
  fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
492
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#info-1
1059
+
1060
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#candle-snapshot
1061
+
493
1062
  :param str symbol: unified symbol of the market to fetch OHLCV data for
494
1063
  :param str timeframe: the length of time each candle represents, support '1m', '15m', '1h', '1d'
495
1064
  :param int [since]: timestamp in ms of the earliest candle to fetch
@@ -501,16 +1070,22 @@ class hyperliquid(Exchange, ImplicitAPI):
501
1070
  await self.load_markets()
502
1071
  market = self.market(symbol)
503
1072
  until = self.safe_integer(params, 'until', self.milliseconds())
1073
+ useTail = since is None
1074
+ originalSince = since
504
1075
  if since is None:
505
- since = 0
506
- if limit is None:
507
- limit = 500
1076
+ if limit is not None:
1077
+ # optimization if limit is provided
1078
+ timeframeInMilliseconds = self.parse_timeframe(timeframe) * 1000
1079
+ since = self.sum(until, timeframeInMilliseconds * limit * -1)
1080
+ useTail = False
1081
+ else:
1082
+ since = 0
508
1083
  params = self.omit(params, ['until'])
509
- request = {
1084
+ request: dict = {
510
1085
  'type': 'candleSnapshot',
511
1086
  'req': {
512
- 'coin': market['base'],
513
- 'interval': timeframe,
1087
+ 'coin': market['base'] if market['swap'] else market['id'],
1088
+ 'interval': self.safe_string(self.timeframes, timeframe, timeframe),
514
1089
  'startTime': since,
515
1090
  'endTime': until,
516
1091
  },
@@ -532,7 +1107,7 @@ class hyperliquid(Exchange, ImplicitAPI):
532
1107
  # }
533
1108
  # ]
534
1109
  #
535
- return self.parse_ohlcvs(response, market, timeframe, since, limit)
1110
+ return self.parse_ohlcvs(response, market, timeframe, originalSince, limit, useTail)
536
1111
 
537
1112
  def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
538
1113
  #
@@ -561,8 +1136,10 @@ class hyperliquid(Exchange, ImplicitAPI):
561
1136
  async def fetch_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
562
1137
  """
563
1138
  get the list of most recent trades for a particular symbol
564
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills
565
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills-by-time
1139
+
1140
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills
1141
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills-by-time
1142
+
566
1143
  :param str symbol: unified market symbol
567
1144
  :param int [since]: the earliest time in ms to fetch trades for
568
1145
  :param int [limit]: the maximum number of trades structures to retrieve
@@ -576,7 +1153,7 @@ class hyperliquid(Exchange, ImplicitAPI):
576
1153
  userAddress, params = self.handle_public_address('fetchTrades', params)
577
1154
  await self.load_markets()
578
1155
  market = self.safe_market(symbol)
579
- request = {
1156
+ request: dict = {
580
1157
  'user': userAddress,
581
1158
  }
582
1159
  if since is not None:
@@ -612,13 +1189,18 @@ class hyperliquid(Exchange, ImplicitAPI):
612
1189
  return self.parse_trades(response, market, since, limit)
613
1190
 
614
1191
  def amount_to_precision(self, symbol, amount):
615
- return self.decimal_to_precision(amount, ROUND, self.markets[symbol]['precision']['amount'], self.precisionMode)
1192
+ market = self.market(symbol)
1193
+ return self.decimal_to_precision(amount, ROUND, market['precision']['amount'], self.precisionMode, self.paddingMode)
616
1194
 
617
1195
  def price_to_precision(self, symbol: str, price) -> str:
618
1196
  market = self.market(symbol)
619
- result = self.decimal_to_precision(price, ROUND, market['precision']['price'], SIGNIFICANT_DIGITS, self.paddingMode)
620
- decimalParsedResult = self.decimal_to_precision(result, ROUND, 6, DECIMAL_PLACES, self.paddingMode)
621
- return decimalParsedResult
1197
+ priceStr = self.number_to_string(price)
1198
+ integerPart = priceStr.split('.')[0]
1199
+ significantDigits = max(5, len(integerPart))
1200
+ result = self.decimal_to_precision(price, ROUND, significantDigits, SIGNIFICANT_DIGITS, self.paddingMode)
1201
+ maxDecimals = 8 if market['spot'] else 6
1202
+ subtractedValue = maxDecimals - self.precision_from_string(self.safe_string(market['precision'], 'amount'))
1203
+ return self.decimal_to_precision(result, ROUND, subtractedValue, DECIMAL_PLACES, self.paddingMode)
622
1204
 
623
1205
  def hash_message(self, message):
624
1206
  return '0x' + self.hash(message, 'keccak', 'hex')
@@ -657,7 +1239,7 @@ class hyperliquid(Exchange, ImplicitAPI):
657
1239
  hash = self.action_hash(action, vaultAdress, nonce)
658
1240
  isTestnet = self.safe_bool(self.options, 'sandboxMode', False)
659
1241
  phantomAgent = self.construct_phantom_agent(hash, isTestnet)
660
- # data = {
1242
+ # data: Dict = {
661
1243
  # 'domain': {
662
1244
  # 'chainId': 1337,
663
1245
  # 'name': 'Exchange',
@@ -681,13 +1263,13 @@ class hyperliquid(Exchange, ImplicitAPI):
681
1263
  # }
682
1264
  zeroAddress = self.safe_string(self.options, 'zeroAddress')
683
1265
  chainId = 1337 # check self out
684
- domain = {
1266
+ domain: dict = {
685
1267
  'chainId': chainId,
686
1268
  'name': 'Exchange',
687
1269
  'verifyingContract': zeroAddress,
688
1270
  'version': '1',
689
1271
  }
690
- messageTypes = {
1272
+ messageTypes: dict = {
691
1273
  'Agent': [
692
1274
  {'name': 'source', 'type': 'string'},
693
1275
  {'name': 'connectionId', 'type': 'bytes32'},
@@ -697,11 +1279,12 @@ class hyperliquid(Exchange, ImplicitAPI):
697
1279
  signature = self.sign_message(msg, self.privateKey)
698
1280
  return signature
699
1281
 
700
- def build_sig(self, chainId, messageTypes, message):
1282
+ def sign_user_signed_action(self, messageTypes, message):
701
1283
  zeroAddress = self.safe_string(self.options, 'zeroAddress')
702
- domain = {
1284
+ chainId = 421614 # check self out
1285
+ domain: dict = {
703
1286
  'chainId': chainId,
704
- 'name': 'Exchange',
1287
+ 'name': 'HyperliquidSignTransaction',
705
1288
  'verifyingContract': zeroAddress,
706
1289
  'version': '1',
707
1290
  }
@@ -709,72 +1292,108 @@ class hyperliquid(Exchange, ImplicitAPI):
709
1292
  signature = self.sign_message(msg, self.privateKey)
710
1293
  return signature
711
1294
 
712
- def build_transfer_sig(self, message):
713
- isSandboxMode = self.safe_bool(self.options, 'sandboxMode')
714
- chainId = 421614 if (isSandboxMode) else 42161
715
- messageTypes = {
716
- 'UsdTransferSignPayload': [
1295
+ def build_usd_send_sig(self, message):
1296
+ messageTypes: dict = {
1297
+ 'HyperliquidTransaction:UsdSend': [
1298
+ {'name': 'hyperliquidChain', 'type': 'string'},
717
1299
  {'name': 'destination', 'type': 'string'},
718
1300
  {'name': 'amount', 'type': 'string'},
719
1301
  {'name': 'time', 'type': 'uint64'},
720
1302
  ],
721
1303
  }
722
- return self.build_sig(chainId, messageTypes, message)
1304
+ return self.sign_user_signed_action(messageTypes, message)
1305
+
1306
+ def build_usd_class_send_sig(self, message):
1307
+ messageTypes: dict = {
1308
+ 'HyperliquidTransaction:UsdClassTransfer': [
1309
+ {'name': 'hyperliquidChain', 'type': 'string'},
1310
+ {'name': 'amount', 'type': 'string'},
1311
+ {'name': 'toPerp', 'type': 'bool'},
1312
+ {'name': 'nonce', 'type': 'uint64'},
1313
+ ],
1314
+ }
1315
+ return self.sign_user_signed_action(messageTypes, message)
723
1316
 
724
1317
  def build_withdraw_sig(self, message):
725
- isSandboxMode = self.safe_bool(self.options, 'sandboxMode')
726
- chainId = 421614 if (isSandboxMode) else 42161
727
- messageTypes = {
728
- 'WithdrawFromBridge2SignPayload': [
1318
+ messageTypes: dict = {
1319
+ 'HyperliquidTransaction:Withdraw': [
1320
+ {'name': 'hyperliquidChain', 'type': 'string'},
729
1321
  {'name': 'destination', 'type': 'string'},
730
- {'name': 'usd', 'type': 'string'},
1322
+ {'name': 'amount', 'type': 'string'},
731
1323
  {'name': 'time', 'type': 'uint64'},
732
1324
  ],
733
1325
  }
734
- return self.build_sig(chainId, messageTypes, message)
1326
+ return self.sign_user_signed_action(messageTypes, message)
735
1327
 
736
1328
  async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
737
1329
  """
738
1330
  create a trade order
739
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
1331
+
1332
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
1333
+
740
1334
  :param str symbol: unified symbol of the market to create an order in
741
1335
  :param str type: 'market' or 'limit'
742
1336
  :param str side: 'buy' or 'sell'
743
1337
  :param float amount: how much of currency you want to trade in units of base currency
744
- :param float [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
1338
+ :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
745
1339
  :param dict [params]: extra parameters specific to the exchange API endpoint
746
1340
  :param str [params.timeInForce]: 'Gtc', 'Ioc', 'Alo'
747
1341
  :param bool [params.postOnly]: True or False whether the order is post-only
748
1342
  :param bool [params.reduceOnly]: True or False whether the order is reduce-only
749
1343
  :param float [params.triggerPrice]: The price at which a trigger order is triggered at
750
- :param str [params.clientOrderId]: client order id, optional 128 bit hex string
1344
+ :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
751
1345
  :param str [params.slippage]: the slippage for market order
1346
+ :param str [params.vaultAddress]: the vault address for order
752
1347
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
753
1348
  """
754
1349
  await self.load_markets()
755
- market = self.market(symbol)
756
- symbol = market['symbol']
757
- order = {
758
- 'symbol': symbol,
759
- 'type': type,
760
- 'side': side,
761
- 'amount': amount,
762
- 'price': price,
763
- 'params': params,
764
- }
765
- response = await self.create_orders([order], params)
766
- first = self.safe_dict(response, 0)
767
- return first
1350
+ order, globalParams = self.parse_create_order_args(symbol, type, side, amount, price, params)
1351
+ orders = await self.create_orders([order], globalParams)
1352
+ return orders[0]
768
1353
 
769
1354
  async def create_orders(self, orders: List[OrderRequest], params={}):
770
1355
  """
771
1356
  create a list of trade orders
772
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
1357
+
1358
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
1359
+
773
1360
  :param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
1361
+ :param dict [params]: extra parameters specific to the exchange API endpoint
774
1362
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
775
1363
  """
776
- self.check_required_credentials()
777
1364
  await self.load_markets()
1365
+ request = self.create_orders_request(orders, params)
1366
+ response = await self.privatePostExchange(request)
1367
+ #
1368
+ # {
1369
+ # "status": "ok",
1370
+ # "response": {
1371
+ # "type": "order",
1372
+ # "data": {
1373
+ # "statuses": [
1374
+ # {
1375
+ # "resting": {
1376
+ # "oid": 5063830287
1377
+ # }
1378
+ # }
1379
+ # ]
1380
+ # }
1381
+ # }
1382
+ # }
1383
+ #
1384
+ responseObj = self.safe_dict(response, 'response', {})
1385
+ data = self.safe_dict(responseObj, 'data', {})
1386
+ statuses = self.safe_list(data, 'statuses', [])
1387
+ return self.parse_orders(statuses, None)
1388
+
1389
+ def create_orders_request(self, orders, params={}) -> dict:
1390
+ """
1391
+ create a list of trade orders
1392
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
1393
+ :param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
1394
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1395
+ """
1396
+ self.check_required_credentials()
778
1397
  defaultSlippage = self.safe_string(self.options, 'defaultSlippage')
779
1398
  defaultSlippage = self.safe_string(params, 'slippage', defaultSlippage)
780
1399
  hasClientOrderId = False
@@ -791,7 +1410,7 @@ class hyperliquid(Exchange, ImplicitAPI):
791
1410
  clientOrderId = self.safe_string_2(orderParams, 'clientOrderId', 'client_id')
792
1411
  if clientOrderId is None:
793
1412
  raise ArgumentsRequired(self.id + ' createOrders() all orders must have clientOrderId if at least one has a clientOrderId')
794
- params = self.omit(params, ['slippage', 'clientOrderId', 'client_id', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice'])
1413
+ params = self.omit(params, ['slippage', 'clientOrderId', 'client_id', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce'])
795
1414
  nonce = self.milliseconds()
796
1415
  orderReq = []
797
1416
  for i in range(0, len(orders)):
@@ -806,7 +1425,6 @@ class hyperliquid(Exchange, ImplicitAPI):
806
1425
  amount = self.safe_string(rawOrder, 'amount')
807
1426
  price = self.safe_string(rawOrder, 'price')
808
1427
  orderParams = self.safe_dict(rawOrder, 'params', {})
809
- orderParams = self.extend(params, orderParams)
810
1428
  clientOrderId = self.safe_string_2(orderParams, 'clientOrderId', 'client_id')
811
1429
  slippage = self.safe_string(orderParams, 'slippage', defaultSlippage)
812
1430
  defaultTimeInForce = 'ioc' if (isMarket) else 'gtc'
@@ -829,7 +1447,7 @@ class hyperliquid(Exchange, ImplicitAPI):
829
1447
  px = self.price_to_precision(symbol, price)
830
1448
  sz = self.amount_to_precision(symbol, amount)
831
1449
  reduceOnly = self.safe_bool(orderParams, 'reduceOnly', False)
832
- orderType = {}
1450
+ orderType: dict = {}
833
1451
  if isTrigger:
834
1452
  isTp = False
835
1453
  if takeProfitPrice is not None:
@@ -846,7 +1464,8 @@ class hyperliquid(Exchange, ImplicitAPI):
846
1464
  orderType['limit'] = {
847
1465
  'tif': timeInForce,
848
1466
  }
849
- orderObj = {
1467
+ orderParams = self.omit(orderParams, ['clientOrderId', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce', 'client_id', 'reduceOnly', 'postOnly'])
1468
+ orderObj: dict = {
850
1469
  'a': self.parse_to_int(market['baseId']),
851
1470
  'b': isBuy,
852
1471
  'p': px,
@@ -858,8 +1477,8 @@ class hyperliquid(Exchange, ImplicitAPI):
858
1477
  if clientOrderId is not None:
859
1478
  orderObj['c'] = clientOrderId
860
1479
  orderReq.append(orderObj)
861
- vaultAddress = self.safe_string(params, 'vaultAddress')
862
- orderAction = {
1480
+ vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
1481
+ orderAction: dict = {
863
1482
  'type': 'order',
864
1483
  'orders': orderReq,
865
1484
  'grouping': 'na',
@@ -868,57 +1487,46 @@ class hyperliquid(Exchange, ImplicitAPI):
868
1487
  if vaultAddress is None:
869
1488
  orderAction['brokerCode'] = 1
870
1489
  signature = self.sign_l1_action(orderAction, nonce, vaultAddress)
871
- request = {
1490
+ request: dict = {
872
1491
  'action': orderAction,
873
1492
  'nonce': nonce,
874
1493
  'signature': signature,
875
1494
  # 'vaultAddress': vaultAddress,
876
1495
  }
877
- response = await self.privatePostExchange(self.extend(request, params))
878
- #
879
- # {
880
- # "status": "ok",
881
- # "response": {
882
- # "type": "order",
883
- # "data": {
884
- # "statuses": [
885
- # {
886
- # "resting": {
887
- # "oid": 5063830287
888
- # }
889
- # }
890
- # ]
891
- # }
892
- # }
893
- # }
894
- #
895
- responseObj = self.safe_dict(response, 'response', {})
896
- data = self.safe_dict(responseObj, 'data', {})
897
- statuses = self.safe_list(data, 'statuses', [])
898
- return self.parse_orders(statuses, None)
1496
+ if vaultAddress is not None:
1497
+ params = self.omit(params, 'vaultAddress')
1498
+ request['vaultAddress'] = vaultAddress
1499
+ return request
899
1500
 
900
1501
  async def cancel_order(self, id: str, symbol: Str = None, params={}):
901
1502
  """
902
1503
  cancels an open order
903
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s
904
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
1504
+
1505
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s
1506
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
1507
+
905
1508
  :param str id: order id
906
1509
  :param str symbol: unified symbol of the market the order was made in
907
1510
  :param dict [params]: extra parameters specific to the exchange API endpoint
908
- :param str [params.clientOrderId]: client order id(default None)
1511
+ :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
1512
+ :param str [params.vaultAddress]: the vault address for order
909
1513
  :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
910
1514
  """
911
- return await self.cancel_orders([id], symbol, params)
1515
+ orders = await self.cancel_orders([id], symbol, params)
1516
+ return self.safe_dict(orders, 0)
912
1517
 
913
1518
  async def cancel_orders(self, ids: List[str], symbol: Str = None, params={}):
914
1519
  """
915
1520
  cancel multiple orders
916
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s
917
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
1521
+
1522
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s
1523
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
1524
+
918
1525
  :param str[] ids: order ids
919
1526
  :param str [symbol]: unified market symbol
920
1527
  :param dict [params]: extra parameters specific to the exchange API endpoint
921
- :param string|str[] [params.clientOrderId]: client order ids(default None)
1528
+ :param string|str[] [params.clientOrderId]: client order ids,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
1529
+ :param str [params.vaultAddress]: the vault address
922
1530
  :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
923
1531
  """
924
1532
  self.check_required_credentials()
@@ -929,12 +1537,12 @@ class hyperliquid(Exchange, ImplicitAPI):
929
1537
  clientOrderId = self.safe_value_2(params, 'clientOrderId', 'client_id')
930
1538
  params = self.omit(params, ['clientOrderId', 'client_id'])
931
1539
  nonce = self.milliseconds()
932
- request = {
1540
+ request: dict = {
933
1541
  'nonce': nonce,
934
1542
  # 'vaultAddress': vaultAddress,
935
1543
  }
936
1544
  cancelReq = []
937
- cancelAction = {
1545
+ cancelAction: dict = {
938
1546
  'type': '',
939
1547
  'cancels': [],
940
1548
  }
@@ -956,11 +1564,92 @@ class hyperliquid(Exchange, ImplicitAPI):
956
1564
  'o': self.parse_to_numeric(ids[i]),
957
1565
  })
958
1566
  cancelAction['cancels'] = cancelReq
959
- vaultAddress = self.safe_string(params, 'vaultAddress')
1567
+ vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
960
1568
  signature = self.sign_l1_action(cancelAction, nonce, vaultAddress)
961
1569
  request['action'] = cancelAction
962
1570
  request['signature'] = signature
963
- response = await self.privatePostExchange(self.extend(request, params))
1571
+ if vaultAddress is not None:
1572
+ params = self.omit(params, 'vaultAddress')
1573
+ request['vaultAddress'] = vaultAddress
1574
+ response = await self.privatePostExchange(request)
1575
+ #
1576
+ # {
1577
+ # "status":"ok",
1578
+ # "response":{
1579
+ # "type":"cancel",
1580
+ # "data":{
1581
+ # "statuses":[
1582
+ # "success"
1583
+ # ]
1584
+ # }
1585
+ # }
1586
+ # }
1587
+ #
1588
+ innerResponse = self.safe_dict(response, 'response')
1589
+ data = self.safe_dict(innerResponse, 'data')
1590
+ statuses = self.safe_list(data, 'statuses')
1591
+ orders = []
1592
+ for i in range(0, len(statuses)):
1593
+ status = statuses[i]
1594
+ orders.append(self.safe_order({
1595
+ 'info': status,
1596
+ 'status': status,
1597
+ }))
1598
+ return orders
1599
+
1600
+ async def cancel_orders_for_symbols(self, orders: List[CancellationRequest], params={}):
1601
+ """
1602
+ cancel multiple orders for multiple symbols
1603
+
1604
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s
1605
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
1606
+
1607
+ :param CancellationRequest[] orders: each order should contain the parameters required by cancelOrder namely id and symbol, example [{"id": "a", "symbol": "BTC/USDT"}, {"id": "b", "symbol": "ETH/USDT"}]
1608
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1609
+ :param str [params.vaultAddress]: the vault address
1610
+ :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1611
+ """
1612
+ self.check_required_credentials()
1613
+ await self.load_markets()
1614
+ nonce = self.milliseconds()
1615
+ request: dict = {
1616
+ 'nonce': nonce,
1617
+ # 'vaultAddress': vaultAddress,
1618
+ }
1619
+ cancelReq = []
1620
+ cancelAction: dict = {
1621
+ 'type': '',
1622
+ 'cancels': [],
1623
+ }
1624
+ cancelByCloid = False
1625
+ for i in range(0, len(orders)):
1626
+ order = orders[i]
1627
+ clientOrderId = self.safe_string(order, 'clientOrderId')
1628
+ if clientOrderId is not None:
1629
+ cancelByCloid = True
1630
+ id = self.safe_string(order, 'id')
1631
+ symbol = self.safe_string(order, 'symbol')
1632
+ if symbol is None:
1633
+ raise ArgumentsRequired(self.id + ' cancelOrdersForSymbols() requires a symbol argument in each order')
1634
+ if id is not None and cancelByCloid:
1635
+ raise BadRequest(self.id + ' cancelOrdersForSymbols() all orders must have either id or clientOrderId')
1636
+ assetKey = 'asset' if cancelByCloid else 'a'
1637
+ idKey = 'cloid' if cancelByCloid else 'o'
1638
+ market = self.market(symbol)
1639
+ cancelObj: dict = {}
1640
+ cancelObj[assetKey] = self.parse_to_numeric(market['baseId'])
1641
+ cancelObj[idKey] = clientOrderId if cancelByCloid else self.parse_to_numeric(id)
1642
+ cancelReq.append(cancelObj)
1643
+ cancelAction['type'] = 'cancelByCloid' if cancelByCloid else 'cancel'
1644
+ cancelAction['cancels'] = cancelReq
1645
+ vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
1646
+ signature = self.sign_l1_action(cancelAction, nonce, vaultAddress)
1647
+ request['action'] = cancelAction
1648
+ request['signature'] = signature
1649
+ if vaultAddress is not None:
1650
+ params = self.omit(params, 'vaultAddress')
1651
+ request['vaultAddress'] = vaultAddress
1652
+ response = await self.privatePostExchange(request)
964
1653
  #
965
1654
  # {
966
1655
  # "status":"ok",
@@ -976,29 +1665,46 @@ class hyperliquid(Exchange, ImplicitAPI):
976
1665
  #
977
1666
  return response
978
1667
 
979
- async def edit_order(self, id: str, symbol: str, type: str, side: str, amount: Num = None, price: Num = None, params={}):
1668
+ async def cancel_all_orders_after(self, timeout: Int, params={}):
980
1669
  """
981
- edit a trade order
982
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#modify-an-order
983
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#modify-multiple-orders
984
- :param str id: cancel order id
985
- :param str symbol: unified symbol of the market to create an order in
986
- :param str type: 'market' or 'limit'
987
- :param str side: 'buy' or 'sell'
988
- :param float amount: how much of currency you want to trade in units of base currency
989
- :param float [price]: the price at which the order is to be fullfilled, in units of the base currency, ignored in market orders
1670
+ dead man's switch, cancel all orders after the given timeout
1671
+ :param number timeout: time in milliseconds, 0 represents cancel the timer
990
1672
  :param dict [params]: extra parameters specific to the exchange API endpoint
991
- :param str [params.timeInForce]: 'Gtc', 'Ioc', 'Alo'
992
- :param bool [params.postOnly]: True or False whether the order is post-only
993
- :param bool [params.reduceOnly]: True or False whether the order is reduce-only
994
- :param float [params.triggerPrice]: The price at which a trigger order is triggered at
995
- :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
996
- :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1673
+ :param str [params.vaultAddress]: the vault address
1674
+ :returns dict: the api result
997
1675
  """
1676
+ self.check_required_credentials()
1677
+ await self.load_markets()
1678
+ params = self.omit(params, ['clientOrderId', 'client_id'])
1679
+ nonce = self.milliseconds()
1680
+ request: dict = {
1681
+ 'nonce': nonce,
1682
+ # 'vaultAddress': vaultAddress,
1683
+ }
1684
+ cancelAction: dict = {
1685
+ 'type': 'scheduleCancel',
1686
+ 'time': nonce + timeout,
1687
+ }
1688
+ vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
1689
+ signature = self.sign_l1_action(cancelAction, nonce, vaultAddress)
1690
+ request['action'] = cancelAction
1691
+ request['signature'] = signature
1692
+ if vaultAddress is not None:
1693
+ params = self.omit(params, 'vaultAddress')
1694
+ request['vaultAddress'] = vaultAddress
1695
+ response = await self.privatePostExchange(request)
1696
+ #
1697
+ # {
1698
+ # "status":"err",
1699
+ # "response":"Cannot set scheduled cancel time until enough volume traded. Required: $1000000. Traded: $373.47205."
1700
+ # }
1701
+ #
1702
+ return response
1703
+
1704
+ def edit_order_request(self, id: str, symbol: str, type: str, side: str, amount: Num = None, price: Num = None, params={}):
998
1705
  self.check_required_credentials()
999
1706
  if id is None:
1000
1707
  raise ArgumentsRequired(self.id + ' editOrder() requires an id argument')
1001
- await self.load_markets()
1002
1708
  market = self.market(symbol)
1003
1709
  type = type.upper()
1004
1710
  isMarket = (type == 'MARKET')
@@ -1025,7 +1731,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1025
1731
  px = self.price_to_precision(symbol, str(price))
1026
1732
  sz = self.amount_to_precision(symbol, amount)
1027
1733
  reduceOnly = self.safe_bool(params, 'reduceOnly', False)
1028
- orderType = {}
1734
+ orderType: dict = {}
1029
1735
  if isTrigger:
1030
1736
  isTp = False
1031
1737
  if takeProfitPrice is not None:
@@ -1045,7 +1751,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1045
1751
  if triggerPrice is None:
1046
1752
  triggerPrice = '0'
1047
1753
  nonce = self.milliseconds()
1048
- orderReq = {
1754
+ orderReq: dict = {
1049
1755
  'a': self.parse_to_int(market['baseId']),
1050
1756
  'b': isBuy,
1051
1757
  'p': px,
@@ -1056,23 +1762,53 @@ class hyperliquid(Exchange, ImplicitAPI):
1056
1762
  }
1057
1763
  if clientOrderId is not None:
1058
1764
  orderReq['c'] = clientOrderId
1059
- modifyReq = {
1765
+ modifyReq: dict = {
1060
1766
  'oid': self.parse_to_int(id),
1061
1767
  'order': orderReq,
1062
1768
  }
1063
- modifyAction = {
1769
+ modifyAction: dict = {
1064
1770
  'type': 'batchModify',
1065
1771
  'modifies': [modifyReq],
1066
1772
  }
1067
- vaultAddress = self.safe_string(params, 'vaultAddress')
1773
+ vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
1068
1774
  signature = self.sign_l1_action(modifyAction, nonce, vaultAddress)
1069
- request = {
1775
+ request: dict = {
1070
1776
  'action': modifyAction,
1071
1777
  'nonce': nonce,
1072
1778
  'signature': signature,
1073
1779
  # 'vaultAddress': vaultAddress,
1074
1780
  }
1075
- response = await self.privatePostExchange(self.extend(request, params))
1781
+ if vaultAddress is not None:
1782
+ params = self.omit(params, 'vaultAddress')
1783
+ request['vaultAddress'] = vaultAddress
1784
+ return request
1785
+
1786
+ async def edit_order(self, id: str, symbol: str, type: str, side: str, amount: Num = None, price: Num = None, params={}):
1787
+ """
1788
+ edit a trade order
1789
+
1790
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#modify-an-order
1791
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#modify-multiple-orders
1792
+
1793
+ :param str id: cancel order id
1794
+ :param str symbol: unified symbol of the market to create an order in
1795
+ :param str type: 'market' or 'limit'
1796
+ :param str side: 'buy' or 'sell'
1797
+ :param float amount: how much of currency you want to trade in units of base currency
1798
+ :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
1799
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1800
+ :param str [params.timeInForce]: 'Gtc', 'Ioc', 'Alo'
1801
+ :param bool [params.postOnly]: True or False whether the order is post-only
1802
+ :param bool [params.reduceOnly]: True or False whether the order is reduce-only
1803
+ :param float [params.triggerPrice]: The price at which a trigger order is triggered at
1804
+ :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
1805
+ :param str [params.vaultAddress]: the vault address for order
1806
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1807
+ """
1808
+ await self.load_markets()
1809
+ market = self.market(symbol)
1810
+ request = self.edit_order_request(id, symbol, type, side, amount, price, params)
1811
+ response = await self.privatePostExchange(request)
1076
1812
  #
1077
1813
  # {
1078
1814
  # "status": "ok",
@@ -1117,7 +1853,9 @@ class hyperliquid(Exchange, ImplicitAPI):
1117
1853
  async def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1118
1854
  """
1119
1855
  fetches historical funding rate prices
1120
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-historical-funding-rates
1856
+
1857
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-historical-funding-rates
1858
+
1121
1859
  :param str symbol: unified symbol of the market to fetch the funding rate history for
1122
1860
  :param int [since]: timestamp in ms of the earliest funding rate to fetch
1123
1861
  :param int [limit]: the maximum amount of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-history-structure>` to fetch
@@ -1126,15 +1864,18 @@ class hyperliquid(Exchange, ImplicitAPI):
1126
1864
  :returns dict[]: a list of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-history-structure>`
1127
1865
  """
1128
1866
  await self.load_markets()
1867
+ if symbol is None:
1868
+ raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
1129
1869
  market = self.market(symbol)
1130
- request = {
1870
+ request: dict = {
1131
1871
  'type': 'fundingHistory',
1132
1872
  'coin': market['base'],
1133
1873
  }
1134
1874
  if since is not None:
1135
1875
  request['startTime'] = since
1136
1876
  else:
1137
- request['startTime'] = self.milliseconds() - 100 * 60 * 60 * 1000
1877
+ maxLimit = 500 if (limit is None) else limit
1878
+ request['startTime'] = self.milliseconds() - maxLimit * 60 * 60 * 1000
1138
1879
  until = self.safe_integer(params, 'until')
1139
1880
  params = self.omit(params, 'until')
1140
1881
  if until is not None:
@@ -1164,10 +1905,86 @@ class hyperliquid(Exchange, ImplicitAPI):
1164
1905
  sorted = self.sort_by(result, 'timestamp')
1165
1906
  return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)
1166
1907
 
1167
- async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1908
+ async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1909
+ """
1910
+ fetch all unfilled currently open orders
1911
+
1912
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-open-orders
1913
+
1914
+ :param str symbol: unified market symbol
1915
+ :param int [since]: the earliest time in ms to fetch open orders for
1916
+ :param int [limit]: the maximum number of open orders structures to retrieve
1917
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1918
+ :param str [params.user]: user address, will default to self.walletAddress if not provided
1919
+ :param str [params.method]: 'openOrders' or 'frontendOpenOrders' default is 'frontendOpenOrders'
1920
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1921
+ """
1922
+ userAddress = None
1923
+ userAddress, params = self.handle_public_address('fetchOpenOrders', params)
1924
+ method = None
1925
+ method, params = self.handle_option_and_params(params, 'fetchOpenOrders', 'method', 'frontendOpenOrders')
1926
+ await self.load_markets()
1927
+ market = self.safe_market(symbol)
1928
+ request: dict = {
1929
+ 'type': method,
1930
+ 'user': userAddress,
1931
+ }
1932
+ response = await self.publicPostInfo(self.extend(request, params))
1933
+ #
1934
+ # [
1935
+ # {
1936
+ # "coin": "ETH",
1937
+ # "limitPx": "2000.0",
1938
+ # "oid": 3991946565,
1939
+ # "origSz": "0.1",
1940
+ # "side": "B",
1941
+ # "sz": "0.1",
1942
+ # "timestamp": 1704346468838
1943
+ # }
1944
+ # ]
1945
+ #
1946
+ orderWithStatus = []
1947
+ for i in range(0, len(response)):
1948
+ order = response[i]
1949
+ extendOrder = {}
1950
+ if self.safe_string(order, 'status') is None:
1951
+ extendOrder['ccxtStatus'] = 'open'
1952
+ orderWithStatus.append(self.extend(order, extendOrder))
1953
+ return self.parse_orders(orderWithStatus, market, since, limit)
1954
+
1955
+ async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1956
+ """
1957
+ fetch all unfilled currently closed orders
1958
+ :param str symbol: unified market symbol
1959
+ :param int [since]: the earliest time in ms to fetch open orders for
1960
+ :param int [limit]: the maximum number of open orders structures to retrieve
1961
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1962
+ :param str [params.user]: user address, will default to self.walletAddress if not provided
1963
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1964
+ """
1965
+ await self.load_markets()
1966
+ orders = await self.fetch_orders(symbol, None, None, params) # don't filter here because we don't want to catch open orders
1967
+ closedOrders = self.filter_by_array(orders, 'status', ['closed'], False)
1968
+ return self.filter_by_symbol_since_limit(closedOrders, symbol, since, limit)
1969
+
1970
+ async def fetch_canceled_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1971
+ """
1972
+ fetch all canceled orders
1973
+ :param str symbol: unified market symbol
1974
+ :param int [since]: the earliest time in ms to fetch open orders for
1975
+ :param int [limit]: the maximum number of open orders structures to retrieve
1976
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1977
+ :param str [params.user]: user address, will default to self.walletAddress if not provided
1978
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1979
+ """
1980
+ await self.load_markets()
1981
+ orders = await self.fetch_orders(symbol, None, None, params) # don't filter here because we don't want to catch open orders
1982
+ closedOrders = self.filter_by_array(orders, 'status', ['canceled'], False)
1983
+ return self.filter_by_symbol_since_limit(closedOrders, symbol, since, limit)
1984
+
1985
+ async def fetch_canceled_and_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1168
1986
  """
1169
- fetch all unfilled currently open orders
1170
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-open-orders
1987
+ fetch all closed and canceled orders
1171
1988
  :param str symbol: unified market symbol
1172
1989
  :param int [since]: the earliest time in ms to fetch open orders for
1173
1990
  :param int [limit]: the maximum number of open orders structures to retrieve
@@ -1175,33 +1992,14 @@ class hyperliquid(Exchange, ImplicitAPI):
1175
1992
  :param str [params.user]: user address, will default to self.walletAddress if not provided
1176
1993
  :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1177
1994
  """
1178
- userAddress = None
1179
- userAddress, params = self.handle_public_address('fetchOpenOrders', params)
1180
1995
  await self.load_markets()
1181
- market = self.safe_market(symbol)
1182
- request = {
1183
- 'type': 'openOrders',
1184
- 'user': userAddress,
1185
- }
1186
- response = await self.publicPostInfo(self.extend(request, params))
1187
- #
1188
- # [
1189
- # {
1190
- # "coin": "ETH",
1191
- # "limitPx": "2000.0",
1192
- # "oid": 3991946565,
1193
- # "origSz": "0.1",
1194
- # "side": "B",
1195
- # "sz": "0.1",
1196
- # "timestamp": 1704346468838
1197
- # }
1198
- # ]
1199
- #
1200
- return self.parse_orders(response, market, since, limit)
1996
+ orders = await self.fetch_orders(symbol, None, None, params) # don't filter here because we don't want to catch open orders
1997
+ closedOrders = self.filter_by_array(orders, 'status', ['canceled', 'closed', 'rejected'], False)
1998
+ return self.filter_by_symbol_since_limit(closedOrders, symbol, since, limit)
1201
1999
 
1202
- async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
2000
+ async def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1203
2001
  """
1204
- fetch all unfilled currently closed orders
2002
+ fetch all orders
1205
2003
  :param str symbol: unified market symbol
1206
2004
  :param int [since]: the earliest time in ms to fetch open orders for
1207
2005
  :param int [limit]: the maximum number of open orders structures to retrieve
@@ -1210,10 +2008,10 @@ class hyperliquid(Exchange, ImplicitAPI):
1210
2008
  :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1211
2009
  """
1212
2010
  userAddress = None
1213
- userAddress, params = self.handle_public_address('fetchClosedOrders', params)
2011
+ userAddress, params = self.handle_public_address('fetchOrders', params)
1214
2012
  await self.load_markets()
1215
2013
  market = self.safe_market(symbol)
1216
- request = {
2014
+ request: dict = {
1217
2015
  'type': 'historicalOrders',
1218
2016
  'user': userAddress,
1219
2017
  }
@@ -1236,7 +2034,10 @@ class hyperliquid(Exchange, ImplicitAPI):
1236
2034
  async def fetch_order(self, id: str, symbol: Str = None, params={}):
1237
2035
  """
1238
2036
  fetches information on an order made by the user
1239
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#query-order-status-by-oid-or-cloid
2037
+
2038
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#query-order-status-by-oid-or-cloid
2039
+
2040
+ :param str id: order id
1240
2041
  :param str symbol: unified symbol of the market the order was made in
1241
2042
  :param dict [params]: extra parameters specific to the exchange API endpoint
1242
2043
  :param str [params.user]: user address, will default to self.walletAddress if not provided
@@ -1246,9 +2047,10 @@ class hyperliquid(Exchange, ImplicitAPI):
1246
2047
  userAddress, params = self.handle_public_address('fetchOrder', params)
1247
2048
  await self.load_markets()
1248
2049
  market = self.safe_market(symbol)
1249
- request = {
2050
+ isClientOrderId = len(id) >= 34
2051
+ request: dict = {
1250
2052
  'type': 'orderStatus',
1251
- 'oid': self.parse_to_numeric(id),
2053
+ 'oid': id if isClientOrderId else self.parse_to_numeric(id),
1252
2054
  'user': userAddress,
1253
2055
  }
1254
2056
  response = await self.publicPostInfo(self.extend(request, params))
@@ -1282,7 +2084,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1282
2084
  data = self.safe_dict(response, 'order')
1283
2085
  return self.parse_order(data, market)
1284
2086
 
1285
- def parse_order(self, order, market: Market = None) -> Order:
2087
+ def parse_order(self, order: dict, market: Market = None) -> Order:
1286
2088
  #
1287
2089
  # fetchOpenOrders
1288
2090
  #
@@ -1354,6 +2156,25 @@ class hyperliquid(Exchange, ImplicitAPI):
1354
2156
  # "oid":6195281425
1355
2157
  # }
1356
2158
  # }
2159
+ # frontendOrder
2160
+ # {
2161
+ # "children": [],
2162
+ # "cloid": null,
2163
+ # "coin": "BLUR",
2164
+ # "isPositionTpsl": False,
2165
+ # "isTrigger": True,
2166
+ # "limitPx": "0.5",
2167
+ # "oid": 8670487141,
2168
+ # "orderType": "Stop Limit",
2169
+ # "origSz": "20.0",
2170
+ # "reduceOnly": False,
2171
+ # "side": "B",
2172
+ # "sz": "20.0",
2173
+ # "tif": null,
2174
+ # "timestamp": 1715523663687,
2175
+ # "triggerCondition": "Price above 0.6",
2176
+ # "triggerPx": "0.6"
2177
+ # }
1357
2178
  #
1358
2179
  entry = self.safe_dict_n(order, ['order', 'resting', 'filled'])
1359
2180
  if entry is None:
@@ -1361,17 +2182,20 @@ class hyperliquid(Exchange, ImplicitAPI):
1361
2182
  coin = self.safe_string(entry, 'coin')
1362
2183
  marketId = None
1363
2184
  if coin is not None:
1364
- marketId = coin + '/USDC:USDC'
2185
+ marketId = self.coin_to_market_id(coin)
1365
2186
  if self.safe_string(entry, 'id') is None:
1366
2187
  market = self.safe_market(marketId, None)
1367
2188
  else:
1368
2189
  market = self.safe_market(marketId, market)
1369
2190
  symbol = market['symbol']
1370
- timestamp = self.safe_integer_2(order, 'timestamp', 'statusTimestamp')
1371
- status = self.safe_string(order, 'status')
2191
+ timestamp = self.safe_integer(entry, 'timestamp')
2192
+ status = self.safe_string_2(order, 'status', 'ccxtStatus')
2193
+ order = self.omit(order, ['ccxtStatus'])
1372
2194
  side = self.safe_string(entry, 'side')
1373
2195
  if side is not None:
1374
2196
  side = 'sell' if (side == 'A') else 'buy'
2197
+ totalAmount = self.safe_string_2(entry, 'origSz', 'totalSz')
2198
+ remaining = self.safe_string(entry, 'sz')
1375
2199
  return self.safe_order({
1376
2200
  'info': order,
1377
2201
  'id': self.safe_string(entry, 'oid'),
@@ -1379,34 +2203,38 @@ class hyperliquid(Exchange, ImplicitAPI):
1379
2203
  'timestamp': timestamp,
1380
2204
  'datetime': self.iso8601(timestamp),
1381
2205
  'lastTradeTimestamp': None,
1382
- 'lastUpdateTimestamp': None,
2206
+ 'lastUpdateTimestamp': self.safe_integer(order, 'statusTimestamp'),
1383
2207
  'symbol': symbol,
1384
- 'type': self.safe_string_lower(entry, 'orderType'),
2208
+ 'type': self.parse_order_type(self.safe_string_lower(entry, 'orderType')),
1385
2209
  'timeInForce': self.safe_string_upper(entry, 'tif'),
1386
2210
  'postOnly': None,
1387
2211
  'reduceOnly': self.safe_bool(entry, 'reduceOnly'),
1388
2212
  'side': side,
1389
- 'price': self.safe_number(entry, 'limitPx'),
2213
+ 'price': self.safe_string(entry, 'limitPx'),
1390
2214
  'triggerPrice': self.safe_number(entry, 'triggerPx') if self.safe_bool(entry, 'isTrigger') else None,
1391
- 'amount': self.safe_number_2(entry, 'sz', 'totalSz'),
2215
+ 'amount': totalAmount,
1392
2216
  'cost': None,
1393
- 'average': self.safe_number(entry, 'avgPx'),
1394
- 'filled': None,
1395
- 'remaining': None,
2217
+ 'average': self.safe_string(entry, 'avgPx'),
2218
+ 'filled': Precise.string_sub(totalAmount, remaining),
2219
+ 'remaining': remaining,
1396
2220
  'status': self.parse_order_status(status),
1397
2221
  'fee': None,
1398
2222
  'trades': None,
1399
2223
  }, market)
1400
2224
 
1401
- def parse_order_status(self, status):
1402
- statuses = {
2225
+ def parse_order_status(self, status: Str):
2226
+ statuses: dict = {
1403
2227
  'triggered': 'open',
1404
2228
  'filled': 'closed',
2229
+ 'open': 'open',
2230
+ 'canceled': 'canceled',
2231
+ 'rejected': 'rejected',
2232
+ 'marginCanceled': 'canceled',
1405
2233
  }
1406
2234
  return self.safe_string(statuses, status, status)
1407
2235
 
1408
2236
  def parse_order_type(self, status):
1409
- statuses = {
2237
+ statuses: dict = {
1410
2238
  'stop limit': 'limit',
1411
2239
  'stop market': 'market',
1412
2240
  }
@@ -1415,8 +2243,10 @@ class hyperliquid(Exchange, ImplicitAPI):
1415
2243
  async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1416
2244
  """
1417
2245
  fetch all trades made by the user
1418
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills
1419
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills-by-time
2246
+
2247
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills
2248
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills-by-time
2249
+
1420
2250
  :param str symbol: unified market symbol
1421
2251
  :param int [since]: the earliest time in ms to fetch trades for
1422
2252
  :param int [limit]: the maximum number of trades structures to retrieve
@@ -1428,7 +2258,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1428
2258
  userAddress, params = self.handle_public_address('fetchMyTrades', params)
1429
2259
  await self.load_markets()
1430
2260
  market = self.safe_market(symbol)
1431
- request = {
2261
+ request: dict = {
1432
2262
  'user': userAddress,
1433
2263
  }
1434
2264
  if since is not None:
@@ -1449,6 +2279,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1449
2279
  # "crossed": True,
1450
2280
  # "dir": "Close Long",
1451
2281
  # "fee": "0.050062",
2282
+ # "feeToken": "USDC",
1452
2283
  # "hash": "0x09d77c96791e98b5775a04092584ab010d009445119c71e4005c0d634ea322bc",
1453
2284
  # "liquidationMarkPx": null,
1454
2285
  # "oid": 3929354691,
@@ -1463,7 +2294,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1463
2294
  #
1464
2295
  return self.parse_trades(response, market, since, limit)
1465
2296
 
1466
- def parse_trade(self, trade, market: Market = None) -> Trade:
2297
+ def parse_trade(self, trade: dict, market: Market = None) -> Trade:
1467
2298
  #
1468
2299
  # {
1469
2300
  # "closedPnl": "0.19343",
@@ -1486,7 +2317,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1486
2317
  price = self.safe_string(trade, 'px')
1487
2318
  amount = self.safe_string(trade, 'sz')
1488
2319
  coin = self.safe_string(trade, 'coin')
1489
- marketId = coin + '/USDC:USDC'
2320
+ marketId = self.coin_to_market_id(coin)
1490
2321
  market = self.safe_market(marketId, None)
1491
2322
  symbol = market['symbol']
1492
2323
  id = self.safe_string(trade, 'tid')
@@ -1507,13 +2338,19 @@ class hyperliquid(Exchange, ImplicitAPI):
1507
2338
  'price': price,
1508
2339
  'amount': amount,
1509
2340
  'cost': None,
1510
- 'fee': {'cost': fee, 'currency': 'USDC'},
2341
+ 'fee': {
2342
+ 'cost': fee,
2343
+ 'currency': self.safe_string(trade, 'feeToken'),
2344
+ 'rate': None,
2345
+ },
1511
2346
  }, market)
1512
2347
 
1513
2348
  async def fetch_position(self, symbol: str, params={}):
1514
2349
  """
1515
2350
  fetch data on an open position
1516
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-state
2351
+
2352
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-users-perpetuals-account-summary
2353
+
1517
2354
  :param str symbol: unified market symbol of the market the position is held in
1518
2355
  :param dict [params]: extra parameters specific to the exchange API endpoint
1519
2356
  :param str [params.user]: user address, will default to self.walletAddress if not provided
@@ -1525,7 +2362,9 @@ class hyperliquid(Exchange, ImplicitAPI):
1525
2362
  async def fetch_positions(self, symbols: Strings = None, params={}):
1526
2363
  """
1527
2364
  fetch all open positions
1528
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-state
2365
+
2366
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-users-perpetuals-account-summary
2367
+
1529
2368
  :param str[] [symbols]: list of unified market symbols
1530
2369
  :param dict [params]: extra parameters specific to the exchange API endpoint
1531
2370
  :param str [params.user]: user address, will default to self.walletAddress if not provided
@@ -1535,7 +2374,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1535
2374
  userAddress = None
1536
2375
  userAddress, params = self.handle_public_address('fetchPositions', params)
1537
2376
  symbols = self.market_symbols(symbols)
1538
- request = {
2377
+ request: dict = {
1539
2378
  'type': 'clearinghouseState',
1540
2379
  'user': userAddress,
1541
2380
  }
@@ -1591,7 +2430,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1591
2430
  result.append(self.parse_position(data[i], None))
1592
2431
  return self.filter_by_array_positions(result, 'symbol', symbols, False)
1593
2432
 
1594
- def parse_position(self, position, market: Market = None):
2433
+ def parse_position(self, position: dict, market: Market = None):
1595
2434
  #
1596
2435
  # {
1597
2436
  # "position": {
@@ -1620,18 +2459,22 @@ class hyperliquid(Exchange, ImplicitAPI):
1620
2459
  #
1621
2460
  entry = self.safe_dict(position, 'position', {})
1622
2461
  coin = self.safe_string(entry, 'coin')
1623
- marketId = coin + '/USDC:USDC'
2462
+ marketId = self.coin_to_market_id(coin)
1624
2463
  market = self.safe_market(marketId, None)
1625
2464
  symbol = market['symbol']
1626
2465
  leverage = self.safe_dict(entry, 'leverage', {})
1627
- isIsolated = (self.safe_string(leverage, 'type') == 'isolated')
1628
- quantity = self.safe_number(leverage, 'rawUsd')
2466
+ marginMode = self.safe_string(leverage, 'type')
2467
+ isIsolated = (marginMode == 'isolated')
2468
+ rawSize = self.safe_string(entry, 'szi')
2469
+ size = rawSize
1629
2470
  side = None
1630
- if quantity is not None:
1631
- side = 'short' if (quantity > 0) else 'long'
1632
- unrealizedPnl = self.safe_number(entry, 'unrealizedPnl')
1633
- initialMargin = self.safe_number(entry, 'marginUsed')
1634
- percentage = unrealizedPnl / initialMargin * 100
2471
+ if size is not None:
2472
+ side = 'long' if Precise.string_gt(rawSize, '0') else 'short'
2473
+ size = Precise.string_abs(size)
2474
+ rawUnrealizedPnl = self.safe_string(entry, 'unrealizedPnl')
2475
+ absRawUnrealizedPnl = Precise.string_abs(rawUnrealizedPnl)
2476
+ initialMargin = self.safe_string(entry, 'marginUsed')
2477
+ percentage = Precise.string_mul(Precise.string_div(absRawUnrealizedPnl, initialMargin), '100')
1635
2478
  return self.safe_position({
1636
2479
  'info': position,
1637
2480
  'id': None,
@@ -1641,21 +2484,21 @@ class hyperliquid(Exchange, ImplicitAPI):
1641
2484
  'isolated': isIsolated,
1642
2485
  'hedged': None,
1643
2486
  'side': side,
1644
- 'contracts': self.parse_number(quantity),
2487
+ 'contracts': self.parse_number(size),
1645
2488
  'contractSize': None,
1646
2489
  'entryPrice': self.safe_number(entry, 'entryPx'),
1647
2490
  'markPrice': None,
1648
2491
  'notional': self.safe_number(entry, 'positionValue'),
1649
2492
  'leverage': self.safe_number(leverage, 'value'),
1650
- 'collateral': None,
1651
- 'initialMargin': initialMargin,
2493
+ 'collateral': self.safe_number(entry, 'marginUsed'),
2494
+ 'initialMargin': self.parse_number(initialMargin),
1652
2495
  'maintenanceMargin': None,
1653
2496
  'initialMarginPercentage': None,
1654
2497
  'maintenanceMarginPercentage': None,
1655
- 'unrealizedPnl': unrealizedPnl,
2498
+ 'unrealizedPnl': self.parse_number(rawUnrealizedPnl),
1656
2499
  'liquidationPrice': self.safe_number(entry, 'liquidationPx'),
1657
- 'marginMode': None,
1658
- 'percentage': percentage,
2500
+ 'marginMode': marginMode,
2501
+ 'percentage': self.parse_number(percentage),
1659
2502
  })
1660
2503
 
1661
2504
  async def set_margin_mode(self, marginMode: str, symbol: Str = None, params={}):
@@ -1675,24 +2518,30 @@ class hyperliquid(Exchange, ImplicitAPI):
1675
2518
  if leverage is None:
1676
2519
  raise ArgumentsRequired(self.id + ' setMarginMode() requires a leverage parameter')
1677
2520
  asset = self.parse_to_int(market['baseId'])
1678
- isCross = (marginMode == 'isolated')
2521
+ isCross = (marginMode == 'cross')
1679
2522
  nonce = self.milliseconds()
1680
2523
  params = self.omit(params, ['leverage'])
1681
- updateAction = {
2524
+ updateAction: dict = {
1682
2525
  'type': 'updateLeverage',
1683
2526
  'asset': asset,
1684
2527
  'isCross': isCross,
1685
2528
  'leverage': leverage,
1686
2529
  }
1687
2530
  vaultAddress = self.safe_string(params, 'vaultAddress')
2531
+ if vaultAddress is not None:
2532
+ params = self.omit(params, 'vaultAddress')
2533
+ if vaultAddress.startswith('0x'):
2534
+ vaultAddress = vaultAddress.replace('0x', '')
1688
2535
  signature = self.sign_l1_action(updateAction, nonce, vaultAddress)
1689
- request = {
2536
+ request: dict = {
1690
2537
  'action': updateAction,
1691
2538
  'nonce': nonce,
1692
2539
  'signature': signature,
1693
2540
  # 'vaultAddress': vaultAddress,
1694
2541
  }
1695
- response = await self.privatePostExchange(self.extend(request, params))
2542
+ if vaultAddress is not None:
2543
+ request['vaultAddress'] = vaultAddress
2544
+ response = await self.privatePostExchange(request)
1696
2545
  #
1697
2546
  # {
1698
2547
  # 'response': {
@@ -1721,21 +2570,24 @@ class hyperliquid(Exchange, ImplicitAPI):
1721
2570
  asset = self.parse_to_int(market['baseId'])
1722
2571
  nonce = self.milliseconds()
1723
2572
  params = self.omit(params, 'marginMode')
1724
- updateAction = {
2573
+ updateAction: dict = {
1725
2574
  'type': 'updateLeverage',
1726
2575
  'asset': asset,
1727
2576
  'isCross': isCross,
1728
2577
  'leverage': leverage,
1729
2578
  }
1730
- vaultAddress = self.safe_string(params, 'vaultAddress')
2579
+ vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
1731
2580
  signature = self.sign_l1_action(updateAction, nonce, vaultAddress)
1732
- request = {
2581
+ request: dict = {
1733
2582
  'action': updateAction,
1734
2583
  'nonce': nonce,
1735
2584
  'signature': signature,
1736
2585
  # 'vaultAddress': vaultAddress,
1737
2586
  }
1738
- response = await self.privatePostExchange(self.extend(request, params))
2587
+ if vaultAddress is not None:
2588
+ params = self.omit(params, 'vaultAddress')
2589
+ request['vaultAddress'] = vaultAddress
2590
+ response = await self.privatePostExchange(request)
1739
2591
  #
1740
2592
  # {
1741
2593
  # 'response': {
@@ -1746,10 +2598,12 @@ class hyperliquid(Exchange, ImplicitAPI):
1746
2598
  #
1747
2599
  return response
1748
2600
 
1749
- async def add_margin(self, symbol: str, amount, params={}):
2601
+ async def add_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
1750
2602
  """
1751
2603
  add margin
1752
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#update-isolated-margin
2604
+
2605
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#update-isolated-margin
2606
+
1753
2607
  :param str symbol: unified market symbol
1754
2608
  :param float amount: amount of margin to add
1755
2609
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -1757,9 +2611,11 @@ class hyperliquid(Exchange, ImplicitAPI):
1757
2611
  """
1758
2612
  return await self.modify_margin_helper(symbol, amount, 'add', params)
1759
2613
 
1760
- async def reduce_margin(self, symbol: str, amount, params={}):
2614
+ async def reduce_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
1761
2615
  """
1762
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#update-isolated-margin
2616
+
2617
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#update-isolated-margin
2618
+
1763
2619
  remove margin from a position
1764
2620
  :param str symbol: unified market symbol
1765
2621
  :param float amount: the amount of margin to remove
@@ -1768,7 +2624,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1768
2624
  """
1769
2625
  return await self.modify_margin_helper(symbol, amount, 'reduce', params)
1770
2626
 
1771
- async def modify_margin_helper(self, symbol: str, amount, type, params={}):
2627
+ async def modify_margin_helper(self, symbol: str, amount, type, params={}) -> MarginModification:
1772
2628
  await self.load_markets()
1773
2629
  market = self.market(symbol)
1774
2630
  asset = self.parse_to_int(market['baseId'])
@@ -1776,21 +2632,24 @@ class hyperliquid(Exchange, ImplicitAPI):
1776
2632
  if type == 'reduce':
1777
2633
  sz = -sz
1778
2634
  nonce = self.milliseconds()
1779
- updateAction = {
2635
+ updateAction: dict = {
1780
2636
  'type': 'updateIsolatedMargin',
1781
2637
  'asset': asset,
1782
2638
  'isBuy': True,
1783
2639
  'ntli': sz,
1784
2640
  }
1785
- vaultAddress = self.safe_string(params, 'vaultAddress')
2641
+ vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
1786
2642
  signature = self.sign_l1_action(updateAction, nonce, vaultAddress)
1787
- request = {
2643
+ request: dict = {
1788
2644
  'action': updateAction,
1789
2645
  'nonce': nonce,
1790
2646
  'signature': signature,
1791
2647
  # 'vaultAddress': vaultAddress,
1792
2648
  }
1793
- response = await self.privatePostExchange(self.extend(request, params))
2649
+ if vaultAddress is not None:
2650
+ params = self.omit(params, 'vaultAddress')
2651
+ request['vaultAddress'] = vaultAddress
2652
+ response = await self.privatePostExchange(request)
1794
2653
  #
1795
2654
  # {
1796
2655
  # 'response': {
@@ -1799,58 +2658,140 @@ class hyperliquid(Exchange, ImplicitAPI):
1799
2658
  # 'status': 'ok'
1800
2659
  # }
1801
2660
  #
1802
- return response
1803
- # return self.extend(self.parse_margin_modification(response, market), {
1804
- # 'code': code,
1805
- # })
2661
+ return self.extend(self.parse_margin_modification(response, market), {
2662
+ 'code': self.safe_string(response, 'status'),
2663
+ })
2664
+
2665
+ def parse_margin_modification(self, data: dict, market: Market = None) -> MarginModification:
2666
+ #
2667
+ # {
2668
+ # 'type': 'default'
2669
+ # }
2670
+ #
2671
+ return {
2672
+ 'info': data,
2673
+ 'symbol': self.safe_symbol(None, market),
2674
+ 'type': None,
2675
+ 'marginMode': 'isolated',
2676
+ 'amount': None,
2677
+ 'total': None,
2678
+ 'code': self.safe_string(market, 'settle'),
2679
+ 'status': None,
2680
+ 'timestamp': None,
2681
+ 'datetime': None,
2682
+ }
1806
2683
 
1807
2684
  async def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
1808
2685
  """
1809
2686
  transfer currency internally between wallets on the same account
1810
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#l1-usdc-transfer
2687
+
2688
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#l1-usdc-transfer
2689
+
1811
2690
  :param str code: unified currency code
1812
2691
  :param float amount: amount to transfer
1813
- :param str fromAccount: account to transfer from
1814
- :param str toAccount: account to transfer to
2692
+ :param str fromAccount: account to transfer from *spot, swap*
2693
+ :param str toAccount: account to transfer to *swap, spot or address*
1815
2694
  :param dict [params]: extra parameters specific to the exchange API endpoint
2695
+ :param str [params.vaultAddress]: the vault address for order
1816
2696
  :returns dict: a `transfer structure <https://docs.ccxt.com/#/?id=transfer-structure>`
1817
2697
  """
1818
2698
  self.check_required_credentials()
1819
2699
  await self.load_markets()
2700
+ isSandboxMode = self.safe_bool(self.options, 'sandboxMode')
2701
+ nonce = self.milliseconds()
2702
+ if self.in_array(fromAccount, ['spot', 'swap', 'perp']):
2703
+ # handle swap <> spot account transfer
2704
+ if not self.in_array(toAccount, ['spot', 'swap', 'perp']):
2705
+ raise NotSupported(self.id + 'transfer() only support spot <> swap transfer')
2706
+ strAmount = self.number_to_string(amount)
2707
+ vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
2708
+ params = self.omit(params, 'vaultAddress')
2709
+ if vaultAddress is not None:
2710
+ strAmount = strAmount + ' subaccount:' + vaultAddress
2711
+ toPerp = (toAccount == 'perp') or (toAccount == 'swap')
2712
+ transferPayload: dict = {
2713
+ 'hyperliquidChain': 'Testnet' if isSandboxMode else 'Mainnet',
2714
+ 'amount': strAmount,
2715
+ 'toPerp': toPerp,
2716
+ 'nonce': nonce,
2717
+ }
2718
+ transferSig = self.build_usd_class_send_sig(transferPayload)
2719
+ transferRequest: dict = {
2720
+ 'action': {
2721
+ 'hyperliquidChain': transferPayload['hyperliquidChain'],
2722
+ 'signatureChainId': '0x66eee',
2723
+ 'type': 'usdClassTransfer',
2724
+ 'amount': strAmount,
2725
+ 'toPerp': toPerp,
2726
+ 'nonce': nonce,
2727
+ },
2728
+ 'nonce': nonce,
2729
+ 'signature': transferSig,
2730
+ }
2731
+ if vaultAddress is not None:
2732
+ transferRequest['vaultAddress'] = vaultAddress
2733
+ transferResponse = await self.privatePostExchange(transferRequest)
2734
+ return transferResponse
2735
+ # handle sub-account/different account transfer
1820
2736
  self.check_address(toAccount)
1821
2737
  if code is not None:
1822
2738
  code = code.upper()
1823
2739
  if code != 'USDC':
1824
- raise NotSupported(self.id + 'withdraw() only support USDC')
1825
- isSandboxMode = self.safe_bool(self.options, 'sandboxMode')
1826
- nonce = self.milliseconds()
1827
- payload = {
2740
+ raise NotSupported(self.id + 'transfer() only support USDC')
2741
+ payload: dict = {
2742
+ 'hyperliquidChain': 'Testnet' if isSandboxMode else 'Mainnet',
1828
2743
  'destination': toAccount,
1829
- 'amount': str(amount),
2744
+ 'amount': self.number_to_string(amount),
1830
2745
  'time': nonce,
1831
2746
  }
1832
- sig = self.build_transfer_sig(payload)
1833
- request = {
2747
+ sig = self.build_usd_send_sig(payload)
2748
+ request: dict = {
1834
2749
  'action': {
1835
- 'chain': 'ArbitrumTestnet' if (isSandboxMode) else 'Arbitrum',
1836
- 'payload': payload,
1837
- 'type': 'usdTransfer',
2750
+ 'hyperliquidChain': payload['hyperliquidChain'],
2751
+ 'signatureChainId': '0x66eee', # check self out
2752
+ 'destination': toAccount,
2753
+ 'amount': str(amount),
2754
+ 'time': nonce,
2755
+ 'type': 'usdSend',
1838
2756
  },
1839
2757
  'nonce': nonce,
1840
2758
  'signature': sig,
1841
2759
  }
1842
- response = await self.privatePostExchange(self.extend(request, params))
1843
- return response
2760
+ response = await self.privatePostExchange(request)
2761
+ #
2762
+ # {'response': {'type': 'default'}, 'status': 'ok'}
2763
+ #
2764
+ return self.parse_transfer(response)
2765
+
2766
+ def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
2767
+ #
2768
+ # {'response': {'type': 'default'}, 'status': 'ok'}
2769
+ #
2770
+ return {
2771
+ 'info': transfer,
2772
+ 'id': None,
2773
+ 'timestamp': None,
2774
+ 'datetime': None,
2775
+ 'currency': None,
2776
+ 'amount': None,
2777
+ 'fromAccount': None,
2778
+ 'toAccount': None,
2779
+ 'status': 'ok',
2780
+ }
1844
2781
 
1845
- async def withdraw(self, code: str, amount, address, tag=None, params={}):
2782
+ async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}) -> Transaction:
1846
2783
  """
1847
2784
  make a withdrawal(only support USDC)
1848
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#initiate-a-withdrawal-request
2785
+
2786
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#initiate-a-withdrawal-request
2787
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#deposit-or-withdraw-from-a-vault
2788
+
1849
2789
  :param str code: unified currency code
1850
2790
  :param float amount: the amount to withdraw
1851
2791
  :param str address: the address to withdraw to
1852
2792
  :param str tag:
1853
2793
  :param dict [params]: extra parameters specific to the exchange API endpoint
2794
+ :param str [params.vaultAddress]: vault address withdraw from
1854
2795
  :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
1855
2796
  """
1856
2797
  self.check_required_credentials()
@@ -1860,38 +2801,457 @@ class hyperliquid(Exchange, ImplicitAPI):
1860
2801
  code = code.upper()
1861
2802
  if code != 'USDC':
1862
2803
  raise NotSupported(self.id + 'withdraw() only support USDC')
1863
- isSandboxMode = self.safe_bool(self.options, 'sandboxMode')
2804
+ vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
2805
+ params = self.omit(params, 'vaultAddress')
1864
2806
  nonce = self.milliseconds()
1865
- payload = {
1866
- 'destination': address,
1867
- 'usd': str(amount),
1868
- 'time': nonce,
1869
- }
1870
- sig = self.build_withdraw_sig(payload)
1871
- request = {
1872
- 'action': {
1873
- 'chain': 'ArbitrumTestnet' if (isSandboxMode) else 'Arbitrum',
1874
- 'payload': payload,
1875
- 'type': 'withdraw2',
1876
- },
2807
+ action: dict = {}
2808
+ sig = None
2809
+ if vaultAddress is not None:
2810
+ action = {
2811
+ 'type': 'vaultTransfer',
2812
+ 'vaultAddress': '0x' + vaultAddress,
2813
+ 'isDeposit': False,
2814
+ 'usd': amount,
2815
+ }
2816
+ sig = self.sign_l1_action(action, nonce)
2817
+ else:
2818
+ isSandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
2819
+ payload: dict = {
2820
+ 'hyperliquidChain': 'Testnet' if isSandboxMode else 'Mainnet',
2821
+ 'destination': address,
2822
+ 'amount': str(amount),
2823
+ 'time': nonce,
2824
+ }
2825
+ sig = self.build_withdraw_sig(payload)
2826
+ action = {
2827
+ 'hyperliquidChain': payload['hyperliquidChain'],
2828
+ 'signatureChainId': '0x66eee', # check self out
2829
+ 'destination': address,
2830
+ 'amount': str(amount),
2831
+ 'time': nonce,
2832
+ 'type': 'withdraw3',
2833
+ }
2834
+ request: dict = {
2835
+ 'action': action,
1877
2836
  'nonce': nonce,
1878
2837
  'signature': sig,
1879
2838
  }
1880
- response = await self.privatePostExchange(self.extend(request, params))
1881
- return response
2839
+ response = await self.privatePostExchange(request)
2840
+ return self.parse_transaction(response)
2841
+
2842
+ def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
2843
+ #
2844
+ # {status: 'ok', response: {type: 'default'}}
2845
+ #
2846
+ # fetchDeposits / fetchWithdrawals
2847
+ # {
2848
+ # "time":1724762307531,
2849
+ # "hash":"0x620a234a7e0eb7930575040f59482a01050058b0802163b4767bfd9033e77781",
2850
+ # "delta":{
2851
+ # "type":"accountClassTransfer",
2852
+ # "usdc":"50.0",
2853
+ # "toPerp":false
2854
+ # }
2855
+ # }
2856
+ #
2857
+ timestamp = self.safe_integer(transaction, 'time')
2858
+ delta = self.safe_dict(transaction, 'delta', {})
2859
+ fee = None
2860
+ feeCost = self.safe_integer(delta, 'fee')
2861
+ if feeCost is not None:
2862
+ fee = {
2863
+ 'currency': 'USDC',
2864
+ 'cost': feeCost,
2865
+ }
2866
+ internal = None
2867
+ type = self.safe_string(delta, 'type')
2868
+ if type is not None:
2869
+ internal = (type == 'internalTransfer')
2870
+ return {
2871
+ 'info': transaction,
2872
+ 'id': None,
2873
+ 'txid': self.safe_string(transaction, 'hash'),
2874
+ 'timestamp': timestamp,
2875
+ 'datetime': self.iso8601(timestamp),
2876
+ 'network': None,
2877
+ 'address': None,
2878
+ 'addressTo': self.safe_string(delta, 'destination'),
2879
+ 'addressFrom': self.safe_string(delta, 'user'),
2880
+ 'tag': None,
2881
+ 'tagTo': None,
2882
+ 'tagFrom': None,
2883
+ 'type': None,
2884
+ 'amount': self.safe_integer(delta, 'usdc'),
2885
+ 'currency': None,
2886
+ 'status': self.safe_string(transaction, 'status'),
2887
+ 'updated': None,
2888
+ 'comment': None,
2889
+ 'internal': internal,
2890
+ 'fee': fee,
2891
+ }
2892
+
2893
+ async def fetch_trading_fee(self, symbol: str, params={}) -> TradingFeeInterface:
2894
+ """
2895
+ fetch the trading fees for a market
2896
+ :param str symbol: unified market symbol
2897
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2898
+ :param str [params.user]: user address, will default to self.walletAddress if not provided
2899
+ :returns dict: a `fee structure <https://docs.ccxt.com/#/?id=fee-structure>`
2900
+ """
2901
+ await self.load_markets()
2902
+ userAddress = None
2903
+ userAddress, params = self.handle_public_address('fetchTradingFee', params)
2904
+ market = self.market(symbol)
2905
+ request: dict = {
2906
+ 'type': 'userFees',
2907
+ 'user': userAddress,
2908
+ }
2909
+ response = await self.publicPostInfo(self.extend(request, params))
2910
+ #
2911
+ # {
2912
+ # "dailyUserVlm": [
2913
+ # {
2914
+ # "date": "2024-07-08",
2915
+ # "userCross": "0.0",
2916
+ # "userAdd": "0.0",
2917
+ # "exchange": "90597185.23639999"
2918
+ # }
2919
+ # ],
2920
+ # "feeSchedule": {
2921
+ # "cross": "0.00035",
2922
+ # "add": "0.0001",
2923
+ # "tiers": {
2924
+ # "vip": [
2925
+ # {
2926
+ # "ntlCutoff": "5000000.0",
2927
+ # "cross": "0.0003",
2928
+ # "add": "0.00005"
2929
+ # }
2930
+ # ],
2931
+ # "mm": [
2932
+ # {
2933
+ # "makerFractionCutoff": "0.005",
2934
+ # "add": "-0.00001"
2935
+ # }
2936
+ # ]
2937
+ # },
2938
+ # "referralDiscount": "0.04"
2939
+ # },
2940
+ # "userCrossRate": "0.00035",
2941
+ # "userAddRate": "0.0001",
2942
+ # "activeReferralDiscount": "0.0"
2943
+ # }
2944
+ #
2945
+ data: dict = {
2946
+ 'userCrossRate': self.safe_string(response, 'userCrossRate'),
2947
+ 'userAddRate': self.safe_string(response, 'userAddRate'),
2948
+ }
2949
+ return self.parse_trading_fee(data, market)
2950
+
2951
+ def parse_trading_fee(self, fee: dict, market: Market = None) -> TradingFeeInterface:
2952
+ #
2953
+ # {
2954
+ # "dailyUserVlm": [
2955
+ # {
2956
+ # "date": "2024-07-08",
2957
+ # "userCross": "0.0",
2958
+ # "userAdd": "0.0",
2959
+ # "exchange": "90597185.23639999"
2960
+ # }
2961
+ # ],
2962
+ # "feeSchedule": {
2963
+ # "cross": "0.00035",
2964
+ # "add": "0.0001",
2965
+ # "tiers": {
2966
+ # "vip": [
2967
+ # {
2968
+ # "ntlCutoff": "5000000.0",
2969
+ # "cross": "0.0003",
2970
+ # "add": "0.00005"
2971
+ # }
2972
+ # ],
2973
+ # "mm": [
2974
+ # {
2975
+ # "makerFractionCutoff": "0.005",
2976
+ # "add": "-0.00001"
2977
+ # }
2978
+ # ]
2979
+ # },
2980
+ # "referralDiscount": "0.04"
2981
+ # },
2982
+ # "userCrossRate": "0.00035",
2983
+ # "userAddRate": "0.0001",
2984
+ # "activeReferralDiscount": "0.0"
2985
+ # }
2986
+ #
2987
+ symbol = self.safe_symbol(None, market)
2988
+ return {
2989
+ 'info': fee,
2990
+ 'symbol': symbol,
2991
+ 'maker': self.safe_number(fee, 'userAddRate'),
2992
+ 'taker': self.safe_number(fee, 'userCrossRate'),
2993
+ 'percentage': None,
2994
+ 'tierBased': None,
2995
+ }
2996
+
2997
+ async def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[LedgerEntry]:
2998
+ """
2999
+ fetch the history of changes, actions done by the user or operations that altered the balance of the user
3000
+ :param str [code]: unified currency code
3001
+ :param int [since]: timestamp in ms of the earliest ledger entry
3002
+ :param int [limit]: max number of ledger entries to return
3003
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3004
+ :param int [params.until]: timestamp in ms of the latest ledger entry
3005
+ :returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger>`
3006
+ """
3007
+ await self.load_markets()
3008
+ userAddress = None
3009
+ userAddress, params = self.handle_public_address('fetchLedger', params)
3010
+ request: dict = {
3011
+ 'type': 'userNonFundingLedgerUpdates',
3012
+ 'user': userAddress,
3013
+ }
3014
+ if since is not None:
3015
+ request['startTime'] = since
3016
+ until = self.safe_integer(params, 'until')
3017
+ if until is not None:
3018
+ request['endTime'] = until
3019
+ params = self.omit(params, ['until'])
3020
+ response = await self.publicPostInfo(self.extend(request, params))
3021
+ #
3022
+ # [
3023
+ # {
3024
+ # "time":1724762307531,
3025
+ # "hash":"0x620a234a7e0eb7930575040f59482a01050058b0802163b4767bfd9033e77781",
3026
+ # "delta":{
3027
+ # "type":"accountClassTransfer",
3028
+ # "usdc":"50.0",
3029
+ # "toPerp":false
3030
+ # }
3031
+ # }
3032
+ # ]
3033
+ #
3034
+ return self.parse_ledger(response, None, since, limit)
3035
+
3036
+ def parse_ledger_entry(self, item: dict, currency: Currency = None) -> LedgerEntry:
3037
+ #
3038
+ # {
3039
+ # "time":1724762307531,
3040
+ # "hash":"0x620a234a7e0eb7930575040f59482a01050058b0802163b4767bfd9033e77781",
3041
+ # "delta":{
3042
+ # "type":"accountClassTransfer",
3043
+ # "usdc":"50.0",
3044
+ # "toPerp":false
3045
+ # }
3046
+ # }
3047
+ #
3048
+ timestamp = self.safe_integer(item, 'time')
3049
+ delta = self.safe_dict(item, 'delta', {})
3050
+ fee = None
3051
+ feeCost = self.safe_integer(delta, 'fee')
3052
+ if feeCost is not None:
3053
+ fee = {
3054
+ 'currency': 'USDC',
3055
+ 'cost': feeCost,
3056
+ }
3057
+ type = self.safe_string(delta, 'type')
3058
+ amount = self.safe_string(delta, 'usdc')
3059
+ return self.safe_ledger_entry({
3060
+ 'info': item,
3061
+ 'id': self.safe_string(item, 'hash'),
3062
+ 'direction': None,
3063
+ 'account': None,
3064
+ 'referenceAccount': self.safe_string(delta, 'user'),
3065
+ 'referenceId': self.safe_string(item, 'hash'),
3066
+ 'type': self.parse_ledger_entry_type(type),
3067
+ 'currency': None,
3068
+ 'amount': self.parse_number(amount),
3069
+ 'timestamp': timestamp,
3070
+ 'datetime': self.iso8601(timestamp),
3071
+ 'before': None,
3072
+ 'after': None,
3073
+ 'status': None,
3074
+ 'fee': fee,
3075
+ }, currency)
3076
+
3077
+ def parse_ledger_entry_type(self, type):
3078
+ ledgerType: dict = {
3079
+ 'internalTransfer': 'transfer',
3080
+ 'accountClassTransfer': 'transfer',
3081
+ }
3082
+ return self.safe_string(ledgerType, type, type)
3083
+
3084
+ async def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
3085
+ """
3086
+ fetch all deposits made to an account
3087
+ :param str code: unified currency code
3088
+ :param int [since]: the earliest time in ms to fetch deposits for
3089
+ :param int [limit]: the maximum number of deposits structures to retrieve
3090
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3091
+ :param int [params.until]: the latest time in ms to fetch withdrawals for
3092
+ :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
3093
+ """
3094
+ await self.load_markets()
3095
+ userAddress = None
3096
+ userAddress, params = self.handle_public_address('fetchDepositsWithdrawals', params)
3097
+ request: dict = {
3098
+ 'type': 'userNonFundingLedgerUpdates',
3099
+ 'user': userAddress,
3100
+ }
3101
+ if since is not None:
3102
+ request['startTime'] = since
3103
+ until = self.safe_integer(params, 'until')
3104
+ if until is not None:
3105
+ request['endTime'] = until
3106
+ params = self.omit(params, ['until'])
3107
+ response = await self.publicPostInfo(self.extend(request, params))
3108
+ #
3109
+ # [
3110
+ # {
3111
+ # "time":1724762307531,
3112
+ # "hash":"0x620a234a7e0eb7930575040f59482a01050058b0802163b4767bfd9033e77781",
3113
+ # "delta":{
3114
+ # "type":"accountClassTransfer",
3115
+ # "usdc":"50.0",
3116
+ # "toPerp":false
3117
+ # }
3118
+ # }
3119
+ # ]
3120
+ #
3121
+ records = self.extract_type_from_delta(response)
3122
+ deposits = self.filter_by_array(records, 'type', ['deposit'], False)
3123
+ return self.parse_transactions(deposits, None, since, limit)
3124
+
3125
+ async def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
3126
+ """
3127
+ fetch all withdrawals made from an account
3128
+ :param str code: unified currency code
3129
+ :param int [since]: the earliest time in ms to fetch withdrawals for
3130
+ :param int [limit]: the maximum number of withdrawals structures to retrieve
3131
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3132
+ :param int [params.until]: the latest time in ms to fetch withdrawals for
3133
+ :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
3134
+ """
3135
+ await self.load_markets()
3136
+ userAddress = None
3137
+ userAddress, params = self.handle_public_address('fetchDepositsWithdrawals', params)
3138
+ request: dict = {
3139
+ 'type': 'userNonFundingLedgerUpdates',
3140
+ 'user': userAddress,
3141
+ }
3142
+ if since is not None:
3143
+ request['startTime'] = since
3144
+ until = self.safe_integer(params, 'until')
3145
+ if until is not None:
3146
+ request['endTime'] = until
3147
+ params = self.omit(params, ['until'])
3148
+ response = await self.publicPostInfo(self.extend(request, params))
3149
+ #
3150
+ # [
3151
+ # {
3152
+ # "time":1724762307531,
3153
+ # "hash":"0x620a234a7e0eb7930575040f59482a01050058b0802163b4767bfd9033e77781",
3154
+ # "delta":{
3155
+ # "type":"accountClassTransfer",
3156
+ # "usdc":"50.0",
3157
+ # "toPerp":false
3158
+ # }
3159
+ # }
3160
+ # ]
3161
+ #
3162
+ records = self.extract_type_from_delta(response)
3163
+ withdrawals = self.filter_by_array(records, 'type', ['withdraw'], False)
3164
+ return self.parse_transactions(withdrawals, None, since, limit)
3165
+
3166
+ async def fetch_open_interests(self, symbols: Strings = None, params={}):
3167
+ """
3168
+ Retrieves the open interest for a list of symbols
3169
+ :param str[] [symbols]: Unified CCXT market symbol
3170
+ :param dict [params]: exchange specific parameters
3171
+ :returns dict} an open interest structure{@link https://docs.ccxt.com/#/?id=open-interest-structure:
3172
+ """
3173
+ await self.load_markets()
3174
+ symbols = self.market_symbols(symbols)
3175
+ swapMarkets = await self.fetch_swap_markets()
3176
+ return self.parse_open_interests(swapMarkets, symbols)
3177
+
3178
+ async def fetch_open_interest(self, symbol: str, params={}):
3179
+ """
3180
+ retrieves the open interest of a contract trading pair
3181
+ :param str symbol: unified CCXT market symbol
3182
+ :param dict [params]: exchange specific parameters
3183
+ :returns dict: an `open interest structure <https://docs.ccxt.com/#/?id=open-interest-structure>`
3184
+ """
3185
+ symbol = self.symbol(symbol)
3186
+ await self.load_markets()
3187
+ ois = await self.fetch_open_interests([symbol], params)
3188
+ return ois[symbol]
3189
+
3190
+ def parse_open_interest(self, interest, market: Market = None):
3191
+ #
3192
+ # {
3193
+ # szDecimals: '2',
3194
+ # name: 'HYPE',
3195
+ # maxLeverage: '3',
3196
+ # funding: '0.00014735',
3197
+ # openInterest: '14677900.74',
3198
+ # prevDayPx: '26.145',
3199
+ # dayNtlVlm: '299643445.12560016',
3200
+ # premium: '0.00081613',
3201
+ # oraclePx: '27.569',
3202
+ # markPx: '27.63',
3203
+ # midPx: '27.599',
3204
+ # impactPxs: ['27.5915', '27.6319'],
3205
+ # dayBaseVlm: '10790652.83',
3206
+ # baseId: 159
3207
+ # }
3208
+ #
3209
+ interest = self.safe_dict(interest, 'info', {})
3210
+ coin = self.safe_string(interest, 'name')
3211
+ marketId = None
3212
+ if coin is not None:
3213
+ marketId = self.coin_to_market_id(coin)
3214
+ return self.safe_open_interest({
3215
+ 'symbol': self.safe_symbol(marketId),
3216
+ 'openInterestAmount': self.safe_number(interest, 'openInterest'),
3217
+ 'openInterestValue': None,
3218
+ 'timestamp': None,
3219
+ 'datetime': None,
3220
+ 'info': interest,
3221
+ }, market)
3222
+
3223
+ def extract_type_from_delta(self, data=[]):
3224
+ records = []
3225
+ for i in range(0, len(data)):
3226
+ record = data[i]
3227
+ record['type'] = record['delta']['type']
3228
+ records.append(record)
3229
+ return records
3230
+
3231
+ def format_vault_address(self, address: Str = None):
3232
+ if address is None:
3233
+ return None
3234
+ if address.startswith('0x'):
3235
+ return address.replace('0x', '')
3236
+ return address
1882
3237
 
1883
3238
  def handle_public_address(self, methodName: str, params: dict):
1884
3239
  userAux = None
1885
3240
  userAux, params = self.handle_option_and_params(params, methodName, 'user')
1886
3241
  user = userAux
1887
3242
  user, params = self.handle_option_and_params(params, methodName, 'address', userAux)
1888
- if user is not None:
3243
+ if (user is not None) and (user != ''):
1889
3244
  return [user, params]
1890
- if self.walletAddress is not None:
3245
+ if (self.walletAddress is not None) and (self.walletAddress != ''):
1891
3246
  return [self.walletAddress, params]
1892
3247
  raise ArgumentsRequired(self.id + ' ' + methodName + '() requires a user parameter inside \'params\' or the wallet address set')
1893
3248
 
1894
- def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
3249
+ def coin_to_market_id(self, coin: Str):
3250
+ if coin.find('/') > -1 or coin.find('@') > -1:
3251
+ return coin # spot
3252
+ return coin + '/USDC:USDC'
3253
+
3254
+ def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
1895
3255
  if not response:
1896
3256
  return None # fallback to default error handler
1897
3257
  # {"status":"err","response":"User or API Wallet 0xb8a6f8b26223de27c31938d56e470a5b832703a5 does not exist."}
@@ -1928,3 +3288,29 @@ class hyperliquid(Exchange, ImplicitAPI):
1928
3288
  }
1929
3289
  body = self.json(params)
1930
3290
  return {'url': url, 'method': method, 'body': body, 'headers': headers}
3291
+
3292
+ def calculate_rate_limiter_cost(self, api, method, path, params, config={}):
3293
+ if ('byType' in config) and ('type' in params):
3294
+ type = params['type']
3295
+ byType = config['byType']
3296
+ if type in byType:
3297
+ return byType[type]
3298
+ return self.safe_value(config, 'cost', 1)
3299
+
3300
+ def parse_create_order_args(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
3301
+ market = self.market(symbol)
3302
+ vaultAddress = self.safe_string(params, 'vaultAddress')
3303
+ params = self.omit(params, 'vaultAddress')
3304
+ symbol = market['symbol']
3305
+ order = {
3306
+ 'symbol': symbol,
3307
+ 'type': type,
3308
+ 'side': side,
3309
+ 'amount': amount,
3310
+ 'price': price,
3311
+ 'params': params,
3312
+ }
3313
+ globalParams = {}
3314
+ if vaultAddress is not None:
3315
+ globalParams['vaultAddress'] = vaultAddress
3316
+ return [order, globalParams]