ccxt-ir 4.3.46.0.2__py2.py3-none-any.whl → 4.5.0__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 (528) hide show
  1. ccxt/__init__.py +39 -35
  2. ccxt/abantether.py +9 -9
  3. ccxt/abstract/alpaca.py +4 -0
  4. ccxt/abstract/apex.py +31 -0
  5. ccxt/abstract/bigone.py +1 -1
  6. ccxt/abstract/binance.py +106 -48
  7. ccxt/abstract/binancecoinm.py +106 -48
  8. ccxt/abstract/binanceus.py +141 -83
  9. ccxt/abstract/binanceusdm.py +106 -48
  10. ccxt/abstract/bingx.py +50 -1
  11. ccxt/abstract/bitbank.py +5 -0
  12. ccxt/abstract/bitfinex.py +136 -65
  13. ccxt/abstract/bitflyer.py +1 -0
  14. ccxt/abstract/bitget.py +67 -0
  15. ccxt/abstract/bitmart.py +19 -1
  16. ccxt/abstract/bitopro.py +1 -0
  17. ccxt/abstract/bitrue.py +68 -68
  18. ccxt/abstract/bitstamp.py +1 -0
  19. ccxt/abstract/blofin.py +30 -0
  20. ccxt/abstract/btcbox.py +2 -0
  21. ccxt/abstract/bybit.py +28 -13
  22. ccxt/abstract/cex.py +28 -29
  23. ccxt/abstract/coinbaseexchange.py +1 -0
  24. ccxt/abstract/coinbaseinternational.py +1 -1
  25. ccxt/abstract/cryptocom.py +16 -0
  26. ccxt/abstract/cryptomus.py +20 -0
  27. ccxt/abstract/defx.py +69 -0
  28. ccxt/abstract/deribit.py +1 -0
  29. ccxt/abstract/derive.py +117 -0
  30. ccxt/abstract/digifinex.py +1 -0
  31. ccxt/abstract/ellipx.py +25 -0
  32. ccxt/abstract/foxbit.py +26 -0
  33. ccxt/abstract/gate.py +19 -0
  34. ccxt/abstract/gateio.py +19 -0
  35. ccxt/abstract/gemini.py +1 -0
  36. ccxt/abstract/hibachi.py +26 -0
  37. ccxt/abstract/hyperliquid.py +1 -1
  38. ccxt/abstract/independentreserve.py +6 -0
  39. ccxt/abstract/kraken.py +1 -0
  40. ccxt/abstract/krakenfutures.py +4 -0
  41. ccxt/abstract/kucoin.py +10 -0
  42. ccxt/abstract/kucoinfutures.py +18 -0
  43. ccxt/abstract/lbank.py +2 -1
  44. ccxt/abstract/luno.py +1 -0
  45. ccxt/abstract/mexc.py +2 -0
  46. ccxt/abstract/modetrade.py +119 -0
  47. ccxt/abstract/myokx.py +349 -0
  48. ccxt/abstract/oceanex.py +5 -0
  49. ccxt/abstract/okx.py +25 -0
  50. ccxt/abstract/okxus.py +349 -0
  51. ccxt/abstract/onetrading.py +0 -12
  52. ccxt/abstract/paradex.py +23 -0
  53. ccxt/abstract/phemex.py +2 -0
  54. ccxt/abstract/poloniex.py +36 -0
  55. ccxt/abstract/tradeogre.py +3 -1
  56. ccxt/abstract/upbit.py +51 -34
  57. ccxt/abstract/whitebit.py +16 -0
  58. ccxt/abstract/woo.py +64 -6
  59. ccxt/abstract/xt.py +10 -5
  60. ccxt/afratether.py +7 -7
  61. ccxt/alpaca.py +828 -51
  62. ccxt/apex.py +1875 -0
  63. ccxt/arzinja.py +7 -7
  64. ccxt/arzplus.py +9 -9
  65. ccxt/ascendex.py +501 -306
  66. ccxt/async_support/__init__.py +39 -35
  67. ccxt/async_support/abantether.py +10 -10
  68. ccxt/async_support/afratether.py +9 -9
  69. ccxt/async_support/alpaca.py +828 -51
  70. ccxt/async_support/apex.py +1875 -0
  71. ccxt/async_support/arzinja.py +10 -10
  72. ccxt/async_support/arzplus.py +12 -12
  73. ccxt/async_support/ascendex.py +502 -306
  74. ccxt/async_support/base/exchange.py +303 -89
  75. ccxt/async_support/base/ws/cache.py +9 -3
  76. ccxt/async_support/base/ws/client.py +173 -38
  77. ccxt/async_support/base/ws/future.py +25 -37
  78. ccxt/async_support/bequant.py +5 -3
  79. ccxt/async_support/bigone.py +279 -144
  80. ccxt/async_support/binance.py +2347 -1158
  81. ccxt/async_support/binancecoinm.py +9 -3
  82. ccxt/async_support/binanceus.py +17 -3
  83. ccxt/async_support/binanceusdm.py +9 -4
  84. ccxt/async_support/bingx.py +2962 -920
  85. ccxt/async_support/bit2c.py +147 -27
  86. ccxt/async_support/bitbank.py +151 -23
  87. ccxt/async_support/bitbns.py +104 -30
  88. ccxt/async_support/bitfinex.py +3291 -1113
  89. ccxt/async_support/bitflyer.py +202 -27
  90. ccxt/async_support/bitget.py +3683 -1538
  91. ccxt/async_support/bithumb.py +195 -38
  92. ccxt/async_support/bitimen.py +12 -12
  93. ccxt/async_support/bitir.py +38 -38
  94. ccxt/async_support/bitmart.py +1288 -350
  95. ccxt/async_support/bitmex.py +260 -75
  96. ccxt/async_support/bitopro.py +262 -62
  97. ccxt/async_support/bitpin.py +17 -16
  98. ccxt/async_support/bitrue.py +459 -290
  99. ccxt/async_support/bitso.py +199 -54
  100. ccxt/async_support/bitstamp.py +230 -96
  101. ccxt/async_support/bitteam.py +167 -25
  102. ccxt/async_support/{huobijp.py → bittrade.py} +158 -30
  103. ccxt/async_support/bitvavo.py +213 -49
  104. ccxt/async_support/blockchaincom.py +160 -46
  105. ccxt/async_support/blofin.py +502 -120
  106. ccxt/async_support/btcalpha.py +169 -31
  107. ccxt/async_support/btcbox.py +292 -23
  108. ccxt/async_support/btcmarkets.py +211 -58
  109. ccxt/async_support/btcturk.py +161 -38
  110. ccxt/async_support/bybit.py +1775 -1030
  111. ccxt/async_support/cex.py +1440 -1303
  112. ccxt/async_support/coinbase.py +724 -212
  113. ccxt/async_support/coinbaseadvanced.py +2 -1
  114. ccxt/async_support/coinbaseexchange.py +388 -89
  115. ccxt/async_support/coinbaseinternational.py +412 -57
  116. ccxt/async_support/coincatch.py +177 -78
  117. ccxt/async_support/coincheck.py +135 -19
  118. ccxt/async_support/coinex.py +606 -232
  119. ccxt/async_support/coinmate.py +189 -63
  120. ccxt/async_support/coinmetro.py +195 -54
  121. ccxt/async_support/coinone.py +158 -51
  122. ccxt/async_support/coinsph.py +336 -61
  123. ccxt/async_support/coinspot.py +151 -52
  124. ccxt/async_support/cryptocom.py +661 -111
  125. ccxt/async_support/cryptomus.py +1137 -0
  126. ccxt/async_support/defx.py +2071 -0
  127. ccxt/async_support/delta.py +299 -99
  128. ccxt/async_support/deribit.py +348 -126
  129. ccxt/async_support/derive.py +2572 -0
  130. ccxt/async_support/digifinex.py +430 -214
  131. ccxt/async_support/ellipx.py +2029 -0
  132. ccxt/async_support/eterex.py +10 -10
  133. ccxt/async_support/excoino.py +31 -31
  134. ccxt/async_support/exir.py +14 -14
  135. ccxt/async_support/exmo.py +344 -131
  136. ccxt/async_support/exnovin.py +10 -10
  137. ccxt/async_support/farhadexchange.py +12 -12
  138. ccxt/async_support/fmfwio.py +2 -1
  139. ccxt/async_support/foxbit.py +1935 -0
  140. ccxt/async_support/gate.py +1351 -529
  141. ccxt/async_support/gateio.py +2 -1
  142. ccxt/async_support/gemini.py +144 -39
  143. ccxt/async_support/hashkey.py +152 -109
  144. ccxt/async_support/hibachi.py +2080 -0
  145. ccxt/async_support/hitbtc.py +395 -167
  146. ccxt/async_support/hitobit.py +12 -12
  147. ccxt/async_support/hollaex.py +307 -119
  148. ccxt/async_support/htx.py +851 -383
  149. ccxt/async_support/huobi.py +2 -1
  150. ccxt/async_support/hyperliquid.py +1848 -536
  151. ccxt/async_support/independentreserve.py +288 -15
  152. ccxt/async_support/indodax.py +190 -33
  153. ccxt/async_support/jibitex.py +12 -12
  154. ccxt/async_support/kraken.py +795 -351
  155. ccxt/async_support/krakenfutures.py +214 -62
  156. ccxt/async_support/kucoin.py +715 -396
  157. ccxt/async_support/kucoinfutures.py +652 -89
  158. ccxt/async_support/latoken.py +217 -113
  159. ccxt/async_support/lbank.py +425 -97
  160. ccxt/async_support/luno.py +382 -35
  161. ccxt/async_support/mercado.py +113 -6
  162. ccxt/async_support/mexc.py +874 -437
  163. ccxt/async_support/modetrade.py +2818 -0
  164. ccxt/async_support/myokx.py +54 -0
  165. ccxt/async_support/ndax.py +221 -64
  166. ccxt/async_support/nobitex.py +31 -37
  167. ccxt/async_support/novadax.py +190 -34
  168. ccxt/async_support/oceanex.py +217 -28
  169. ccxt/async_support/okcoin.py +253 -145
  170. ccxt/async_support/okexchange.py +11 -11
  171. ccxt/async_support/okx.py +1088 -351
  172. ccxt/async_support/okxus.py +54 -0
  173. ccxt/async_support/ompfinex.py +25 -24
  174. ccxt/async_support/onetrading.py +213 -392
  175. ccxt/async_support/oxfun.py +245 -166
  176. ccxt/async_support/p2b.py +151 -29
  177. ccxt/async_support/paradex.py +562 -49
  178. ccxt/async_support/paymium.py +82 -19
  179. ccxt/async_support/phemex.py +713 -172
  180. ccxt/async_support/poloniex.py +1602 -283
  181. ccxt/async_support/probit.py +224 -95
  182. ccxt/async_support/ramzinex.py +30 -27
  183. ccxt/async_support/sarmayex.py +9 -9
  184. ccxt/async_support/sarrafex.py +13 -13
  185. ccxt/async_support/tabdeal.py +14 -13
  186. ccxt/async_support/tetherland.py +9 -9
  187. ccxt/async_support/timex.py +210 -51
  188. ccxt/async_support/tokocrypto.py +167 -47
  189. ccxt/async_support/tradeogre.py +266 -31
  190. ccxt/async_support/twox.py +9 -9
  191. ccxt/async_support/ubitex.py +12 -12
  192. ccxt/async_support/upbit.py +568 -165
  193. ccxt/async_support/vertex.py +160 -32
  194. ccxt/async_support/wallex.py +12 -12
  195. ccxt/async_support/wavesexchange.py +165 -30
  196. ccxt/async_support/whitebit.py +975 -127
  197. ccxt/async_support/woo.py +1918 -1016
  198. ccxt/async_support/woofipro.py +433 -141
  199. ccxt/async_support/xt.py +649 -193
  200. ccxt/async_support/yobit.py +195 -70
  201. ccxt/async_support/zaif.py +91 -15
  202. ccxt/async_support/zonda.py +151 -36
  203. ccxt/base/decimal_to_precision.py +14 -10
  204. ccxt/base/errors.py +49 -18
  205. ccxt/base/exchange.py +1556 -450
  206. ccxt/base/precise.py +10 -0
  207. ccxt/base/types.py +114 -6
  208. ccxt/bequant.py +5 -3
  209. ccxt/bigone.py +279 -144
  210. ccxt/binance.py +2347 -1158
  211. ccxt/binancecoinm.py +9 -3
  212. ccxt/binanceus.py +17 -3
  213. ccxt/binanceusdm.py +9 -4
  214. ccxt/bingx.py +2962 -920
  215. ccxt/bit2c.py +147 -27
  216. ccxt/bitbank.py +151 -23
  217. ccxt/bitbns.py +104 -30
  218. ccxt/bitfinex.py +3290 -1113
  219. ccxt/bitflyer.py +202 -27
  220. ccxt/bitget.py +3683 -1538
  221. ccxt/bithumb.py +194 -38
  222. ccxt/bitimen.py +9 -9
  223. ccxt/bitir.py +35 -35
  224. ccxt/bitmart.py +1288 -350
  225. ccxt/bitmex.py +260 -75
  226. ccxt/bitopro.py +262 -62
  227. ccxt/bitpin.py +15 -14
  228. ccxt/bitrue.py +459 -290
  229. ccxt/bitso.py +199 -54
  230. ccxt/bitstamp.py +230 -96
  231. ccxt/bitteam.py +167 -25
  232. ccxt/{huobijp.py → bittrade.py} +158 -30
  233. ccxt/bitvavo.py +213 -49
  234. ccxt/blockchaincom.py +160 -46
  235. ccxt/blofin.py +502 -120
  236. ccxt/btcalpha.py +169 -31
  237. ccxt/btcbox.py +291 -23
  238. ccxt/btcmarkets.py +211 -58
  239. ccxt/btcturk.py +161 -38
  240. ccxt/bybit.py +1775 -1030
  241. ccxt/cex.py +1439 -1303
  242. ccxt/coinbase.py +724 -212
  243. ccxt/coinbaseadvanced.py +2 -1
  244. ccxt/coinbaseexchange.py +388 -89
  245. ccxt/coinbaseinternational.py +412 -57
  246. ccxt/coincatch.py +177 -78
  247. ccxt/coincheck.py +135 -19
  248. ccxt/coinex.py +606 -232
  249. ccxt/coinmate.py +189 -63
  250. ccxt/coinmetro.py +194 -54
  251. ccxt/coinone.py +158 -51
  252. ccxt/coinsph.py +336 -61
  253. ccxt/coinspot.py +151 -52
  254. ccxt/cryptocom.py +661 -111
  255. ccxt/cryptomus.py +1137 -0
  256. ccxt/defx.py +2070 -0
  257. ccxt/delta.py +299 -99
  258. ccxt/deribit.py +348 -126
  259. ccxt/derive.py +2571 -0
  260. ccxt/digifinex.py +430 -214
  261. ccxt/ellipx.py +2029 -0
  262. ccxt/eterex.py +7 -7
  263. ccxt/excoino.py +29 -29
  264. ccxt/exir.py +11 -11
  265. ccxt/exmo.py +343 -131
  266. ccxt/exnovin.py +8 -8
  267. ccxt/farhadexchange.py +10 -10
  268. ccxt/fmfwio.py +2 -1
  269. ccxt/foxbit.py +1935 -0
  270. ccxt/gate.py +1351 -529
  271. ccxt/gateio.py +2 -1
  272. ccxt/gemini.py +144 -39
  273. ccxt/hashkey.py +152 -109
  274. ccxt/hibachi.py +2079 -0
  275. ccxt/hitbtc.py +395 -167
  276. ccxt/hitobit.py +9 -9
  277. ccxt/hollaex.py +307 -119
  278. ccxt/htx.py +851 -383
  279. ccxt/huobi.py +2 -1
  280. ccxt/hyperliquid.py +1848 -536
  281. ccxt/independentreserve.py +287 -15
  282. ccxt/indodax.py +190 -33
  283. ccxt/jibitex.py +9 -9
  284. ccxt/kraken.py +794 -351
  285. ccxt/krakenfutures.py +214 -62
  286. ccxt/kucoin.py +715 -396
  287. ccxt/kucoinfutures.py +652 -89
  288. ccxt/latoken.py +217 -113
  289. ccxt/lbank.py +425 -97
  290. ccxt/luno.py +382 -35
  291. ccxt/mercado.py +113 -6
  292. ccxt/mexc.py +873 -437
  293. ccxt/modetrade.py +2818 -0
  294. ccxt/myokx.py +54 -0
  295. ccxt/ndax.py +221 -64
  296. ccxt/nobitex.py +29 -35
  297. ccxt/novadax.py +190 -34
  298. ccxt/oceanex.py +217 -28
  299. ccxt/okcoin.py +253 -145
  300. ccxt/okexchange.py +9 -9
  301. ccxt/okx.py +1088 -351
  302. ccxt/okxus.py +54 -0
  303. ccxt/ompfinex.py +22 -21
  304. ccxt/onetrading.py +213 -392
  305. ccxt/oxfun.py +245 -166
  306. ccxt/p2b.py +151 -29
  307. ccxt/paradex.py +562 -49
  308. ccxt/paymium.py +82 -19
  309. ccxt/phemex.py +712 -172
  310. ccxt/poloniex.py +1601 -283
  311. ccxt/pro/__init__.py +76 -17
  312. ccxt/pro/alpaca.py +21 -6
  313. ccxt/pro/apex.py +984 -0
  314. ccxt/pro/ascendex.py +58 -10
  315. ccxt/pro/bequant.py +6 -1
  316. ccxt/pro/binance.py +728 -156
  317. ccxt/pro/binancecoinm.py +6 -2
  318. ccxt/pro/binanceus.py +8 -4
  319. ccxt/pro/binanceusdm.py +7 -2
  320. ccxt/pro/bingx.py +333 -142
  321. ccxt/pro/bitfinex.py +727 -262
  322. ccxt/pro/bitget.py +570 -79
  323. ccxt/pro/bithumb.py +20 -6
  324. ccxt/pro/bitmart.py +216 -87
  325. ccxt/pro/bitmex.py +47 -9
  326. ccxt/pro/bitopro.py +26 -14
  327. ccxt/pro/bitrue.py +22 -22
  328. ccxt/pro/bitstamp.py +54 -21
  329. ccxt/pro/{huobijp.py → bittrade.py} +7 -6
  330. ccxt/pro/bitvavo.py +191 -67
  331. ccxt/pro/blockchaincom.py +21 -8
  332. ccxt/pro/blofin.py +9 -1
  333. ccxt/pro/bybit.py +632 -245
  334. ccxt/pro/cex.py +59 -24
  335. ccxt/pro/coinbase.py +102 -73
  336. ccxt/pro/coinbaseadvanced.py +2 -1
  337. ccxt/pro/coinbaseexchange.py +8 -8
  338. ccxt/pro/coinbaseinternational.py +181 -25
  339. ccxt/pro/coincatch.py +6 -7
  340. ccxt/pro/coincheck.py +11 -6
  341. ccxt/pro/coinex.py +967 -665
  342. ccxt/pro/coinone.py +16 -9
  343. ccxt/pro/cryptocom.py +448 -45
  344. ccxt/pro/defx.py +831 -0
  345. ccxt/pro/deribit.py +150 -14
  346. ccxt/pro/derive.py +704 -0
  347. ccxt/pro/exmo.py +239 -6
  348. ccxt/pro/gate.py +623 -65
  349. ccxt/pro/gateio.py +2 -1
  350. ccxt/pro/gemini.py +27 -11
  351. ccxt/pro/hashkey.py +2 -2
  352. ccxt/pro/hitbtc.py +196 -91
  353. ccxt/pro/hollaex.py +23 -7
  354. ccxt/pro/htx.py +51 -14
  355. ccxt/pro/huobi.py +2 -1
  356. ccxt/pro/hyperliquid.py +591 -27
  357. ccxt/pro/independentreserve.py +9 -6
  358. ccxt/pro/kraken.py +640 -320
  359. ccxt/pro/krakenfutures.py +62 -35
  360. ccxt/pro/kucoin.py +267 -46
  361. ccxt/pro/kucoinfutures.py +165 -21
  362. ccxt/pro/lbank.py +102 -21
  363. ccxt/pro/luno.py +12 -8
  364. ccxt/pro/mexc.py +877 -111
  365. ccxt/pro/modetrade.py +1271 -0
  366. ccxt/pro/myokx.py +38 -0
  367. ccxt/pro/ndax.py +15 -2
  368. ccxt/pro/okcoin.py +23 -4
  369. ccxt/pro/okx.py +573 -98
  370. ccxt/pro/okxus.py +38 -0
  371. ccxt/pro/onetrading.py +30 -13
  372. ccxt/pro/oxfun.py +131 -27
  373. ccxt/pro/p2b.py +88 -22
  374. ccxt/pro/paradex.py +3 -3
  375. ccxt/pro/phemex.py +75 -21
  376. ccxt/pro/poloniex.py +124 -41
  377. ccxt/pro/probit.py +87 -80
  378. ccxt/pro/tradeogre.py +272 -0
  379. ccxt/pro/upbit.py +152 -12
  380. ccxt/pro/vertex.py +8 -3
  381. ccxt/pro/whitebit.py +58 -5
  382. ccxt/pro/woo.py +228 -37
  383. ccxt/pro/woofipro.py +106 -18
  384. ccxt/pro/xt.py +111 -5
  385. ccxt/probit.py +224 -95
  386. ccxt/protobuf/__init__.py +0 -0
  387. ccxt/protobuf/mexc/PrivateAccountV3Api_pb2.py +37 -0
  388. ccxt/protobuf/mexc/PrivateDealsV3Api_pb2.py +37 -0
  389. ccxt/protobuf/mexc/PrivateOrdersV3Api_pb2.py +37 -0
  390. ccxt/protobuf/mexc/PublicAggreBookTickerV3Api_pb2.py +37 -0
  391. ccxt/protobuf/mexc/PublicAggreDealsV3Api_pb2.py +39 -0
  392. ccxt/protobuf/mexc/PublicAggreDepthsV3Api_pb2.py +39 -0
  393. ccxt/protobuf/mexc/PublicBookTickerBatchV3Api_pb2.py +38 -0
  394. ccxt/protobuf/mexc/PublicBookTickerV3Api_pb2.py +37 -0
  395. ccxt/protobuf/mexc/PublicDealsV3Api_pb2.py +39 -0
  396. ccxt/protobuf/mexc/PublicIncreaseDepthsBatchV3Api_pb2.py +38 -0
  397. ccxt/protobuf/mexc/PublicIncreaseDepthsV3Api_pb2.py +39 -0
  398. ccxt/protobuf/mexc/PublicLimitDepthsV3Api_pb2.py +39 -0
  399. ccxt/protobuf/mexc/PublicMiniTickerV3Api_pb2.py +37 -0
  400. ccxt/protobuf/mexc/PublicMiniTickersV3Api_pb2.py +38 -0
  401. ccxt/protobuf/mexc/PublicSpotKlineV3Api_pb2.py +37 -0
  402. ccxt/protobuf/mexc/PushDataV3ApiWrapper_pb2.py +52 -0
  403. ccxt/protobuf/mexc/__init__.py +0 -0
  404. ccxt/ramzinex.py +28 -25
  405. ccxt/sarmayex.py +7 -7
  406. ccxt/sarrafex.py +10 -10
  407. ccxt/static_dependencies/__init__.py +1 -1
  408. ccxt/static_dependencies/lark/py.typed +0 -0
  409. ccxt/static_dependencies/marshmallow/py.typed +0 -0
  410. ccxt/static_dependencies/marshmallow_dataclass/py.typed +0 -0
  411. ccxt/static_dependencies/marshmallow_oneofschema/py.typed +0 -0
  412. ccxt/tabdeal.py +12 -11
  413. ccxt/test/tests_async.py +261 -57
  414. ccxt/test/tests_helpers.py +1 -3
  415. ccxt/test/tests_init.py +4 -3
  416. ccxt/test/tests_sync.py +261 -57
  417. ccxt/tetherland.py +7 -7
  418. ccxt/timex.py +210 -51
  419. ccxt/tokocrypto.py +167 -47
  420. ccxt/tradeogre.py +266 -31
  421. ccxt/twox.py +7 -7
  422. ccxt/ubitex.py +9 -9
  423. ccxt/upbit.py +568 -165
  424. ccxt/vertex.py +160 -32
  425. ccxt/wallex.py +9 -9
  426. ccxt/wavesexchange.py +165 -30
  427. ccxt/whitebit.py +975 -127
  428. ccxt/woo.py +1917 -1016
  429. ccxt/woofipro.py +432 -141
  430. ccxt/xt.py +649 -193
  431. ccxt/yobit.py +194 -70
  432. ccxt/zaif.py +91 -15
  433. ccxt/zonda.py +151 -36
  434. {ccxt_ir-4.3.46.0.2.dist-info → ccxt_ir-4.5.0.dist-info}/METADATA +225 -73
  435. ccxt_ir-4.5.0.dist-info/RECORD +743 -0
  436. {ccxt_ir-4.3.46.0.2.dist-info → ccxt_ir-4.5.0.dist-info}/WHEEL +1 -1
  437. ccxt/abstract/ace.py +0 -15
  438. ccxt/abstract/bitbay.py +0 -53
  439. ccxt/abstract/bitcoincom.py +0 -115
  440. ccxt/abstract/bitfinex2.py +0 -139
  441. ccxt/abstract/bitpanda.py +0 -35
  442. ccxt/abstract/bl3p.py +0 -19
  443. ccxt/abstract/coinlist.py +0 -54
  444. ccxt/abstract/currencycom.py +0 -68
  445. ccxt/abstract/hitbtc3.py +0 -115
  446. ccxt/abstract/idex.py +0 -26
  447. ccxt/abstract/kuna.py +0 -182
  448. ccxt/abstract/lykke.py +0 -29
  449. ccxt/abstract/poloniexfutures.py +0 -48
  450. ccxt/abstract/wazirx.py +0 -30
  451. ccxt/ace.py +0 -1012
  452. ccxt/async_support/ace.py +0 -1012
  453. ccxt/async_support/base/ws/aiohttp_client.py +0 -125
  454. ccxt/async_support/base/ws/fast_client.py +0 -96
  455. ccxt/async_support/bitbay.py +0 -17
  456. ccxt/async_support/bitcoincom.py +0 -17
  457. ccxt/async_support/bitfinex2.py +0 -3552
  458. ccxt/async_support/bitpanda.py +0 -16
  459. ccxt/async_support/bl3p.py +0 -485
  460. ccxt/async_support/coinlist.py +0 -2243
  461. ccxt/async_support/currencycom.py +0 -1950
  462. ccxt/async_support/hitbtc3.py +0 -16
  463. ccxt/async_support/idex.py +0 -1766
  464. ccxt/async_support/kuna.py +0 -1841
  465. ccxt/async_support/lykke.py +0 -1270
  466. ccxt/async_support/poloniexfutures.py +0 -1717
  467. ccxt/async_support/wazirx.py +0 -1224
  468. ccxt/bitbay.py +0 -17
  469. ccxt/bitcoincom.py +0 -17
  470. ccxt/bitfinex2.py +0 -3552
  471. ccxt/bitpanda.py +0 -16
  472. ccxt/bl3p.py +0 -485
  473. ccxt/coinlist.py +0 -2243
  474. ccxt/currencycom.py +0 -1950
  475. ccxt/hitbtc3.py +0 -16
  476. ccxt/idex.py +0 -1766
  477. ccxt/kuna.py +0 -1841
  478. ccxt/lykke.py +0 -1270
  479. ccxt/poloniexfutures.py +0 -1717
  480. ccxt/pro/bitcoincom.py +0 -34
  481. ccxt/pro/bitfinex2.py +0 -1083
  482. ccxt/pro/bitpanda.py +0 -15
  483. ccxt/pro/currencycom.py +0 -536
  484. ccxt/pro/idex.py +0 -672
  485. ccxt/pro/poloniexfutures.py +0 -990
  486. ccxt/pro/wazirx.py +0 -749
  487. ccxt/test/base/__init__.py +0 -29
  488. ccxt/test/base/test_account.py +0 -26
  489. ccxt/test/base/test_balance.py +0 -56
  490. ccxt/test/base/test_borrow_interest.py +0 -35
  491. ccxt/test/base/test_borrow_rate.py +0 -32
  492. ccxt/test/base/test_calculate_fee.py +0 -51
  493. ccxt/test/base/test_crypto.py +0 -127
  494. ccxt/test/base/test_currency.py +0 -76
  495. ccxt/test/base/test_datetime.py +0 -109
  496. ccxt/test/base/test_decimal_to_precision.py +0 -392
  497. ccxt/test/base/test_deep_extend.py +0 -68
  498. ccxt/test/base/test_deposit_withdrawal.py +0 -50
  499. ccxt/test/base/test_exchange_datetime_functions.py +0 -76
  500. ccxt/test/base/test_funding_rate_history.py +0 -29
  501. ccxt/test/base/test_last_price.py +0 -31
  502. ccxt/test/base/test_ledger_entry.py +0 -45
  503. ccxt/test/base/test_ledger_item.py +0 -48
  504. ccxt/test/base/test_leverage_tier.py +0 -33
  505. ccxt/test/base/test_liquidation.py +0 -50
  506. ccxt/test/base/test_margin_mode.py +0 -24
  507. ccxt/test/base/test_margin_modification.py +0 -35
  508. ccxt/test/base/test_market.py +0 -193
  509. ccxt/test/base/test_number.py +0 -411
  510. ccxt/test/base/test_ohlcv.py +0 -33
  511. ccxt/test/base/test_open_interest.py +0 -32
  512. ccxt/test/base/test_order.py +0 -64
  513. ccxt/test/base/test_order_book.py +0 -69
  514. ccxt/test/base/test_position.py +0 -60
  515. ccxt/test/base/test_shared_methods.py +0 -353
  516. ccxt/test/base/test_status.py +0 -24
  517. ccxt/test/base/test_throttle.py +0 -126
  518. ccxt/test/base/test_ticker.py +0 -92
  519. ccxt/test/base/test_trade.py +0 -47
  520. ccxt/test/base/test_trading_fee.py +0 -26
  521. ccxt/test/base/test_transaction.py +0 -39
  522. ccxt/test/test_async.py +0 -1649
  523. ccxt/test/test_sync.py +0 -1648
  524. ccxt/wazirx.py +0 -1224
  525. ccxt_ir-4.3.46.0.2.dist-info/RECORD +0 -772
  526. /ccxt/abstract/{huobijp.py → bittrade.py} +0 -0
  527. {ccxt_ir-4.3.46.0.2.dist-info → ccxt_ir-4.5.0.dist-info/licenses}/LICENSE.txt +0 -0
  528. {ccxt_ir-4.3.46.0.2.dist-info → ccxt_ir-4.5.0.dist-info}/top_level.txt +0 -0
@@ -6,11 +6,13 @@
6
6
  from ccxt.async_support.base.exchange import Exchange
7
7
  from ccxt.abstract.hyperliquid import ImplicitAPI
8
8
  import asyncio
9
- from ccxt.base.types import Balances, Currencies, Currency, Int, MarginModification, Market, Num, Order, OrderBook, OrderRequest, CancellationRequest, OrderSide, OrderType, Str, Strings, Trade, Transaction, TransferEntry
9
+ import math
10
+ from ccxt.base.types import Any, Balances, Currencies, Currency, Int, LedgerEntry, MarginModification, Market, Num, Order, OrderBook, OrderRequest, CancellationRequest, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade, TradingFeeInterface, Transaction, TransferEntry
10
11
  from typing import List
11
12
  from ccxt.base.errors import ExchangeError
12
13
  from ccxt.base.errors import ArgumentsRequired
13
14
  from ccxt.base.errors import BadRequest
15
+ from ccxt.base.errors import InsufficientFunds
14
16
  from ccxt.base.errors import InvalidOrder
15
17
  from ccxt.base.errors import OrderNotFound
16
18
  from ccxt.base.errors import NotSupported
@@ -23,14 +25,14 @@ from ccxt.base.precise import Precise
23
25
 
24
26
  class hyperliquid(Exchange, ImplicitAPI):
25
27
 
26
- def describe(self):
28
+ def describe(self) -> Any:
27
29
  return self.deep_extend(super(hyperliquid, self).describe(), {
28
30
  'id': 'hyperliquid',
29
31
  'name': 'Hyperliquid',
30
32
  'countries': [],
31
33
  'version': 'v1',
32
34
  'rateLimit': 50, # 1200 requests per minute, 20 request per second
33
- 'certified': False,
35
+ 'certified': True,
34
36
  'pro': True,
35
37
  'dex': True,
36
38
  'has': {
@@ -55,31 +57,36 @@ class hyperliquid(Exchange, ImplicitAPI):
55
57
  'createMarketSellOrderWithCost': False,
56
58
  'createOrder': True,
57
59
  'createOrders': True,
60
+ 'createOrderWithTakeProfitAndStopLoss': True,
58
61
  'createReduceOnlyOrder': True,
62
+ 'createStopOrder': True,
63
+ 'createTriggerOrder': True,
59
64
  'editOrder': True,
65
+ 'editOrders': True,
60
66
  'fetchAccounts': False,
61
67
  'fetchBalance': True,
62
68
  'fetchBorrowInterest': False,
63
69
  'fetchBorrowRateHistories': False,
64
70
  'fetchBorrowRateHistory': False,
65
- 'fetchCanceledOrders': False,
71
+ 'fetchCanceledAndClosedOrders': True,
72
+ 'fetchCanceledOrders': True,
66
73
  'fetchClosedOrders': True,
67
74
  'fetchCrossBorrowRate': False,
68
75
  'fetchCrossBorrowRates': False,
69
76
  'fetchCurrencies': True,
70
77
  'fetchDepositAddress': False,
71
78
  'fetchDepositAddresses': False,
72
- 'fetchDeposits': False,
79
+ 'fetchDeposits': True,
73
80
  'fetchDepositWithdrawFee': 'emulated',
74
81
  'fetchDepositWithdrawFees': False,
75
- 'fetchFundingHistory': False,
82
+ 'fetchFundingHistory': True,
76
83
  'fetchFundingRate': False,
77
84
  'fetchFundingRateHistory': True,
78
- 'fetchFundingRates': False,
85
+ 'fetchFundingRates': True,
79
86
  'fetchIndexOHLCV': False,
80
87
  'fetchIsolatedBorrowRate': False,
81
88
  'fetchIsolatedBorrowRates': False,
82
- 'fetchLedger': False,
89
+ 'fetchLedger': True,
83
90
  'fetchLeverage': False,
84
91
  'fetchLeverageTiers': False,
85
92
  'fetchLiquidations': False,
@@ -90,28 +97,29 @@ class hyperliquid(Exchange, ImplicitAPI):
90
97
  'fetchMyLiquidations': False,
91
98
  'fetchMyTrades': True,
92
99
  'fetchOHLCV': True,
93
- 'fetchOpenInterest': False,
100
+ 'fetchOpenInterest': True,
94
101
  'fetchOpenInterestHistory': False,
102
+ 'fetchOpenInterests': True,
95
103
  'fetchOpenOrders': True,
96
104
  'fetchOrder': True,
97
105
  'fetchOrderBook': True,
98
- 'fetchOrders': False,
106
+ 'fetchOrders': True,
99
107
  'fetchOrderTrades': False,
100
108
  'fetchPosition': True,
101
109
  'fetchPositionMode': False,
102
110
  'fetchPositions': True,
103
111
  'fetchPositionsRisk': False,
104
112
  'fetchPremiumIndexOHLCV': False,
105
- 'fetchTicker': False,
106
- 'fetchTickers': False,
113
+ 'fetchTicker': 'emulated',
114
+ 'fetchTickers': True,
107
115
  'fetchTime': False,
108
116
  'fetchTrades': True,
109
- 'fetchTradingFee': False,
117
+ 'fetchTradingFee': True,
110
118
  'fetchTradingFees': False,
111
119
  'fetchTransfer': False,
112
120
  'fetchTransfers': False,
113
121
  'fetchWithdrawal': False,
114
- 'fetchWithdrawals': False,
122
+ 'fetchWithdrawals': True,
115
123
  'reduceMargin': True,
116
124
  'repayCrossMargin': False,
117
125
  'repayIsolatedMargin': False,
@@ -131,12 +139,12 @@ class hyperliquid(Exchange, ImplicitAPI):
131
139
  '1h': '1h',
132
140
  '2h': '2h',
133
141
  '4h': '4h',
134
- '6h': '6h',
142
+ '8h': '8h',
135
143
  '12h': '12h',
136
144
  '1d': '1d',
137
145
  '3d': '3d',
138
146
  '1w': '1w',
139
- '1M': '1m',
147
+ '1M': '1M',
140
148
  },
141
149
  'hostname': 'hyperliquid.xyz',
142
150
  'urls': {
@@ -157,7 +165,18 @@ class hyperliquid(Exchange, ImplicitAPI):
157
165
  'api': {
158
166
  'public': {
159
167
  'post': {
160
- 'info': 1,
168
+ 'info': {
169
+ 'cost': 20,
170
+ 'byType': {
171
+ 'l2Book': 2,
172
+ 'allMids': 2,
173
+ 'clearinghouseState': 2,
174
+ 'orderStatus': 2,
175
+ 'spotClearinghouseState': 2,
176
+ 'exchangeStatus': 2,
177
+ 'candleSnapshot': 4,
178
+ },
179
+ },
161
180
  },
162
181
  },
163
182
  'private': {
@@ -168,12 +187,12 @@ class hyperliquid(Exchange, ImplicitAPI):
168
187
  },
169
188
  'fees': {
170
189
  'swap': {
171
- 'taker': self.parse_number('0.00035'),
172
- 'maker': self.parse_number('0.0001'),
190
+ 'taker': self.parse_number('0.00045'),
191
+ 'maker': self.parse_number('0.00015'),
173
192
  },
174
193
  'spot': {
175
- 'taker': self.parse_number('0.00035'),
176
- 'maker': self.parse_number('0.0001'),
194
+ 'taker': self.parse_number('0.0007'),
195
+ 'maker': self.parse_number('0.0004'),
177
196
  },
178
197
  },
179
198
  'requiredCredentials': {
@@ -188,7 +207,7 @@ class hyperliquid(Exchange, ImplicitAPI):
188
207
  'broad': {
189
208
  'Price must be divisible by tick size.': InvalidOrder,
190
209
  'Order must have minimum value of $10': InvalidOrder,
191
- 'Insufficient margin to place order.': InvalidOrder,
210
+ 'Insufficient margin to place order.': InsufficientFunds,
192
211
  'Reduce only order would increase position.': InvalidOrder,
193
212
  'Post only order would have immediately matched,': InvalidOrder,
194
213
  'Order could not immediately match against any resting orders.': InvalidOrder,
@@ -196,6 +215,12 @@ class hyperliquid(Exchange, ImplicitAPI):
196
215
  'No liquidity available for market order.': InvalidOrder,
197
216
  'Order was never placed, already canceled, or filled.': OrderNotFound,
198
217
  'User or API Wallet ': InvalidOrder,
218
+ 'Order has invalid size': InvalidOrder,
219
+ 'Order price cannot be more than 80% away from the reference price': InvalidOrder,
220
+ 'Order has zero size.': InvalidOrder,
221
+ 'Insufficient spot balance asset': InsufficientFunds,
222
+ 'Insufficient balance for withdrawal': InsufficientFunds,
223
+ 'Insufficient balance for token transfer': InsufficientFunds,
199
224
  },
200
225
  },
201
226
  'precisionMode': TICK_SIZE,
@@ -207,6 +232,114 @@ class hyperliquid(Exchange, ImplicitAPI):
207
232
  'defaultSlippage': 0.05,
208
233
  'zeroAddress': '0x0000000000000000000000000000000000000000',
209
234
  },
235
+ 'features': {
236
+ 'default': {
237
+ 'sandbox': True,
238
+ 'createOrder': {
239
+ 'marginMode': False,
240
+ 'triggerPrice': False,
241
+ 'triggerPriceType': None,
242
+ 'triggerDirection': False,
243
+ 'stopLossPrice': False,
244
+ 'takeProfitPrice': False,
245
+ 'attachedStopLossTakeProfit': {
246
+ 'triggerPriceType': {
247
+ 'last': False,
248
+ 'mark': False,
249
+ 'index': False,
250
+ },
251
+ 'triggerPrice': True,
252
+ 'type': True,
253
+ 'price': True,
254
+ },
255
+ 'timeInForce': {
256
+ 'IOC': True,
257
+ 'FOK': False,
258
+ 'PO': True,
259
+ 'GTD': False,
260
+ },
261
+ 'hedged': False,
262
+ 'trailing': False,
263
+ 'leverage': False,
264
+ 'marketBuyByCost': False,
265
+ 'marketBuyRequiresPrice': False,
266
+ 'selfTradePrevention': False,
267
+ 'iceberg': False,
268
+ },
269
+ 'createOrders': {
270
+ 'max': 1000,
271
+ },
272
+ 'fetchMyTrades': {
273
+ 'marginMode': False,
274
+ 'limit': 2000,
275
+ 'daysBack': None,
276
+ 'untilDays': None,
277
+ 'symbolRequired': True,
278
+ },
279
+ 'fetchOrder': {
280
+ 'marginMode': False,
281
+ 'trigger': False,
282
+ 'trailing': False,
283
+ 'symbolRequired': True,
284
+ },
285
+ 'fetchOpenOrders': {
286
+ 'marginMode': False,
287
+ 'limit': 2000,
288
+ 'trigger': False,
289
+ 'trailing': False,
290
+ 'symbolRequired': True,
291
+ },
292
+ 'fetchOrders': {
293
+ 'marginMode': False,
294
+ 'limit': 2000,
295
+ 'daysBack': None,
296
+ 'untilDays': None,
297
+ 'trigger': False,
298
+ 'trailing': False,
299
+ 'symbolRequired': True,
300
+ },
301
+ 'fetchClosedOrders': {
302
+ 'marginMode': False,
303
+ 'limit': 2000,
304
+ 'daysBack': None,
305
+ 'daysBackCanceled': None,
306
+ 'untilDays': None,
307
+ 'trigger': False,
308
+ 'trailing': False,
309
+ 'symbolRequired': True,
310
+ },
311
+ 'fetchOHLCV': {
312
+ 'limit': 5000,
313
+ },
314
+ },
315
+ 'spot': {
316
+ 'extends': 'default',
317
+ },
318
+ 'forPerps': {
319
+ 'extends': 'default',
320
+ 'createOrder': {
321
+ 'stopLossPrice': True,
322
+ 'takeProfitPrice': True,
323
+ 'attachedStopLossTakeProfit': None, # todo, in two orders
324
+ },
325
+ },
326
+ 'swap': {
327
+ 'linear': {
328
+ 'extends': 'forPerps',
329
+ },
330
+ 'inverse': {
331
+ 'extends': 'forPerps',
332
+ },
333
+ },
334
+ 'future': {
335
+ 'linear': {
336
+ 'extends': 'forPerps',
337
+ },
338
+ 'inverse': {
339
+ 'extends': 'forPerps',
340
+ },
341
+ },
342
+ },
210
343
  })
211
344
 
212
345
  def set_sandbox_mode(self, enabled):
@@ -216,10 +349,14 @@ class hyperliquid(Exchange, ImplicitAPI):
216
349
  async def fetch_currencies(self, params={}) -> Currencies:
217
350
  """
218
351
  fetches all available currencies on an exchange
219
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-exchange-metadata
352
+
353
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-metadata
354
+
220
355
  :param dict [params]: extra parameters specific to the exchange API endpoint
221
356
  :returns dict: an associative dictionary of currencies
222
357
  """
358
+ if self.check_required_credentials(False):
359
+ await self.handle_builder_fee_approval()
223
360
  request: dict = {
224
361
  'type': 'meta',
225
362
  }
@@ -245,7 +382,7 @@ class hyperliquid(Exchange, ImplicitAPI):
245
382
  id = i
246
383
  name = self.safe_string(data, 'name')
247
384
  code = self.safe_currency_code(name)
248
- result[code] = {
385
+ result[code] = self.safe_currency_structure({
249
386
  'id': id,
250
387
  'name': name,
251
388
  'code': code,
@@ -256,15 +393,27 @@ class hyperliquid(Exchange, ImplicitAPI):
256
393
  'withdraw': None,
257
394
  'networks': None,
258
395
  'fee': None,
259
- # 'fees': fees,
260
- 'limits': None,
261
- }
396
+ 'type': 'crypto',
397
+ 'limits': {
398
+ 'amount': {
399
+ 'min': None,
400
+ 'max': None,
401
+ },
402
+ 'withdraw': {
403
+ 'min': None,
404
+ 'max': None,
405
+ },
406
+ },
407
+ })
262
408
  return result
263
409
 
264
410
  async def fetch_markets(self, params={}) -> List[Market]:
265
411
  """
266
412
  retrieves data on all markets for hyperliquid
267
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-asset-contexts-includes-mark-price-current-funding-open-interest-etc
413
+
414
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-asset-contexts-includes-mark-price-current-funding-open-interest-etc
415
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-spot-asset-contexts
416
+
268
417
  :param dict [params]: extra parameters specific to the exchange API endpoint
269
418
  :returns dict[]: an array of objects representing market data
270
419
  """
@@ -280,7 +429,9 @@ class hyperliquid(Exchange, ImplicitAPI):
280
429
  async def fetch_swap_markets(self, params={}) -> List[Market]:
281
430
  """
282
431
  retrieves data on all swap markets for hyperliquid
283
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-asset-contexts-includes-mark-price-current-funding-open-interest-etc
432
+
433
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-asset-contexts-includes-mark-price-current-funding-open-interest-etc
434
+
284
435
  :param dict [params]: extra parameters specific to the exchange API endpoint
285
436
  :returns dict[]: an array of objects representing market data
286
437
  """
@@ -320,22 +471,66 @@ class hyperliquid(Exchange, ImplicitAPI):
320
471
  #
321
472
  #
322
473
  meta = self.safe_dict(response, 0, {})
323
- meta = self.safe_list(meta, 'universe', [])
324
- assetCtxs = self.safe_dict(response, 1, {})
474
+ universe = self.safe_list(meta, 'universe', [])
475
+ assetCtxs = self.safe_list(response, 1, [])
325
476
  result = []
326
- for i in range(0, len(meta)):
477
+ for i in range(0, len(universe)):
327
478
  data = self.extend(
328
- self.safe_dict(meta, i, {}),
479
+ self.safe_dict(universe, i, {}),
329
480
  self.safe_dict(assetCtxs, i, {})
330
481
  )
331
482
  data['baseId'] = i
332
483
  result.append(data)
333
484
  return self.parse_markets(result)
334
485
 
486
+ def calculate_price_precision(self, price: float, amountPrecision: float, maxDecimals: float):
487
+ """
488
+ Helper function to calculate the Hyperliquid DECIMAL_PLACES price precision
489
+ :param float price: the price to use in the calculation
490
+ :param int amountPrecision: the amountPrecision to use in the calculation
491
+ :param int maxDecimals: the maxDecimals to use in the calculation
492
+ :returns int: The calculated price precision
493
+ """
494
+ pricePrecision = 0
495
+ priceStr = self.number_to_string(price)
496
+ if priceStr is None:
497
+ return 0
498
+ priceSplitted = priceStr.split('.')
499
+ if Precise.string_eq(priceStr, '0'):
500
+ # Significant digits is always hasattr(self, 5) case
501
+ significantDigits = 5
502
+ # Integer digits is always hasattr(self, 0) case(0 doesn't count)
503
+ integerDigits = 0
504
+ # Calculate the price precision
505
+ pricePrecision = min(maxDecimals - amountPrecision, significantDigits - integerDigits)
506
+ elif Precise.string_gt(priceStr, '0') and Precise.string_lt(priceStr, '1'):
507
+ # Significant digits, always hasattr(self, 5) case
508
+ significantDigits = 5
509
+ # Get the part after the decimal separator
510
+ decimalPart = self.safe_string(priceSplitted, 1, '')
511
+ # Count the number of leading zeros in the decimal part
512
+ leadingZeros = 0
513
+ while((leadingZeros <= len(decimalPart)) and (decimalPart[leadingZeros] == '0')):
514
+ leadingZeros = leadingZeros + 1
515
+ # Calculate price precision based on leading zeros and significant digits
516
+ pricePrecision = leadingZeros + significantDigits
517
+ # Calculate the price precision based on maxDecimals - szDecimals and the calculated price precision from the previous step
518
+ pricePrecision = min(maxDecimals - amountPrecision, pricePrecision)
519
+ else:
520
+ # Count the numbers before the decimal separator
521
+ integerPart = self.safe_string(priceSplitted, 0, '')
522
+ # Get significant digits, take the max() of 5 and the integer digits count
523
+ significantDigits = max(5, len(integerPart))
524
+ # Calculate price precision based on maxDecimals - szDecimals and significantDigits - len(integerPart)
525
+ pricePrecision = min(maxDecimals - amountPrecision, significantDigits - len(integerPart))
526
+ return self.parse_to_int(pricePrecision)
527
+
335
528
  async def fetch_spot_markets(self, params={}) -> List[Market]:
336
529
  """
337
530
  retrieves data on all spot markets for hyperliquid
338
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-asset-contexts-includes-mark-price-current-funding-open-interest-etc
531
+
532
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-spot-asset-contexts
533
+
339
534
  :param dict [params]: extra parameters specific to the exchange API endpoint
340
535
  :returns dict[]: an array of objects representing market data
341
536
  """
@@ -346,127 +541,88 @@ class hyperliquid(Exchange, ImplicitAPI):
346
541
  #
347
542
  # [
348
543
  # {
349
- # 'tokens': [
544
+ # "tokens": [
350
545
  # {
351
- # 'name': 'USDC',
352
- # 'szDecimals': '8',
353
- # 'weiDecimals': '8',
546
+ # "name": "USDC",
547
+ # "szDecimals": 8,
548
+ # "weiDecimals" 8,
549
+ # "index": 0,
550
+ # "tokenId": "0x6d1e7cde53ba9467b783cb7c530ce054",
551
+ # "isCanonical": True,
552
+ # "evmContract":null,
553
+ # "fullName":null
354
554
  # },
355
555
  # {
356
- # 'name': 'PURR',
357
- # 'szDecimals': '0',
358
- # 'weiDecimals': '5',
359
- # },
556
+ # "name": "PURR",
557
+ # "szDecimals": 0,
558
+ # "weiDecimals": 5,
559
+ # "index": 1,
560
+ # "tokenId": "0xc1fb593aeffbeb02f85e0308e9956a90",
561
+ # "isCanonical": True,
562
+ # "evmContract":null,
563
+ # "fullName":null
564
+ # }
360
565
  # ],
361
- # 'universe': [
566
+ # "universe": [
362
567
  # {
363
- # 'name': 'PURR/USDC',
364
- # 'tokens': [
365
- # 1,
366
- # 0,
367
- # ],
368
- # },
369
- # ],
568
+ # "name": "PURR/USDC",
569
+ # "tokens": [1, 0],
570
+ # "index": 0,
571
+ # "isCanonical": True
572
+ # }
573
+ # ]
370
574
  # },
371
575
  # [
372
576
  # {
373
- # 'dayNtlVlm': '264250385.14640012',
374
- # 'markPx': '0.018314',
375
- # 'midPx': '0.0182235',
376
- # 'prevDayPx': '0.017427',
377
- # },
378
- # ],
379
- # ]
380
- # mainnet
381
- # [
382
- # {
383
- # "canonical_tokens2":[
384
- # 0,
385
- # 1
386
- # ],
387
- # "spot_infos":[
388
- # {
389
- # "name":"PURR/USDC",
390
- # "tokens":[
391
- # 1,
392
- # 0
393
- # ]
394
- # }
395
- # ],
396
- # "token_id_to_token":[
397
- # [
398
- # "0x6d1e7cde53ba9467b783cb7c530ce054",
399
- # 0
400
- # ],
401
- # [
402
- # "0xc1fb593aeffbeb02f85e0308e9956a90",
403
- # 1
404
- # ]
405
- # ],
406
- # "token_infos":[
407
- # {
408
- # "deployer":null,
409
- # "spec":{
410
- # "name":"USDC",
411
- # "szDecimals":"8",
412
- # "weiDecimals":"8"
413
- # },
414
- # "spots":[
415
- # ]
416
- # },
417
- # {
418
- # "deployer":null,
419
- # "spec":{
420
- # "name":"PURR",
421
- # "szDecimals":"0",
422
- # "weiDecimals":"5"
423
- # },
424
- # "spots":[
425
- # 0
426
- # ]
427
- # }
428
- # ]
429
- # },
430
- # [
431
- # {
432
- # "dayNtlVlm":"35001170.16631",
433
- # "markPx":"0.15743",
434
- # "midPx":"0.157555",
435
- # "prevDayPx":"0.158"
436
- # }
577
+ # "dayNtlVlm":"8906.0",
578
+ # "markPx":"0.14",
579
+ # "midPx":"0.209265",
580
+ # "prevDayPx":"0.20432"
581
+ # }
437
582
  # ]
438
583
  # ]
439
584
  #
440
- # response differs depending on the environment(mainnet vs sandbox)
441
585
  first = self.safe_dict(response, 0, {})
442
- meta = self.safe_list_2(first, 'universe', 'spot_infos', [])
443
- tokens = self.safe_list_2(first, 'tokens', 'token_infos', [])
586
+ second = self.safe_list(response, 1, [])
587
+ meta = self.safe_list(first, 'universe', [])
588
+ tokens = self.safe_list(first, 'tokens', [])
444
589
  markets = []
445
590
  for i in range(0, len(meta)):
446
591
  market = self.safe_dict(meta, i, {})
592
+ index = self.safe_integer(market, 'index')
593
+ extraData = self.safe_dict(second, index, {})
447
594
  marketName = self.safe_string(market, 'name')
448
- if marketName.find('/') < 0:
449
- # there are some weird spot markets in testnet, eg @2
450
- continue
451
- marketParts = marketName.split('/')
452
- baseName = self.safe_string(marketParts, 0)
453
- quoteId = self.safe_string(marketParts, 1)
454
- base = self.safe_currency_code(baseName)
455
- quote = self.safe_currency_code(quoteId)
456
- symbol = base + '/' + quote
595
+ # if marketName.find('/') < 0:
596
+ # # there are some weird spot markets in testnet, eg @2
597
+ # continue
598
+ # }
599
+ # marketParts = marketName.split('/')
600
+ # baseName = self.safe_string(marketParts, 0)
601
+ # quoteId = self.safe_string(marketParts, 1)
457
602
  fees = self.safe_dict(self.fees, 'spot', {})
458
603
  taker = self.safe_number(fees, 'taker')
459
604
  maker = self.safe_number(fees, 'maker')
460
605
  tokensPos = self.safe_list(market, 'tokens', [])
461
606
  baseTokenPos = self.safe_integer(tokensPos, 0)
462
- # quoteTokenPos = self.safe_integer(tokensPos, 1)
607
+ quoteTokenPos = self.safe_integer(tokensPos, 1)
463
608
  baseTokenInfo = self.safe_dict(tokens, baseTokenPos, {})
464
- # quoteTokenInfo = self.safe_dict(tokens, quoteTokenPos, {})
609
+ quoteTokenInfo = self.safe_dict(tokens, quoteTokenPos, {})
610
+ baseName = self.safe_string(baseTokenInfo, 'name')
611
+ quoteId = self.safe_string(quoteTokenInfo, 'name')
612
+ base = self.safe_currency_code(baseName)
613
+ quote = self.safe_currency_code(quoteId)
614
+ symbol = base + '/' + quote
465
615
  innerBaseTokenInfo = self.safe_dict(baseTokenInfo, 'spec', baseTokenInfo)
466
616
  # innerQuoteTokenInfo = self.safe_dict(quoteTokenInfo, 'spec', quoteTokenInfo)
467
- amountPrecision = self.parse_number(self.parse_precision(self.safe_string(innerBaseTokenInfo, 'szDecimals')))
617
+ amountPrecisionStr = self.safe_string(innerBaseTokenInfo, 'szDecimals')
618
+ amountPrecision = int(amountPrecisionStr)
619
+ price = self.safe_number(extraData, 'midPx')
620
+ pricePrecision = 0
621
+ if price is not None:
622
+ pricePrecision = self.calculate_price_precision(price, amountPrecision, 8)
623
+ pricePrecisionStr = self.number_to_string(pricePrecision)
468
624
  # quotePrecision = self.parse_number(self.parse_precision(self.safe_string(innerQuoteTokenInfo, 'szDecimals')))
469
- baseId = self.number_to_string(i + 10000)
625
+ baseId = self.number_to_string(index + 10000)
470
626
  markets.append(self.safe_market_structure({
471
627
  'id': marketName,
472
628
  'symbol': symbol,
@@ -474,6 +630,7 @@ class hyperliquid(Exchange, ImplicitAPI):
474
630
  'quote': quote,
475
631
  'settle': None,
476
632
  'baseId': baseId,
633
+ 'baseName': baseName,
477
634
  'quoteId': quoteId,
478
635
  'settleId': None,
479
636
  'type': 'spot',
@@ -495,8 +652,8 @@ class hyperliquid(Exchange, ImplicitAPI):
495
652
  'strike': None,
496
653
  'optionType': None,
497
654
  'precision': {
498
- 'amount': amountPrecision, # decimal places
499
- 'price': 5, # significant digits
655
+ 'amount': self.parse_number(self.parse_precision(amountPrecisionStr)),
656
+ 'price': self.parse_number(self.parse_precision(pricePrecisionStr)),
500
657
  },
501
658
  'limits': {
502
659
  'leverage': {
@@ -512,12 +669,12 @@ class hyperliquid(Exchange, ImplicitAPI):
512
669
  'max': None,
513
670
  },
514
671
  'cost': {
515
- 'min': None,
672
+ 'min': self.parse_number('10'),
516
673
  'max': None,
517
674
  },
518
675
  },
519
676
  'created': None,
520
- 'info': market,
677
+ 'info': self.extend(extraData, market),
521
678
  }))
522
679
  return markets
523
680
 
@@ -543,7 +700,8 @@ class hyperliquid(Exchange, ImplicitAPI):
543
700
  # }
544
701
  #
545
702
  quoteId = 'USDC'
546
- base = self.safe_string(market, 'name')
703
+ baseName = self.safe_string(market, 'name')
704
+ base = self.safe_currency_code(baseName)
547
705
  quote = self.safe_currency_code(quoteId)
548
706
  baseId = self.safe_string(market, 'baseId')
549
707
  settleId = 'USDC'
@@ -557,13 +715,25 @@ class hyperliquid(Exchange, ImplicitAPI):
557
715
  fees = self.safe_dict(self.fees, 'swap', {})
558
716
  taker = self.safe_number(fees, 'taker')
559
717
  maker = self.safe_number(fees, 'maker')
560
- return {
718
+ amountPrecisionStr = self.safe_string(market, 'szDecimals')
719
+ amountPrecision = int(amountPrecisionStr)
720
+ price = self.safe_number(market, 'markPx', 0)
721
+ pricePrecision = 0
722
+ if price is not None:
723
+ pricePrecision = self.calculate_price_precision(price, amountPrecision, 6)
724
+ pricePrecisionStr = self.number_to_string(pricePrecision)
725
+ isDelisted = self.safe_bool(market, 'isDelisted')
726
+ active = True
727
+ if isDelisted is not None:
728
+ active = not isDelisted
729
+ return self.safe_market_structure({
561
730
  'id': baseId,
562
731
  'symbol': symbol,
563
732
  'base': base,
564
733
  'quote': quote,
565
734
  'settle': settle,
566
735
  'baseId': baseId,
736
+ 'baseName': baseName,
567
737
  'quoteId': quoteId,
568
738
  'settleId': settleId,
569
739
  'type': 'swap',
@@ -572,7 +742,7 @@ class hyperliquid(Exchange, ImplicitAPI):
572
742
  'swap': swap,
573
743
  'future': False,
574
744
  'option': False,
575
- 'active': True,
745
+ 'active': active,
576
746
  'contract': contract,
577
747
  'linear': True,
578
748
  'inverse': False,
@@ -584,13 +754,13 @@ class hyperliquid(Exchange, ImplicitAPI):
584
754
  'strike': None,
585
755
  'optionType': None,
586
756
  'precision': {
587
- 'amount': self.parse_number(self.parse_precision(self.safe_string(market, 'szDecimals'))), # decimal places
588
- 'price': 5, # significant digits
757
+ 'amount': self.parse_number(self.parse_precision(amountPrecisionStr)),
758
+ 'price': self.parse_number(self.parse_precision(pricePrecisionStr)),
589
759
  },
590
760
  'limits': {
591
761
  'leverage': {
592
762
  'min': None,
593
- 'max': None,
763
+ 'max': self.safe_integer(market, 'maxLeverage'),
594
764
  },
595
765
  'amount': {
596
766
  'min': None,
@@ -601,31 +771,37 @@ class hyperliquid(Exchange, ImplicitAPI):
601
771
  'max': None,
602
772
  },
603
773
  'cost': {
604
- 'min': None,
774
+ 'min': self.parse_number('10'),
605
775
  'max': None,
606
776
  },
607
777
  },
608
778
  'created': None,
609
779
  'info': market,
610
- }
780
+ })
611
781
 
612
782
  async def fetch_balance(self, params={}) -> Balances:
613
783
  """
614
784
  query for balance and get the amount of funds available for trading or funds locked in orders
615
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-state
785
+
786
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-a-users-token-balances
787
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-users-perpetuals-account-summary
788
+
616
789
  :param dict [params]: extra parameters specific to the exchange API endpoint
617
790
  :param str [params.user]: user address, will default to self.walletAddress if not provided
618
791
  :param str [params.type]: wallet type, ['spot', 'swap'], defaults to swap
792
+ :param str [params.marginMode]: 'cross' or 'isolated', for margin trading, uses self.options.defaultMarginMode if not passed, defaults to None/None/None
793
+ :param str [params.subAccountAddress]: sub account user address
619
794
  :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
620
795
  """
621
796
  userAddress = None
622
797
  userAddress, params = self.handle_public_address('fetchBalance', params)
623
798
  type = None
624
799
  type, params = self.handle_market_type_and_params('fetchBalance', None, params)
800
+ marginMode = None
801
+ marginMode, params = self.handle_margin_mode_and_params('fetchBalance', params)
625
802
  isSpot = (type == 'spot')
626
- reqType = 'spotClearinghouseState' if (isSpot) else 'clearinghouseState'
627
803
  request: dict = {
628
- 'type': reqType,
804
+ 'type': 'spotClearinghouseState' if (isSpot) else 'clearinghouseState',
629
805
  'user': userAddress,
630
806
  }
631
807
  response = await self.publicPostInfo(self.extend(request, params))
@@ -672,18 +848,22 @@ class hyperliquid(Exchange, ImplicitAPI):
672
848
  code = self.safe_currency_code(self.safe_string(balance, 'coin'))
673
849
  account = self.account()
674
850
  total = self.safe_string(balance, 'total')
675
- free = self.safe_string(balance, 'hold')
851
+ used = self.safe_string(balance, 'hold')
676
852
  account['total'] = total
677
- account['free'] = free
853
+ account['used'] = used
678
854
  spotBalances[code] = account
679
855
  return self.safe_balance(spotBalances)
680
856
  data = self.safe_dict(response, 'marginSummary', {})
857
+ usdcBalance = {
858
+ 'total': self.safe_number(data, 'accountValue'),
859
+ }
860
+ if (marginMode is not None) and (marginMode == 'isolated'):
861
+ usdcBalance['free'] = self.safe_number(response, 'withdrawable')
862
+ else:
863
+ usdcBalance['used'] = self.safe_number(data, 'totalMarginUsed')
681
864
  result: dict = {
682
865
  'info': response,
683
- 'USDC': {
684
- 'total': self.safe_float(data, 'accountValue'),
685
- 'used': self.safe_float(data, 'totalMarginUsed'),
686
- },
866
+ 'USDC': usdcBalance,
687
867
  }
688
868
  timestamp = self.safe_integer(response, 'time')
689
869
  result['timestamp'] = timestamp
@@ -693,7 +873,9 @@ class hyperliquid(Exchange, ImplicitAPI):
693
873
  async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
694
874
  """
695
875
  fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
696
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#info
876
+
877
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#l2-book-snapshot
878
+
697
879
  :param str symbol: unified symbol of the market to fetch the order book for
698
880
  :param int [limit]: the maximum amount of order book entries to return
699
881
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -703,7 +885,7 @@ class hyperliquid(Exchange, ImplicitAPI):
703
885
  market = self.market(symbol)
704
886
  request: dict = {
705
887
  'type': 'l2Book',
706
- 'coin': market['base'] if market['swap'] else market['id'],
888
+ 'coin': market['baseName'] if market['swap'] else market['id'],
707
889
  }
708
890
  response = await self.publicPostInfo(self.extend(request, params))
709
891
  #
@@ -736,10 +918,181 @@ class hyperliquid(Exchange, ImplicitAPI):
736
918
  timestamp = self.safe_integer(response, 'time')
737
919
  return self.parse_order_book(result, market['symbol'], timestamp, 'bids', 'asks', 'px', 'sz')
738
920
 
921
+ async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
922
+ """
923
+ fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
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
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-spot-asset-contexts
927
+
928
+ :param str[] [symbols]: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
929
+ :param dict [params]: extra parameters specific to the exchange API endpoint
930
+ :param str [params.type]: 'spot' or 'swap', by default fetches both
931
+ :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
932
+ """
933
+ await self.load_markets()
934
+ symbols = self.market_symbols(symbols)
935
+ # at self stage, to get tickers data, we use fetchMarkets endpoints
936
+ response = []
937
+ type = self.safe_string(params, 'type')
938
+ params = self.omit(params, 'type')
939
+ if type == 'spot':
940
+ response = await self.fetch_spot_markets(params)
941
+ elif type == 'swap':
942
+ response = await self.fetch_swap_markets(params)
943
+ else:
944
+ response = await self.fetch_markets(params)
945
+ # same response "fetchMarkets"
946
+ result: dict = {}
947
+ for i in range(0, len(response)):
948
+ market = response[i]
949
+ info = market['info']
950
+ ticker = self.parse_ticker(info, market)
951
+ symbol = self.safe_string(ticker, 'symbol')
952
+ result[symbol] = ticker
953
+ return self.filter_by_array_tickers(result, 'symbol', symbols)
954
+
955
+ async def fetch_funding_rates(self, symbols: Strings = None, params={}) -> FundingRates:
956
+ """
957
+ retrieves data on all swap markets for hyperliquid
958
+
959
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-asset-contexts-includes-mark-price-current-funding-open-interest-etc
960
+
961
+ :param str[] [symbols]: list of unified market symbols
962
+ :param dict [params]: extra parameters specific to the exchange API endpoint
963
+ :returns dict[]: an array of objects representing market data
964
+ """
965
+ request: dict = {
966
+ 'type': 'metaAndAssetCtxs',
967
+ }
968
+ response = await self.publicPostInfo(self.extend(request, params))
969
+ #
970
+ # [
971
+ # {
972
+ # "universe": [
973
+ # {
974
+ # "maxLeverage": 50,
975
+ # "name": "SOL",
976
+ # "onlyIsolated": False,
977
+ # "szDecimals": 2
978
+ # }
979
+ # ]
980
+ # },
981
+ # [
982
+ # {
983
+ # "dayNtlVlm": "9450588.2273",
984
+ # "funding": "0.0000198",
985
+ # "impactPxs": [
986
+ # "108.04",
987
+ # "108.06"
988
+ # ],
989
+ # "markPx": "108.04",
990
+ # "midPx": "108.05",
991
+ # "openInterest": "10764.48",
992
+ # "oraclePx": "107.99",
993
+ # "premium": "0.00055561",
994
+ # "prevDayPx": "111.81"
995
+ # }
996
+ # ]
997
+ # ]
998
+ #
999
+ #
1000
+ meta = self.safe_dict(response, 0, {})
1001
+ universe = self.safe_list(meta, 'universe', [])
1002
+ assetCtxs = self.safe_list(response, 1, [])
1003
+ result = []
1004
+ for i in range(0, len(universe)):
1005
+ data = self.extend(
1006
+ self.safe_dict(universe, i, {}),
1007
+ self.safe_dict(assetCtxs, i, {})
1008
+ )
1009
+ result.append(data)
1010
+ return self.parse_funding_rates(result, symbols)
1011
+
1012
+ def parse_funding_rate(self, info, market: Market = None) -> FundingRate:
1013
+ #
1014
+ # {
1015
+ # "maxLeverage": "50",
1016
+ # "name": "ETH",
1017
+ # "onlyIsolated": False,
1018
+ # "szDecimals": "4",
1019
+ # "dayNtlVlm": "1709813.11535",
1020
+ # "funding": "0.00004807",
1021
+ # "impactPxs": [
1022
+ # "2369.3",
1023
+ # "2369.6"
1024
+ # ],
1025
+ # "markPx": "2369.6",
1026
+ # "midPx": "2369.45",
1027
+ # "openInterest": "1815.4712",
1028
+ # "oraclePx": "2367.3",
1029
+ # "premium": "0.00090821",
1030
+ # "prevDayPx": "2381.5"
1031
+ # }
1032
+ #
1033
+ base = self.safe_string(info, 'name')
1034
+ marketId = self.coin_to_market_id(base)
1035
+ symbol = self.safe_symbol(marketId, market)
1036
+ funding = self.safe_number(info, 'funding')
1037
+ markPx = self.safe_number(info, 'markPx')
1038
+ oraclePx = self.safe_number(info, 'oraclePx')
1039
+ fundingTimestamp = (int(math.floor(self.milliseconds()) / 60 / 60 / 1000) + 1) * 60 * 60 * 1000
1040
+ return {
1041
+ 'info': info,
1042
+ 'symbol': symbol,
1043
+ 'markPrice': markPx,
1044
+ 'indexPrice': oraclePx,
1045
+ 'interestRate': None,
1046
+ 'estimatedSettlePrice': None,
1047
+ 'timestamp': None,
1048
+ 'datetime': None,
1049
+ 'fundingRate': funding,
1050
+ 'fundingTimestamp': fundingTimestamp,
1051
+ 'fundingDatetime': self.iso8601(fundingTimestamp),
1052
+ 'nextFundingRate': None,
1053
+ 'nextFundingTimestamp': None,
1054
+ 'nextFundingDatetime': None,
1055
+ 'previousFundingRate': None,
1056
+ 'previousFundingTimestamp': None,
1057
+ 'previousFundingDatetime': None,
1058
+ 'interval': '1h',
1059
+ }
1060
+
1061
+ def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
1062
+ #
1063
+ # {
1064
+ # "prevDayPx": "3400.5",
1065
+ # "dayNtlVlm": "511297257.47936022",
1066
+ # "markPx": "3464.7",
1067
+ # "midPx": "3465.05",
1068
+ # "oraclePx": "3460.1", # only in swap
1069
+ # "openInterest": "64638.1108", # only in swap
1070
+ # "premium": "0.00141614", # only in swap
1071
+ # "funding": "0.00008727", # only in swap
1072
+ # "impactPxs": ["3465.0", "3465.1"], # only in swap
1073
+ # "coin": "PURR", # only in spot
1074
+ # "circulatingSupply": "998949190.03400207", # only in spot
1075
+ # },
1076
+ #
1077
+ bidAsk = self.safe_list(ticker, 'impactPxs')
1078
+ return self.safe_ticker({
1079
+ 'symbol': market['symbol'],
1080
+ 'timestamp': None,
1081
+ 'datetime': None,
1082
+ 'previousClose': self.safe_number(ticker, 'prevDayPx'),
1083
+ 'close': self.safe_number(ticker, 'midPx'),
1084
+ 'bid': self.safe_number(bidAsk, 0),
1085
+ 'ask': self.safe_number(bidAsk, 1),
1086
+ 'quoteVolume': self.safe_number(ticker, 'dayNtlVlm'),
1087
+ 'info': ticker,
1088
+ }, market)
1089
+
739
1090
  async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
740
1091
  """
741
1092
  fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
742
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#info-1
1093
+
1094
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#candle-snapshot
1095
+
743
1096
  :param str symbol: unified symbol of the market to fetch OHLCV data for
744
1097
  :param str timeframe: the length of time each candle represents, support '1m', '15m', '1h', '1d'
745
1098
  :param int [since]: timestamp in ms of the earliest candle to fetch
@@ -751,16 +1104,22 @@ class hyperliquid(Exchange, ImplicitAPI):
751
1104
  await self.load_markets()
752
1105
  market = self.market(symbol)
753
1106
  until = self.safe_integer(params, 'until', self.milliseconds())
1107
+ useTail = since is None
1108
+ originalSince = since
754
1109
  if since is None:
755
- since = 0
756
- if limit is None:
757
- limit = 500
1110
+ if limit is not None:
1111
+ # optimization if limit is provided
1112
+ timeframeInMilliseconds = self.parse_timeframe(timeframe) * 1000
1113
+ since = self.sum(until, timeframeInMilliseconds * limit * -1)
1114
+ useTail = False
1115
+ else:
1116
+ since = 0
758
1117
  params = self.omit(params, ['until'])
759
1118
  request: dict = {
760
1119
  'type': 'candleSnapshot',
761
1120
  'req': {
762
- 'coin': market['base'] if market['swap'] else market['id'],
763
- 'interval': timeframe,
1121
+ 'coin': market['baseName'] if market['swap'] else market['id'],
1122
+ 'interval': self.safe_string(self.timeframes, timeframe, timeframe),
764
1123
  'startTime': since,
765
1124
  'endTime': until,
766
1125
  },
@@ -782,7 +1141,7 @@ class hyperliquid(Exchange, ImplicitAPI):
782
1141
  # }
783
1142
  # ]
784
1143
  #
785
- return self.parse_ohlcvs(response, market, timeframe, since, limit)
1144
+ return self.parse_ohlcvs(response, market, timeframe, originalSince, limit, useTail)
786
1145
 
787
1146
  def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
788
1147
  #
@@ -808,11 +1167,13 @@ class hyperliquid(Exchange, ImplicitAPI):
808
1167
  self.safe_number(ohlcv, 'v'),
809
1168
  ]
810
1169
 
811
- async def fetch_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1170
+ async def fetch_trades(self, symbol: Str, since: Int = None, limit: Int = None, params={}):
812
1171
  """
813
1172
  get the list of most recent trades for a particular symbol
814
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills
815
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills-by-time
1173
+
1174
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills
1175
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills-by-time
1176
+
816
1177
  :param str symbol: unified market symbol
817
1178
  :param int [since]: the earliest time in ms to fetch trades for
818
1179
  :param int [limit]: the maximum number of trades structures to retrieve
@@ -820,6 +1181,7 @@ class hyperliquid(Exchange, ImplicitAPI):
820
1181
  :param int [params.until]: timestamp in ms of the latest trade
821
1182
  :param str [params.address]: wallet address that made trades
822
1183
  :param str [params.user]: wallet address that made trades
1184
+ :param str [params.subAccountAddress]: sub account user address
823
1185
  :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
824
1186
  """
825
1187
  userAddress = None
@@ -863,15 +1225,17 @@ class hyperliquid(Exchange, ImplicitAPI):
863
1225
 
864
1226
  def amount_to_precision(self, symbol, amount):
865
1227
  market = self.market(symbol)
866
- if market['spot']:
867
- return super(hyperliquid, self).amount_to_precision(symbol, amount)
868
- return self.decimal_to_precision(amount, ROUND, self.markets[symbol]['precision']['amount'], self.precisionMode)
1228
+ return self.decimal_to_precision(amount, ROUND, market['precision']['amount'], self.precisionMode, self.paddingMode)
869
1229
 
870
1230
  def price_to_precision(self, symbol: str, price) -> str:
871
1231
  market = self.market(symbol)
872
- result = self.decimal_to_precision(price, ROUND, market['precision']['price'], SIGNIFICANT_DIGITS, self.paddingMode)
873
- decimalParsedResult = self.decimal_to_precision(result, ROUND, 6, DECIMAL_PLACES, self.paddingMode)
874
- return decimalParsedResult
1232
+ priceStr = self.number_to_string(price)
1233
+ integerPart = priceStr.split('.')[0]
1234
+ significantDigits = max(5, len(integerPart))
1235
+ result = self.decimal_to_precision(price, ROUND, significantDigits, SIGNIFICANT_DIGITS, self.paddingMode)
1236
+ maxDecimals = 8 if market['spot'] else 6
1237
+ subtractedValue = maxDecimals - self.precision_from_string(self.safe_string(market['precision'], 'amount'))
1238
+ return self.decimal_to_precision(result, ROUND, subtractedValue, DECIMAL_PLACES, self.paddingMode)
875
1239
 
876
1240
  def hash_message(self, message):
877
1241
  return '0x' + self.hash(message, 'keccak', 'hex')
@@ -963,7 +1327,7 @@ class hyperliquid(Exchange, ImplicitAPI):
963
1327
  signature = self.sign_message(msg, self.privateKey)
964
1328
  return signature
965
1329
 
966
- def build_transfer_sig(self, message):
1330
+ def build_usd_send_sig(self, message):
967
1331
  messageTypes: dict = {
968
1332
  'HyperliquidTransaction:UsdSend': [
969
1333
  {'name': 'hyperliquidChain', 'type': 'string'},
@@ -974,6 +1338,17 @@ class hyperliquid(Exchange, ImplicitAPI):
974
1338
  }
975
1339
  return self.sign_user_signed_action(messageTypes, message)
976
1340
 
1341
+ def build_usd_class_send_sig(self, message):
1342
+ messageTypes: dict = {
1343
+ 'HyperliquidTransaction:UsdClassTransfer': [
1344
+ {'name': 'hyperliquidChain', 'type': 'string'},
1345
+ {'name': 'amount', 'type': 'string'},
1346
+ {'name': 'toPerp', 'type': 'bool'},
1347
+ {'name': 'nonce', 'type': 'uint64'},
1348
+ ],
1349
+ }
1350
+ return self.sign_user_signed_action(messageTypes, message)
1351
+
977
1352
  def build_withdraw_sig(self, message):
978
1353
  messageTypes: dict = {
979
1354
  'HyperliquidTransaction:Withdraw': [
@@ -985,15 +1360,78 @@ class hyperliquid(Exchange, ImplicitAPI):
985
1360
  }
986
1361
  return self.sign_user_signed_action(messageTypes, message)
987
1362
 
1363
+ def build_approve_builder_fee_sig(self, message):
1364
+ messageTypes: dict = {
1365
+ 'HyperliquidTransaction:ApproveBuilderFee': [
1366
+ {'name': 'hyperliquidChain', 'type': 'string'},
1367
+ {'name': 'maxFeeRate', 'type': 'string'},
1368
+ {'name': 'builder', 'type': 'address'},
1369
+ {'name': 'nonce', 'type': 'uint64'},
1370
+ ],
1371
+ }
1372
+ return self.sign_user_signed_action(messageTypes, message)
1373
+
1374
+ async def approve_builder_fee(self, builder: str, maxFeeRate: str):
1375
+ nonce = self.milliseconds()
1376
+ isSandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
1377
+ payload: dict = {
1378
+ 'hyperliquidChain': 'Testnet' if isSandboxMode else 'Mainnet',
1379
+ 'maxFeeRate': maxFeeRate,
1380
+ 'builder': builder,
1381
+ 'nonce': nonce,
1382
+ }
1383
+ sig = self.build_approve_builder_fee_sig(payload)
1384
+ action = {
1385
+ 'hyperliquidChain': payload['hyperliquidChain'],
1386
+ 'signatureChainId': '0x66eee',
1387
+ 'maxFeeRate': payload['maxFeeRate'],
1388
+ 'builder': payload['builder'],
1389
+ 'nonce': nonce,
1390
+ 'type': 'approveBuilderFee',
1391
+ }
1392
+ request: dict = {
1393
+ 'action': action,
1394
+ 'nonce': nonce,
1395
+ 'signature': sig,
1396
+ 'vaultAddress': None,
1397
+ }
1398
+ #
1399
+ # {
1400
+ # "status": "ok",
1401
+ # "response": {
1402
+ # "type": "default"
1403
+ # }
1404
+ # }
1405
+ #
1406
+ return await self.privatePostExchange(request)
1407
+
1408
+ async def handle_builder_fee_approval(self):
1409
+ buildFee = self.safe_bool(self.options, 'builderFee', True)
1410
+ if not buildFee:
1411
+ return False # skip if builder fee is not enabled
1412
+ approvedBuilderFee = self.safe_bool(self.options, 'approvedBuilderFee', False)
1413
+ if approvedBuilderFee:
1414
+ return True # skip if builder fee is already approved
1415
+ try:
1416
+ builder = self.safe_string(self.options, 'builder', '0x6530512A6c89C7cfCEbC3BA7fcD9aDa5f30827a6')
1417
+ maxFeeRate = self.safe_string(self.options, 'feeRate', '0.01%')
1418
+ await self.approve_builder_fee(builder, maxFeeRate)
1419
+ self.options['approvedBuilderFee'] = True
1420
+ except Exception as e:
1421
+ self.options['builderFee'] = False # disable builder fee if an error occurs
1422
+ return True
1423
+
988
1424
  async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
989
1425
  """
990
1426
  create a trade order
991
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
1427
+
1428
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
1429
+
992
1430
  :param str symbol: unified symbol of the market to create an order in
993
1431
  :param str type: 'market' or 'limit'
994
1432
  :param str side: 'buy' or 'sell'
995
1433
  :param float amount: how much of currency you want to trade in units of base currency
996
- :param float [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
1434
+ :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
997
1435
  :param dict [params]: extra parameters specific to the exchange API endpoint
998
1436
  :param str [params.timeInForce]: 'Gtc', 'Ioc', 'Alo'
999
1437
  :param bool [params.postOnly]: True or False whether the order is post-only
@@ -1002,140 +1440,28 @@ class hyperliquid(Exchange, ImplicitAPI):
1002
1440
  :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
1003
1441
  :param str [params.slippage]: the slippage for market order
1004
1442
  :param str [params.vaultAddress]: the vault address for order
1443
+ :param str [params.subAccountAddress]: sub account user address
1005
1444
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1006
1445
  """
1007
1446
  await self.load_markets()
1008
- market = self.market(symbol)
1009
- vaultAddress = self.safe_string(params, 'vaultAddress')
1010
- params = self.omit(params, 'vaultAddress')
1011
- symbol = market['symbol']
1012
- order = {
1013
- 'symbol': symbol,
1014
- 'type': type,
1015
- 'side': side,
1016
- 'amount': amount,
1017
- 'price': price,
1018
- 'params': params,
1019
- }
1020
- globalParams: dict = {}
1021
- if vaultAddress is not None:
1022
- globalParams['vaultAddress'] = vaultAddress
1023
- response = await self.create_orders([order], globalParams)
1024
- first = self.safe_dict(response, 0)
1025
- return first
1447
+ order, globalParams = self.parse_create_edit_order_args(None, symbol, type, side, amount, price, params)
1448
+ orders = await self.create_orders([order], globalParams)
1449
+ return orders[0]
1026
1450
 
1027
1451
  async def create_orders(self, orders: List[OrderRequest], params={}):
1028
1452
  """
1029
1453
  create a list of trade orders
1030
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
1454
+
1455
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
1456
+
1031
1457
  :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
1458
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1032
1459
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1033
1460
  """
1034
- self.check_required_credentials()
1035
1461
  await self.load_markets()
1036
- defaultSlippage = self.safe_string(self.options, 'defaultSlippage')
1037
- defaultSlippage = self.safe_string(params, 'slippage', defaultSlippage)
1038
- hasClientOrderId = False
1039
- for i in range(0, len(orders)):
1040
- rawOrder = orders[i]
1041
- orderParams = self.safe_dict(rawOrder, 'params', {})
1042
- clientOrderId = self.safe_string_2(orderParams, 'clientOrderId', 'client_id')
1043
- if clientOrderId is not None:
1044
- hasClientOrderId = True
1045
- if hasClientOrderId:
1046
- for i in range(0, len(orders)):
1047
- rawOrder = orders[i]
1048
- orderParams = self.safe_dict(rawOrder, 'params', {})
1049
- clientOrderId = self.safe_string_2(orderParams, 'clientOrderId', 'client_id')
1050
- if clientOrderId is None:
1051
- raise ArgumentsRequired(self.id + ' createOrders() all orders must have clientOrderId if at least one has a clientOrderId')
1052
- params = self.omit(params, ['slippage', 'clientOrderId', 'client_id', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce'])
1053
- nonce = self.milliseconds()
1054
- orderReq = []
1055
- for i in range(0, len(orders)):
1056
- rawOrder = orders[i]
1057
- marketId = self.safe_string(rawOrder, 'symbol')
1058
- market = self.market(marketId)
1059
- symbol = market['symbol']
1060
- type = self.safe_string_upper(rawOrder, 'type')
1061
- isMarket = (type == 'MARKET')
1062
- side = self.safe_string_upper(rawOrder, 'side')
1063
- isBuy = (side == 'BUY')
1064
- amount = self.safe_string(rawOrder, 'amount')
1065
- price = self.safe_string(rawOrder, 'price')
1066
- orderParams = self.safe_dict(rawOrder, 'params', {})
1067
- clientOrderId = self.safe_string_2(orderParams, 'clientOrderId', 'client_id')
1068
- slippage = self.safe_string(orderParams, 'slippage', defaultSlippage)
1069
- defaultTimeInForce = 'ioc' if (isMarket) else 'gtc'
1070
- postOnly = self.safe_bool(orderParams, 'postOnly', False)
1071
- if postOnly:
1072
- defaultTimeInForce = 'alo'
1073
- timeInForce = self.safe_string_lower(orderParams, 'timeInForce', defaultTimeInForce)
1074
- timeInForce = self.capitalize(timeInForce)
1075
- triggerPrice = self.safe_string_2(orderParams, 'triggerPrice', 'stopPrice')
1076
- stopLossPrice = self.safe_string(orderParams, 'stopLossPrice', triggerPrice)
1077
- takeProfitPrice = self.safe_string(orderParams, 'takeProfitPrice')
1078
- isTrigger = (stopLossPrice or takeProfitPrice)
1079
- px = None
1080
- if isMarket:
1081
- if price is None:
1082
- raise ArgumentsRequired(self.id + ' market orders require price to calculate the max slippage price. Default slippage can be set in options(default is 5%).')
1083
- px = Precise.string_mul(price, Precise.string_add('1', slippage)) if (isBuy) else Precise.string_mul(price, Precise.string_sub('1', slippage))
1084
- px = self.price_to_precision(symbol, px) # round after adding slippage
1085
- else:
1086
- px = self.price_to_precision(symbol, price)
1087
- sz = self.amount_to_precision(symbol, amount)
1088
- reduceOnly = self.safe_bool(orderParams, 'reduceOnly', False)
1089
- orderType: dict = {}
1090
- if isTrigger:
1091
- isTp = False
1092
- if takeProfitPrice is not None:
1093
- triggerPrice = self.price_to_precision(symbol, takeProfitPrice)
1094
- isTp = True
1095
- else:
1096
- triggerPrice = self.price_to_precision(symbol, stopLossPrice)
1097
- orderType['trigger'] = {
1098
- 'isMarket': isMarket,
1099
- 'triggerPx': triggerPrice,
1100
- 'tpsl': 'tp' if (isTp) else 'sl',
1101
- }
1102
- else:
1103
- orderType['limit'] = {
1104
- 'tif': timeInForce,
1105
- }
1106
- orderParams = self.omit(orderParams, ['clientOrderId', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce', 'client_id', 'reduceOnly', 'postOnly'])
1107
- orderObj: dict = {
1108
- 'a': self.parse_to_int(market['baseId']),
1109
- 'b': isBuy,
1110
- 'p': px,
1111
- 's': sz,
1112
- 'r': reduceOnly,
1113
- 't': orderType,
1114
- # 'c': clientOrderId,
1115
- }
1116
- if clientOrderId is not None:
1117
- orderObj['c'] = clientOrderId
1118
- orderReq.append(self.extend(orderObj, orderParams))
1119
- vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
1120
- orderAction: dict = {
1121
- 'type': 'order',
1122
- 'orders': orderReq,
1123
- 'grouping': 'na',
1124
- # 'brokerCode': 1, # cant
1125
- }
1126
- if vaultAddress is None:
1127
- orderAction['brokerCode'] = 1
1128
- signature = self.sign_l1_action(orderAction, nonce, vaultAddress)
1129
- request: dict = {
1130
- 'action': orderAction,
1131
- 'nonce': nonce,
1132
- 'signature': signature,
1133
- # 'vaultAddress': vaultAddress,
1134
- }
1135
- if vaultAddress is not None:
1136
- params = self.omit(params, 'vaultAddress')
1137
- request['vaultAddress'] = vaultAddress
1138
- response = await self.privatePostExchange(self.extend(request, params))
1462
+ await self.handle_builder_fee_approval()
1463
+ request = self.create_orders_request(orders, params)
1464
+ response = await self.privatePostExchange(request)
1139
1465
  #
1140
1466
  # {
1141
1467
  # "status": "ok",
@@ -1158,36 +1484,235 @@ class hyperliquid(Exchange, ImplicitAPI):
1158
1484
  statuses = self.safe_list(data, 'statuses', [])
1159
1485
  return self.parse_orders(statuses, None)
1160
1486
 
1487
+ def create_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: str, price: Str = None, params={}):
1488
+ market = self.market(symbol)
1489
+ type = type.upper()
1490
+ side = side.upper()
1491
+ isMarket = (type == 'MARKET')
1492
+ isBuy = (side == 'BUY')
1493
+ clientOrderId = self.safe_string_2(params, 'clientOrderId', 'client_id')
1494
+ slippage = self.safe_string(params, 'slippage')
1495
+ defaultTimeInForce = 'ioc' if (isMarket) else 'gtc'
1496
+ postOnly = self.safe_bool(params, 'postOnly', False)
1497
+ if postOnly:
1498
+ defaultTimeInForce = 'alo'
1499
+ timeInForce = self.safe_string_lower(params, 'timeInForce', defaultTimeInForce)
1500
+ timeInForce = self.capitalize(timeInForce)
1501
+ triggerPrice = self.safe_string_2(params, 'triggerPrice', 'stopPrice')
1502
+ stopLossPrice = self.safe_string(params, 'stopLossPrice', triggerPrice)
1503
+ takeProfitPrice = self.safe_string(params, 'takeProfitPrice')
1504
+ isTrigger = (stopLossPrice or takeProfitPrice)
1505
+ px = None
1506
+ if isMarket:
1507
+ if price is None:
1508
+ raise ArgumentsRequired(self.id + ' market orders require price to calculate the max slippage price. Default slippage can be set in options(default is 5%).')
1509
+ px = Precise.string_mul(price, Precise.string_add('1', slippage)) if (isBuy) else Precise.string_mul(price, Precise.string_sub('1', slippage))
1510
+ px = self.price_to_precision(symbol, px) # round after adding slippage
1511
+ else:
1512
+ px = self.price_to_precision(symbol, price)
1513
+ sz = self.amount_to_precision(symbol, amount)
1514
+ reduceOnly = self.safe_bool(params, 'reduceOnly', False)
1515
+ orderType: dict = {}
1516
+ if isTrigger:
1517
+ isTp = False
1518
+ if takeProfitPrice is not None:
1519
+ triggerPrice = self.price_to_precision(symbol, takeProfitPrice)
1520
+ isTp = True
1521
+ else:
1522
+ triggerPrice = self.price_to_precision(symbol, stopLossPrice)
1523
+ orderType['trigger'] = {
1524
+ 'isMarket': isMarket,
1525
+ 'triggerPx': triggerPrice,
1526
+ 'tpsl': 'tp' if (isTp) else 'sl',
1527
+ }
1528
+ else:
1529
+ orderType['limit'] = {
1530
+ 'tif': timeInForce,
1531
+ }
1532
+ params = self.omit(params, ['clientOrderId', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce', 'client_id', 'reduceOnly', 'postOnly'])
1533
+ orderObj: dict = {
1534
+ 'a': self.parse_to_int(market['baseId']),
1535
+ 'b': isBuy,
1536
+ 'p': px,
1537
+ 's': sz,
1538
+ 'r': reduceOnly,
1539
+ 't': orderType,
1540
+ # 'c': clientOrderId,
1541
+ }
1542
+ if clientOrderId is not None:
1543
+ orderObj['c'] = clientOrderId
1544
+ return orderObj
1545
+
1546
+ def create_orders_request(self, orders, params={}) -> dict:
1547
+ """
1548
+ create a list of trade orders
1549
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
1550
+ :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
1551
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1552
+ """
1553
+ self.check_required_credentials()
1554
+ defaultSlippage = self.safe_string(self.options, 'defaultSlippage')
1555
+ defaultSlippage = self.safe_string(params, 'slippage', defaultSlippage)
1556
+ hasClientOrderId = False
1557
+ for i in range(0, len(orders)):
1558
+ rawOrder = orders[i]
1559
+ orderParams = self.safe_dict(rawOrder, 'params', {})
1560
+ clientOrderId = self.safe_string_2(orderParams, 'clientOrderId', 'client_id')
1561
+ if clientOrderId is not None:
1562
+ hasClientOrderId = True
1563
+ if hasClientOrderId:
1564
+ for i in range(0, len(orders)):
1565
+ rawOrder = orders[i]
1566
+ orderParams = self.safe_dict(rawOrder, 'params', {})
1567
+ clientOrderId = self.safe_string_2(orderParams, 'clientOrderId', 'client_id')
1568
+ if clientOrderId is None:
1569
+ raise ArgumentsRequired(self.id + ' createOrders() all orders must have clientOrderId if at least one has a clientOrderId')
1570
+ params = self.omit(params, ['slippage', 'clientOrderId', 'client_id', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce'])
1571
+ nonce = self.milliseconds()
1572
+ orderReq = []
1573
+ grouping = 'na'
1574
+ for i in range(0, len(orders)):
1575
+ rawOrder = orders[i]
1576
+ marketId = self.safe_string(rawOrder, 'symbol')
1577
+ market = self.market(marketId)
1578
+ symbol = market['symbol']
1579
+ type = self.safe_string_upper(rawOrder, 'type')
1580
+ side = self.safe_string_upper(rawOrder, 'side')
1581
+ amount = self.safe_string(rawOrder, 'amount')
1582
+ price = self.safe_string(rawOrder, 'price')
1583
+ orderParams = self.safe_dict(rawOrder, 'params', {})
1584
+ slippage = self.safe_string(orderParams, 'slippage', defaultSlippage)
1585
+ orderParams['slippage'] = slippage
1586
+ stopLoss = self.safe_value(orderParams, 'stopLoss')
1587
+ takeProfit = self.safe_value(orderParams, 'takeProfit')
1588
+ isTrigger = (stopLoss or takeProfit)
1589
+ orderParams = self.omit(orderParams, ['stopLoss', 'takeProfit'])
1590
+ mainOrderObj: dict = self.create_order_request(symbol, type, side, amount, price, orderParams)
1591
+ orderReq.append(mainOrderObj)
1592
+ if isTrigger:
1593
+ # grouping opposed orders for sl/tp
1594
+ stopLossOrderTriggerPrice = self.safe_string_n(stopLoss, ['triggerPrice', 'stopPrice'])
1595
+ stopLossOrderType = self.safe_string(stopLoss, 'type')
1596
+ stopLossOrderLimitPrice = self.safe_string_n(stopLoss, ['price', 'stopLossPrice'], stopLossOrderTriggerPrice)
1597
+ takeProfitOrderTriggerPrice = self.safe_string_n(takeProfit, ['triggerPrice', 'stopPrice'])
1598
+ takeProfitOrderType = self.safe_string(takeProfit, 'type')
1599
+ takeProfitOrderLimitPrice = self.safe_string_n(takeProfit, ['price', 'takeProfitPrice'], takeProfitOrderTriggerPrice)
1600
+ grouping = 'normalTpsl'
1601
+ orderParams = self.omit(orderParams, ['stopLoss', 'takeProfit'])
1602
+ triggerOrderSide = ''
1603
+ if side == 'BUY':
1604
+ triggerOrderSide = 'sell'
1605
+ else:
1606
+ triggerOrderSide = 'buy'
1607
+ if takeProfit is not None:
1608
+ orderObj: dict = self.create_order_request(symbol, takeProfitOrderType, triggerOrderSide, amount, takeProfitOrderLimitPrice, self.extend(orderParams, {
1609
+ 'takeProfitPrice': takeProfitOrderTriggerPrice,
1610
+ 'reduceOnly': True,
1611
+ }))
1612
+ orderReq.append(orderObj)
1613
+ if stopLoss is not None:
1614
+ orderObj: dict = self.create_order_request(symbol, stopLossOrderType, triggerOrderSide, amount, stopLossOrderLimitPrice, self.extend(orderParams, {
1615
+ 'stopLossPrice': stopLossOrderTriggerPrice,
1616
+ 'reduceOnly': True,
1617
+ }))
1618
+ orderReq.append(orderObj)
1619
+ vaultAddress = None
1620
+ vaultAddress, params = self.handle_option_and_params(params, 'createOrder', 'vaultAddress')
1621
+ vaultAddress = self.format_vault_address(vaultAddress)
1622
+ orderAction: dict = {
1623
+ 'type': 'order',
1624
+ 'orders': orderReq,
1625
+ 'grouping': grouping,
1626
+ }
1627
+ if self.safe_bool(self.options, 'approvedBuilderFee', False):
1628
+ wallet = self.safe_string_lower(self.options, 'builder', '0x6530512A6c89C7cfCEbC3BA7fcD9aDa5f30827a6')
1629
+ orderAction['builder'] = {'b': wallet, 'f': self.safe_integer(self.options, 'feeInt', 10)}
1630
+ signature = self.sign_l1_action(orderAction, nonce, vaultAddress)
1631
+ request: dict = {
1632
+ 'action': orderAction,
1633
+ 'nonce': nonce,
1634
+ 'signature': signature,
1635
+ # 'vaultAddress': vaultAddress,
1636
+ }
1637
+ if vaultAddress is not None:
1638
+ params = self.omit(params, 'vaultAddress')
1639
+ request['vaultAddress'] = vaultAddress
1640
+ return request
1641
+
1161
1642
  async def cancel_order(self, id: str, symbol: Str = None, params={}):
1162
1643
  """
1163
1644
  cancels an open order
1164
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s
1165
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
1645
+
1646
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s
1647
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
1648
+
1166
1649
  :param str id: order id
1167
1650
  :param str symbol: unified symbol of the market the order was made in
1168
1651
  :param dict [params]: extra parameters specific to the exchange API endpoint
1169
1652
  :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
1170
1653
  :param str [params.vaultAddress]: the vault address for order
1654
+ :param str [params.subAccountAddress]: sub account user address
1171
1655
  :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1172
1656
  """
1173
- return await self.cancel_orders([id], symbol, params)
1657
+ orders = await self.cancel_orders([id], symbol, params)
1658
+ return self.safe_dict(orders, 0)
1174
1659
 
1175
1660
  async def cancel_orders(self, ids: List[str], symbol: Str = None, params={}):
1176
1661
  """
1177
1662
  cancel multiple orders
1178
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s
1179
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
1663
+
1664
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s
1665
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
1666
+
1180
1667
  :param str[] ids: order ids
1181
1668
  :param str [symbol]: unified market symbol
1182
1669
  :param dict [params]: extra parameters specific to the exchange API endpoint
1183
1670
  :param string|str[] [params.clientOrderId]: client order ids,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
1184
1671
  :param str [params.vaultAddress]: the vault address
1672
+ :param str [params.subAccountAddress]: sub account user address
1185
1673
  :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1186
1674
  """
1187
1675
  self.check_required_credentials()
1188
1676
  if symbol is None:
1189
1677
  raise ArgumentsRequired(self.id + ' cancelOrders() requires a symbol argument')
1190
1678
  await self.load_markets()
1679
+ request = self.cancel_orders_request(ids, symbol, params)
1680
+ response = await self.privatePostExchange(request)
1681
+ #
1682
+ # {
1683
+ # "status":"ok",
1684
+ # "response":{
1685
+ # "type":"cancel",
1686
+ # "data":{
1687
+ # "statuses":[
1688
+ # "success"
1689
+ # ]
1690
+ # }
1691
+ # }
1692
+ # }
1693
+ #
1694
+ innerResponse = self.safe_dict(response, 'response')
1695
+ data = self.safe_dict(innerResponse, 'data')
1696
+ statuses = self.safe_list(data, 'statuses')
1697
+ orders = []
1698
+ for i in range(0, len(statuses)):
1699
+ status = statuses[i]
1700
+ orders.append(self.safe_order({
1701
+ 'info': status,
1702
+ 'status': status,
1703
+ }))
1704
+ return orders
1705
+
1706
+ def cancel_orders_request(self, ids: List[str], symbol: Str = None, params={}) -> dict:
1707
+ """
1708
+ build the request payload for cancelling multiple orders
1709
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s
1710
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
1711
+ :param str[] ids: order ids
1712
+ :param str symbol: unified market symbol
1713
+ :param dict [params]:
1714
+ :returns dict: the raw request object to be sent to the exchange
1715
+ """
1191
1716
  market = self.market(symbol)
1192
1717
  clientOrderId = self.safe_value_2(params, 'clientOrderId', 'client_id')
1193
1718
  params = self.omit(params, ['clientOrderId', 'client_id'])
@@ -1219,37 +1744,28 @@ class hyperliquid(Exchange, ImplicitAPI):
1219
1744
  'o': self.parse_to_numeric(ids[i]),
1220
1745
  })
1221
1746
  cancelAction['cancels'] = cancelReq
1222
- vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
1747
+ vaultAddress = None
1748
+ vaultAddress, params = self.handle_option_and_params_2(params, 'cancelOrders', 'vaultAddress', 'subAccountAddress')
1749
+ vaultAddress = self.format_vault_address(vaultAddress)
1223
1750
  signature = self.sign_l1_action(cancelAction, nonce, vaultAddress)
1224
1751
  request['action'] = cancelAction
1225
1752
  request['signature'] = signature
1226
1753
  if vaultAddress is not None:
1227
1754
  params = self.omit(params, 'vaultAddress')
1228
1755
  request['vaultAddress'] = vaultAddress
1229
- response = await self.privatePostExchange(self.extend(request, params))
1230
- #
1231
- # {
1232
- # "status":"ok",
1233
- # "response":{
1234
- # "type":"cancel",
1235
- # "data":{
1236
- # "statuses":[
1237
- # "success"
1238
- # ]
1239
- # }
1240
- # }
1241
- # }
1242
- #
1243
- return response
1756
+ return request
1244
1757
 
1245
1758
  async def cancel_orders_for_symbols(self, orders: List[CancellationRequest], params={}):
1246
1759
  """
1247
1760
  cancel multiple orders for multiple symbols
1248
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s
1249
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
1250
- :param CancellationRequest[] orders: each order should contain the parameters required by cancelOrder namely id and symbol
1761
+
1762
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s
1763
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
1764
+
1765
+ :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"}]
1251
1766
  :param dict [params]: extra parameters specific to the exchange API endpoint
1252
1767
  :param str [params.vaultAddress]: the vault address
1768
+ :param str [params.subAccountAddress]: sub account user address
1253
1769
  :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1254
1770
  """
1255
1771
  self.check_required_credentials()
@@ -1285,14 +1801,16 @@ class hyperliquid(Exchange, ImplicitAPI):
1285
1801
  cancelReq.append(cancelObj)
1286
1802
  cancelAction['type'] = 'cancelByCloid' if cancelByCloid else 'cancel'
1287
1803
  cancelAction['cancels'] = cancelReq
1288
- vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
1804
+ vaultAddress = None
1805
+ vaultAddress, params = self.handle_option_and_params_2(params, 'cancelOrdersForSymbols', 'vaultAddress', 'subAccountAddress')
1806
+ vaultAddress = self.format_vault_address(vaultAddress)
1289
1807
  signature = self.sign_l1_action(cancelAction, nonce, vaultAddress)
1290
1808
  request['action'] = cancelAction
1291
1809
  request['signature'] = signature
1292
1810
  if vaultAddress is not None:
1293
1811
  params = self.omit(params, 'vaultAddress')
1294
1812
  request['vaultAddress'] = vaultAddress
1295
- response = await self.privatePostExchange(self.extend(request, params))
1813
+ response = await self.privatePostExchange(request)
1296
1814
  #
1297
1815
  # {
1298
1816
  # "status":"ok",
@@ -1306,7 +1824,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1306
1824
  # }
1307
1825
  # }
1308
1826
  #
1309
- return response
1827
+ return [self.safe_order({'info': response})]
1310
1828
 
1311
1829
  async def cancel_all_orders_after(self, timeout: Int, params={}):
1312
1830
  """
@@ -1314,6 +1832,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1314
1832
  :param number timeout: time in milliseconds, 0 represents cancel the timer
1315
1833
  :param dict [params]: extra parameters specific to the exchange API endpoint
1316
1834
  :param str [params.vaultAddress]: the vault address
1835
+ :param str [params.subAccountAddress]: sub account user address
1317
1836
  :returns dict: the api result
1318
1837
  """
1319
1838
  self.check_required_credentials()
@@ -1328,14 +1847,16 @@ class hyperliquid(Exchange, ImplicitAPI):
1328
1847
  'type': 'scheduleCancel',
1329
1848
  'time': nonce + timeout,
1330
1849
  }
1331
- vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
1850
+ vaultAddress = None
1851
+ vaultAddress, params = self.handle_option_and_params_2(params, 'cancelAllOrdersAfter', 'vaultAddress', 'subAccountAddress')
1852
+ vaultAddress = self.format_vault_address(vaultAddress)
1332
1853
  signature = self.sign_l1_action(cancelAction, nonce, vaultAddress)
1333
1854
  request['action'] = cancelAction
1334
1855
  request['signature'] = signature
1335
1856
  if vaultAddress is not None:
1336
1857
  params = self.omit(params, 'vaultAddress')
1337
1858
  request['vaultAddress'] = vaultAddress
1338
- response = await self.privatePostExchange(self.extend(request, params))
1859
+ response = await self.privatePostExchange(request)
1339
1860
  #
1340
1861
  # {
1341
1862
  # "status":"err",
@@ -1344,96 +1865,102 @@ class hyperliquid(Exchange, ImplicitAPI):
1344
1865
  #
1345
1866
  return response
1346
1867
 
1347
- async def edit_order(self, id: str, symbol: str, type: str, side: str, amount: Num = None, price: Num = None, params={}):
1348
- """
1349
- edit a trade order
1350
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#modify-an-order
1351
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#modify-multiple-orders
1352
- :param str id: cancel order id
1353
- :param str symbol: unified symbol of the market to create an order in
1354
- :param str type: 'market' or 'limit'
1355
- :param str side: 'buy' or 'sell'
1356
- :param float amount: how much of currency you want to trade in units of base currency
1357
- :param float [price]: the price at which the order is to be fullfilled, in units of the base currency, ignored in market orders
1358
- :param dict [params]: extra parameters specific to the exchange API endpoint
1359
- :param str [params.timeInForce]: 'Gtc', 'Ioc', 'Alo'
1360
- :param bool [params.postOnly]: True or False whether the order is post-only
1361
- :param bool [params.reduceOnly]: True or False whether the order is reduce-only
1362
- :param float [params.triggerPrice]: The price at which a trigger order is triggered at
1363
- :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
1364
- :param str [params.vaultAddress]: the vault address for order
1365
- :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1366
- """
1868
+ def edit_orders_request(self, orders, params={}):
1367
1869
  self.check_required_credentials()
1368
- if id is None:
1369
- raise ArgumentsRequired(self.id + ' editOrder() requires an id argument')
1370
- await self.load_markets()
1371
- market = self.market(symbol)
1372
- type = type.upper()
1373
- isMarket = (type == 'MARKET')
1374
- side = side.upper()
1375
- isBuy = (side == 'BUY')
1376
- defaultSlippage = self.safe_string(self.options, 'defaultSlippage')
1377
- slippage = self.safe_string(params, 'slippage', defaultSlippage)
1378
- defaultTimeInForce = 'ioc' if (isMarket) else 'gtc'
1379
- postOnly = self.safe_bool(params, 'postOnly', False)
1380
- if postOnly:
1381
- defaultTimeInForce = 'alo'
1382
- timeInForce = self.safe_string_lower(params, 'timeInForce', defaultTimeInForce)
1383
- timeInForce = self.capitalize(timeInForce)
1384
- clientOrderId = self.safe_string_2(params, 'clientOrderId', 'client_id')
1385
- triggerPrice = self.safe_string_2(params, 'triggerPrice', 'stopPrice')
1386
- stopLossPrice = self.safe_string(params, 'stopLossPrice', triggerPrice)
1387
- takeProfitPrice = self.safe_string(params, 'takeProfitPrice')
1388
- isTrigger = (stopLossPrice or takeProfitPrice)
1389
- params = self.omit(params, ['slippage', 'timeInForce', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice', 'clientOrderId', 'client_id'])
1390
- px = str(price)
1391
- if isMarket:
1392
- px = str(Precise.string_mul(price), Precise.string_add('1', slippage)) if (isBuy) else str(Precise.string_mul(price), Precise.string_sub('1', slippage))
1393
- else:
1394
- px = self.price_to_precision(symbol, str(price))
1395
- sz = self.amount_to_precision(symbol, amount)
1396
- reduceOnly = self.safe_bool(params, 'reduceOnly', False)
1397
- orderType: dict = {}
1398
- if isTrigger:
1399
- isTp = False
1400
- if takeProfitPrice is not None:
1401
- triggerPrice = self.price_to_precision(symbol, takeProfitPrice)
1402
- isTp = True
1870
+ hasClientOrderId = False
1871
+ for i in range(0, len(orders)):
1872
+ rawOrder = orders[i]
1873
+ orderParams = self.safe_dict(rawOrder, 'params', {})
1874
+ clientOrderId = self.safe_string_2(orderParams, 'clientOrderId', 'client_id')
1875
+ if clientOrderId is not None:
1876
+ hasClientOrderId = True
1877
+ if hasClientOrderId:
1878
+ for i in range(0, len(orders)):
1879
+ rawOrder = orders[i]
1880
+ orderParams = self.safe_dict(rawOrder, 'params', {})
1881
+ clientOrderId = self.safe_string_2(orderParams, 'clientOrderId', 'client_id')
1882
+ if clientOrderId is None:
1883
+ raise ArgumentsRequired(self.id + ' editOrders() all orders must have clientOrderId if at least one has a clientOrderId')
1884
+ params = self.omit(params, ['slippage', 'clientOrderId', 'client_id', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce'])
1885
+ modifies = []
1886
+ for i in range(0, len(orders)):
1887
+ rawOrder = orders[i]
1888
+ id = self.safe_string(rawOrder, 'id')
1889
+ marketId = self.safe_string(rawOrder, 'symbol')
1890
+ market = self.market(marketId)
1891
+ symbol = market['symbol']
1892
+ type = self.safe_string_upper(rawOrder, 'type')
1893
+ isMarket = (type == 'MARKET')
1894
+ side = self.safe_string_upper(rawOrder, 'side')
1895
+ isBuy = (side == 'BUY')
1896
+ amount = self.safe_string(rawOrder, 'amount')
1897
+ price = self.safe_string(rawOrder, 'price')
1898
+ orderParams = self.safe_dict(rawOrder, 'params', {})
1899
+ defaultSlippage = self.safe_string(self.options, 'defaultSlippage')
1900
+ slippage = self.safe_string(orderParams, 'slippage', defaultSlippage)
1901
+ defaultTimeInForce = 'ioc' if (isMarket) else 'gtc'
1902
+ postOnly = self.safe_bool(orderParams, 'postOnly', False)
1903
+ if postOnly:
1904
+ defaultTimeInForce = 'alo'
1905
+ timeInForce = self.safe_string_lower(orderParams, 'timeInForce', defaultTimeInForce)
1906
+ timeInForce = self.capitalize(timeInForce)
1907
+ clientOrderId = self.safe_string_2(orderParams, 'clientOrderId', 'client_id')
1908
+ triggerPrice = self.safe_string_2(orderParams, 'triggerPrice', 'stopPrice')
1909
+ stopLossPrice = self.safe_string(orderParams, 'stopLossPrice', triggerPrice)
1910
+ takeProfitPrice = self.safe_string(orderParams, 'takeProfitPrice')
1911
+ isTrigger = (stopLossPrice or takeProfitPrice)
1912
+ reduceOnly = self.safe_bool(orderParams, 'reduceOnly', False)
1913
+ orderParams = self.omit(orderParams, ['slippage', 'timeInForce', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice', 'clientOrderId', 'client_id', 'postOnly', 'reduceOnly'])
1914
+ px = self.number_to_string(price)
1915
+ if isMarket:
1916
+ px = Precise.string_mul(px, Precise.string_add('1', slippage)) if (isBuy) else Precise.string_mul(px, Precise.string_sub('1', slippage))
1917
+ px = self.price_to_precision(symbol, px)
1403
1918
  else:
1404
- triggerPrice = self.price_to_precision(symbol, stopLossPrice)
1405
- orderType['trigger'] = {
1406
- 'isMarket': isMarket,
1407
- 'triggerPx': triggerPrice,
1408
- 'tpsl': 'tp' if (isTp) else 'sl',
1919
+ px = self.price_to_precision(symbol, px)
1920
+ sz = self.amount_to_precision(symbol, amount)
1921
+ orderType: dict = {}
1922
+ if isTrigger:
1923
+ isTp = False
1924
+ if takeProfitPrice is not None:
1925
+ triggerPrice = self.price_to_precision(symbol, takeProfitPrice)
1926
+ isTp = True
1927
+ else:
1928
+ triggerPrice = self.price_to_precision(symbol, stopLossPrice)
1929
+ orderType['trigger'] = {
1930
+ 'isMarket': isMarket,
1931
+ 'triggerPx': triggerPrice,
1932
+ 'tpsl': 'tp' if (isTp) else 'sl',
1933
+ }
1934
+ else:
1935
+ orderType['limit'] = {
1936
+ 'tif': timeInForce,
1937
+ }
1938
+ if triggerPrice is None:
1939
+ triggerPrice = '0'
1940
+ orderReq: dict = {
1941
+ 'a': self.parse_to_int(market['baseId']),
1942
+ 'b': isBuy,
1943
+ 'p': px,
1944
+ 's': sz,
1945
+ 'r': reduceOnly,
1946
+ 't': orderType,
1947
+ # 'c': clientOrderId,
1409
1948
  }
1410
- else:
1411
- orderType['limit'] = {
1412
- 'tif': timeInForce,
1949
+ if clientOrderId is not None:
1950
+ orderReq['c'] = clientOrderId
1951
+ modifyReq: dict = {
1952
+ 'oid': self.parse_to_int(id),
1953
+ 'order': orderReq,
1413
1954
  }
1414
- if triggerPrice is None:
1415
- triggerPrice = '0'
1955
+ modifies.append(modifyReq)
1416
1956
  nonce = self.milliseconds()
1417
- orderReq: dict = {
1418
- 'a': self.parse_to_int(market['baseId']),
1419
- 'b': isBuy,
1420
- 'p': px,
1421
- 's': sz,
1422
- 'r': reduceOnly,
1423
- 't': orderType,
1424
- # 'c': clientOrderId,
1425
- }
1426
- if clientOrderId is not None:
1427
- orderReq['c'] = clientOrderId
1428
- modifyReq: dict = {
1429
- 'oid': self.parse_to_int(id),
1430
- 'order': orderReq,
1431
- }
1432
1957
  modifyAction: dict = {
1433
1958
  'type': 'batchModify',
1434
- 'modifies': [modifyReq],
1959
+ 'modifies': modifies,
1435
1960
  }
1436
- vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
1961
+ vaultAddress = None
1962
+ vaultAddress, params = self.handle_option_and_params(params, 'editOrder', 'vaultAddress')
1963
+ vaultAddress = self.format_vault_address(vaultAddress)
1437
1964
  signature = self.sign_l1_action(modifyAction, nonce, vaultAddress)
1438
1965
  request: dict = {
1439
1966
  'action': modifyAction,
@@ -1442,9 +1969,51 @@ class hyperliquid(Exchange, ImplicitAPI):
1442
1969
  # 'vaultAddress': vaultAddress,
1443
1970
  }
1444
1971
  if vaultAddress is not None:
1445
- params = self.omit(params, 'vaultAddress')
1446
1972
  request['vaultAddress'] = vaultAddress
1447
- response = await self.privatePostExchange(self.extend(request, params))
1973
+ return request
1974
+
1975
+ async def edit_order(self, id: str, symbol: str, type: str, side: str, amount: Num = None, price: Num = None, params={}):
1976
+ """
1977
+ edit a trade order
1978
+
1979
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#modify-multiple-orders
1980
+
1981
+ :param str id: cancel order id
1982
+ :param str symbol: unified symbol of the market to create an order in
1983
+ :param str type: 'market' or 'limit'
1984
+ :param str side: 'buy' or 'sell'
1985
+ :param float amount: how much of currency you want to trade in units of base currency
1986
+ :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
1987
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1988
+ :param str [params.timeInForce]: 'Gtc', 'Ioc', 'Alo'
1989
+ :param bool [params.postOnly]: True or False whether the order is post-only
1990
+ :param bool [params.reduceOnly]: True or False whether the order is reduce-only
1991
+ :param float [params.triggerPrice]: The price at which a trigger order is triggered at
1992
+ :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
1993
+ :param str [params.vaultAddress]: the vault address for order
1994
+ :param str [params.subAccountAddress]: sub account user address
1995
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1996
+ """
1997
+ await self.load_markets()
1998
+ if id is None:
1999
+ raise ArgumentsRequired(self.id + ' editOrder() requires an id argument')
2000
+ order, globalParams = self.parse_create_edit_order_args(id, symbol, type, side, amount, price, params)
2001
+ orders = await self.edit_orders([order], globalParams)
2002
+ return orders[0]
2003
+
2004
+ async def edit_orders(self, orders: List[OrderRequest], params={}):
2005
+ """
2006
+ edit a list of trade orders
2007
+
2008
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#modify-multiple-orders
2009
+
2010
+ :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
2011
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2012
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2013
+ """
2014
+ await self.load_markets()
2015
+ request = self.edit_orders_request(orders, params)
2016
+ response = await self.privatePostExchange(request)
1448
2017
  #
1449
2018
  # {
1450
2019
  # "status": "ok",
@@ -1483,13 +2052,52 @@ class hyperliquid(Exchange, ImplicitAPI):
1483
2052
  responseObject = self.safe_dict(response, 'response', {})
1484
2053
  dataObject = self.safe_dict(responseObject, 'data', {})
1485
2054
  statuses = self.safe_list(dataObject, 'statuses', [])
1486
- first = self.safe_dict(statuses, 0, {})
1487
- return self.parse_order(first, market)
2055
+ return self.parse_orders(statuses)
2056
+
2057
+ async def create_vault(self, name: str, description: str, initialUsd: int, params={}):
2058
+ """
2059
+ creates a value
2060
+ :param str name: The name of the vault
2061
+ :param str description: The description of the vault
2062
+ :param number initialUsd: The initialUsd of the vault
2063
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2064
+ :returns dict: the api result
2065
+ """
2066
+ self.check_required_credentials()
2067
+ await self.load_markets()
2068
+ nonce = self.milliseconds()
2069
+ request: dict = {
2070
+ 'nonce': nonce,
2071
+ }
2072
+ usd = self.parse_to_int(Precise.string_mul(self.number_to_string(initialUsd), '1000000'))
2073
+ action: dict = {
2074
+ 'type': 'createVault',
2075
+ 'name': name,
2076
+ 'description': description,
2077
+ 'initialUsd': usd,
2078
+ 'nonce': nonce,
2079
+ }
2080
+ signature = self.sign_l1_action(action, nonce)
2081
+ request['action'] = action
2082
+ request['signature'] = signature
2083
+ response = await self.privatePostExchange(self.extend(request, params))
2084
+ #
2085
+ # {
2086
+ # "status": "ok",
2087
+ # "response": {
2088
+ # "type": "createVault",
2089
+ # "data": "0x04fddcbc9ce80219301bd16f18491bedf2a8c2b8"
2090
+ # }
2091
+ # }
2092
+ #
2093
+ return response
1488
2094
 
1489
2095
  async def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1490
2096
  """
1491
2097
  fetches historical funding rate prices
1492
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-historical-funding-rates
2098
+
2099
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-historical-funding-rates
2100
+
1493
2101
  :param str symbol: unified symbol of the market to fetch the funding rate history for
1494
2102
  :param int [since]: timestamp in ms of the earliest funding rate to fetch
1495
2103
  :param int [limit]: the maximum amount of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-history-structure>` to fetch
@@ -1498,15 +2106,18 @@ class hyperliquid(Exchange, ImplicitAPI):
1498
2106
  :returns dict[]: a list of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-history-structure>`
1499
2107
  """
1500
2108
  await self.load_markets()
2109
+ if symbol is None:
2110
+ raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
1501
2111
  market = self.market(symbol)
1502
2112
  request: dict = {
1503
2113
  'type': 'fundingHistory',
1504
- 'coin': market['base'],
2114
+ 'coin': market['baseName'],
1505
2115
  }
1506
2116
  if since is not None:
1507
2117
  request['startTime'] = since
1508
2118
  else:
1509
- request['startTime'] = self.milliseconds() - 100 * 60 * 60 * 1000
2119
+ maxLimit = 500 if (limit is None) else limit
2120
+ request['startTime'] = self.milliseconds() - maxLimit * 60 * 60 * 1000
1510
2121
  until = self.safe_integer(params, 'until')
1511
2122
  params = self.omit(params, 'until')
1512
2123
  if until is not None:
@@ -1539,13 +2150,16 @@ class hyperliquid(Exchange, ImplicitAPI):
1539
2150
  async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1540
2151
  """
1541
2152
  fetch all unfilled currently open orders
1542
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-open-orders
2153
+
2154
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-open-orders
2155
+
1543
2156
  :param str symbol: unified market symbol
1544
2157
  :param int [since]: the earliest time in ms to fetch open orders for
1545
2158
  :param int [limit]: the maximum number of open orders structures to retrieve
1546
2159
  :param dict [params]: extra parameters specific to the exchange API endpoint
1547
2160
  :param str [params.user]: user address, will default to self.walletAddress if not provided
1548
2161
  :param str [params.method]: 'openOrders' or 'frontendOpenOrders' default is 'frontendOpenOrders'
2162
+ :param str [params.subAccountAddress]: sub account user address
1549
2163
  :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1550
2164
  """
1551
2165
  userAddress = None
@@ -1572,7 +2186,14 @@ class hyperliquid(Exchange, ImplicitAPI):
1572
2186
  # }
1573
2187
  # ]
1574
2188
  #
1575
- return self.parse_orders(response, market, since, limit)
2189
+ orderWithStatus = []
2190
+ for i in range(0, len(response)):
2191
+ order = response[i]
2192
+ extendOrder = {}
2193
+ if self.safe_string(order, 'status') is None:
2194
+ extendOrder['ccxtStatus'] = 'open'
2195
+ orderWithStatus.append(self.extend(order, extendOrder))
2196
+ return self.parse_orders(orderWithStatus, market, since, limit)
1576
2197
 
1577
2198
  async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1578
2199
  """
@@ -1584,8 +2205,54 @@ class hyperliquid(Exchange, ImplicitAPI):
1584
2205
  :param str [params.user]: user address, will default to self.walletAddress if not provided
1585
2206
  :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1586
2207
  """
2208
+ await self.load_markets()
2209
+ orders = await self.fetch_orders(symbol, None, None, params) # don't filter here because we don't want to catch open orders
2210
+ closedOrders = self.filter_by_array(orders, 'status', ['closed'], False)
2211
+ return self.filter_by_symbol_since_limit(closedOrders, symbol, since, limit)
2212
+
2213
+ async def fetch_canceled_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
2214
+ """
2215
+ fetch all canceled orders
2216
+ :param str symbol: unified market symbol
2217
+ :param int [since]: the earliest time in ms to fetch open orders for
2218
+ :param int [limit]: the maximum number of open orders structures to retrieve
2219
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2220
+ :param str [params.user]: user address, will default to self.walletAddress if not provided
2221
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2222
+ """
2223
+ await self.load_markets()
2224
+ orders = await self.fetch_orders(symbol, None, None, params) # don't filter here because we don't want to catch open orders
2225
+ closedOrders = self.filter_by_array(orders, 'status', ['canceled'], False)
2226
+ return self.filter_by_symbol_since_limit(closedOrders, symbol, since, limit)
2227
+
2228
+ async def fetch_canceled_and_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
2229
+ """
2230
+ fetch all closed and canceled orders
2231
+ :param str symbol: unified market symbol
2232
+ :param int [since]: the earliest time in ms to fetch open orders for
2233
+ :param int [limit]: the maximum number of open orders structures to retrieve
2234
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2235
+ :param str [params.user]: user address, will default to self.walletAddress if not provided
2236
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2237
+ """
2238
+ await self.load_markets()
2239
+ orders = await self.fetch_orders(symbol, None, None, params) # don't filter here because we don't want to catch open orders
2240
+ closedOrders = self.filter_by_array(orders, 'status', ['canceled', 'closed', 'rejected'], False)
2241
+ return self.filter_by_symbol_since_limit(closedOrders, symbol, since, limit)
2242
+
2243
+ async def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
2244
+ """
2245
+ fetch all orders
2246
+ :param str symbol: unified market symbol
2247
+ :param int [since]: the earliest time in ms to fetch open orders for
2248
+ :param int [limit]: the maximum number of open orders structures to retrieve
2249
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2250
+ :param str [params.user]: user address, will default to self.walletAddress if not provided
2251
+ :param str [params.subAccountAddress]: sub account user address
2252
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2253
+ """
1587
2254
  userAddress = None
1588
- userAddress, params = self.handle_public_address('fetchClosedOrders', params)
2255
+ userAddress, params = self.handle_public_address('fetchOrders', params)
1589
2256
  await self.load_markets()
1590
2257
  market = self.safe_market(symbol)
1591
2258
  request: dict = {
@@ -1611,21 +2278,33 @@ class hyperliquid(Exchange, ImplicitAPI):
1611
2278
  async def fetch_order(self, id: str, symbol: Str = None, params={}):
1612
2279
  """
1613
2280
  fetches information on an order made by the user
1614
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#query-order-status-by-oid-or-cloid
2281
+
2282
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#query-order-status-by-oid-or-cloid
2283
+
2284
+ :param str id: order id
1615
2285
  :param str symbol: unified symbol of the market the order was made in
1616
2286
  :param dict [params]: extra parameters specific to the exchange API endpoint
2287
+ :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
1617
2288
  :param str [params.user]: user address, will default to self.walletAddress if not provided
2289
+ :param str [params.subAccountAddress]: sub account user address
1618
2290
  :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1619
2291
  """
1620
2292
  userAddress = None
1621
2293
  userAddress, params = self.handle_public_address('fetchOrder', params)
1622
2294
  await self.load_markets()
1623
2295
  market = self.safe_market(symbol)
2296
+ clientOrderId = self.safe_string(params, 'clientOrderId')
1624
2297
  request: dict = {
1625
2298
  'type': 'orderStatus',
1626
- 'oid': self.parse_to_numeric(id),
2299
+ # 'oid': id if isClientOrderId else self.parse_to_numeric(id),
1627
2300
  'user': userAddress,
1628
2301
  }
2302
+ if clientOrderId is not None:
2303
+ params = self.omit(params, 'clientOrderId')
2304
+ request['oid'] = clientOrderId
2305
+ else:
2306
+ isClientOrderId = len(id) >= 34
2307
+ request['oid'] = id if isClientOrderId else self.parse_to_numeric(id)
1629
2308
  response = await self.publicPostInfo(self.extend(request, params))
1630
2309
  #
1631
2310
  # {
@@ -1658,6 +2337,10 @@ class hyperliquid(Exchange, ImplicitAPI):
1658
2337
  return self.parse_order(data, market)
1659
2338
 
1660
2339
  def parse_order(self, order: dict, market: Market = None) -> Order:
2340
+ #
2341
+ # createOrdersWs error
2342
+ #
2343
+ # {error: 'Insufficient margin to place order. asset=159'}
1661
2344
  #
1662
2345
  # fetchOpenOrders
1663
2346
  #
@@ -1749,26 +2432,36 @@ class hyperliquid(Exchange, ImplicitAPI):
1749
2432
  # "triggerPx": "0.6"
1750
2433
  # }
1751
2434
  #
2435
+ error = self.safe_string(order, 'error')
2436
+ if error is not None:
2437
+ return self.safe_order({
2438
+ 'info': order,
2439
+ 'status': 'rejected',
2440
+ })
1752
2441
  entry = self.safe_dict_n(order, ['order', 'resting', 'filled'])
1753
2442
  if entry is None:
1754
2443
  entry = order
1755
2444
  coin = self.safe_string(entry, 'coin')
1756
2445
  marketId = None
1757
2446
  if coin is not None:
1758
- if coin.find('/') > -1:
1759
- marketId = coin
1760
- else:
1761
- marketId = coin + '/USDC:USDC'
2447
+ marketId = self.coin_to_market_id(coin)
1762
2448
  if self.safe_string(entry, 'id') is None:
1763
2449
  market = self.safe_market(marketId, None)
1764
2450
  else:
1765
2451
  market = self.safe_market(marketId, market)
1766
2452
  symbol = market['symbol']
1767
- timestamp = self.safe_integer_2(order, 'timestamp', 'statusTimestamp')
1768
- status = self.safe_string(order, 'status')
2453
+ timestamp = self.safe_integer(entry, 'timestamp')
2454
+ status = self.safe_string_2(order, 'status', 'ccxtStatus')
2455
+ order = self.omit(order, ['ccxtStatus'])
1769
2456
  side = self.safe_string(entry, 'side')
1770
2457
  if side is not None:
1771
2458
  side = 'sell' if (side == 'A') else 'buy'
2459
+ totalAmount = self.safe_string_2(entry, 'origSz', 'totalSz')
2460
+ remaining = self.safe_string(entry, 'sz')
2461
+ tif = self.safe_string_upper(entry, 'tif')
2462
+ postOnly = None
2463
+ if tif is not None:
2464
+ postOnly = (tif == 'ALO')
1772
2465
  return self.safe_order({
1773
2466
  'info': order,
1774
2467
  'id': self.safe_string(entry, 'oid'),
@@ -1776,30 +2469,40 @@ class hyperliquid(Exchange, ImplicitAPI):
1776
2469
  'timestamp': timestamp,
1777
2470
  'datetime': self.iso8601(timestamp),
1778
2471
  'lastTradeTimestamp': None,
1779
- 'lastUpdateTimestamp': None,
2472
+ 'lastUpdateTimestamp': self.safe_integer(order, 'statusTimestamp'),
1780
2473
  'symbol': symbol,
1781
2474
  'type': self.parse_order_type(self.safe_string_lower(entry, 'orderType')),
1782
- 'timeInForce': self.safe_string_upper(entry, 'tif'),
1783
- 'postOnly': None,
2475
+ 'timeInForce': tif,
2476
+ 'postOnly': postOnly,
1784
2477
  'reduceOnly': self.safe_bool(entry, 'reduceOnly'),
1785
2478
  'side': side,
1786
- 'price': self.safe_number(entry, 'limitPx'),
2479
+ 'price': self.safe_string(entry, 'limitPx'),
1787
2480
  'triggerPrice': self.safe_number(entry, 'triggerPx') if self.safe_bool(entry, 'isTrigger') else None,
1788
- 'amount': self.safe_number_2(entry, 'sz', 'totalSz'),
2481
+ 'amount': totalAmount,
1789
2482
  'cost': None,
1790
- 'average': self.safe_number(entry, 'avgPx'),
1791
- 'filled': None,
1792
- 'remaining': None,
2483
+ 'average': self.safe_string(entry, 'avgPx'),
2484
+ 'filled': Precise.string_sub(totalAmount, remaining),
2485
+ 'remaining': remaining,
1793
2486
  'status': self.parse_order_status(status),
1794
2487
  'fee': None,
1795
2488
  'trades': None,
1796
2489
  }, market)
1797
2490
 
1798
2491
  def parse_order_status(self, status: Str):
2492
+ if status is None:
2493
+ return None
1799
2494
  statuses: dict = {
1800
2495
  'triggered': 'open',
1801
2496
  'filled': 'closed',
2497
+ 'open': 'open',
2498
+ 'canceled': 'canceled',
2499
+ 'rejected': 'rejected',
2500
+ 'marginCanceled': 'canceled',
1802
2501
  }
2502
+ if status.endswith('Rejected'):
2503
+ return 'rejected'
2504
+ if status.endswith('Canceled'):
2505
+ return 'canceled'
1803
2506
  return self.safe_string(statuses, status, status)
1804
2507
 
1805
2508
  def parse_order_type(self, status):
@@ -1812,13 +2515,16 @@ class hyperliquid(Exchange, ImplicitAPI):
1812
2515
  async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1813
2516
  """
1814
2517
  fetch all trades made by the user
1815
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills
1816
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills-by-time
2518
+
2519
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills
2520
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills-by-time
2521
+
1817
2522
  :param str symbol: unified market symbol
1818
2523
  :param int [since]: the earliest time in ms to fetch trades for
1819
2524
  :param int [limit]: the maximum number of trades structures to retrieve
1820
2525
  :param dict [params]: extra parameters specific to the exchange API endpoint
1821
2526
  :param int [params.until]: timestamp in ms of the latest trade
2527
+ :param str [params.subAccountAddress]: sub account user address
1822
2528
  :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
1823
2529
  """
1824
2530
  userAddress = None
@@ -1846,6 +2552,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1846
2552
  # "crossed": True,
1847
2553
  # "dir": "Close Long",
1848
2554
  # "fee": "0.050062",
2555
+ # "feeToken": "USDC",
1849
2556
  # "hash": "0x09d77c96791e98b5775a04092584ab010d009445119c71e4005c0d634ea322bc",
1850
2557
  # "liquidationMarkPx": null,
1851
2558
  # "oid": 3929354691,
@@ -1883,7 +2590,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1883
2590
  price = self.safe_string(trade, 'px')
1884
2591
  amount = self.safe_string(trade, 'sz')
1885
2592
  coin = self.safe_string(trade, 'coin')
1886
- marketId = coin + '/USDC:USDC'
2593
+ marketId = self.coin_to_market_id(coin)
1887
2594
  market = self.safe_market(marketId, None)
1888
2595
  symbol = market['symbol']
1889
2596
  id = self.safe_string(trade, 'tid')
@@ -1891,6 +2598,10 @@ class hyperliquid(Exchange, ImplicitAPI):
1891
2598
  if side is not None:
1892
2599
  side = 'sell' if (side == 'A') else 'buy'
1893
2600
  fee = self.safe_string(trade, 'fee')
2601
+ takerOrMaker = None
2602
+ crossed = self.safe_bool(trade, 'crossed')
2603
+ if crossed is not None:
2604
+ takerOrMaker = 'taker' if crossed else 'maker'
1894
2605
  return self.safe_trade({
1895
2606
  'info': trade,
1896
2607
  'timestamp': timestamp,
@@ -1900,17 +2611,23 @@ class hyperliquid(Exchange, ImplicitAPI):
1900
2611
  'order': self.safe_string(trade, 'oid'),
1901
2612
  'type': None,
1902
2613
  'side': side,
1903
- 'takerOrMaker': None,
2614
+ 'takerOrMaker': takerOrMaker,
1904
2615
  'price': price,
1905
2616
  'amount': amount,
1906
2617
  'cost': None,
1907
- 'fee': {'cost': fee, 'currency': 'USDC'},
2618
+ 'fee': {
2619
+ 'cost': fee,
2620
+ 'currency': self.safe_string(trade, 'feeToken'),
2621
+ 'rate': None,
2622
+ },
1908
2623
  }, market)
1909
2624
 
1910
2625
  async def fetch_position(self, symbol: str, params={}):
1911
2626
  """
1912
2627
  fetch data on an open position
1913
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-state
2628
+
2629
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-users-perpetuals-account-summary
2630
+
1914
2631
  :param str symbol: unified market symbol of the market the position is held in
1915
2632
  :param dict [params]: extra parameters specific to the exchange API endpoint
1916
2633
  :param str [params.user]: user address, will default to self.walletAddress if not provided
@@ -1919,13 +2636,16 @@ class hyperliquid(Exchange, ImplicitAPI):
1919
2636
  positions = await self.fetch_positions([symbol], params)
1920
2637
  return self.safe_dict(positions, 0, {})
1921
2638
 
1922
- async def fetch_positions(self, symbols: Strings = None, params={}):
2639
+ async def fetch_positions(self, symbols: Strings = None, params={}) -> List[Position]:
1923
2640
  """
1924
2641
  fetch all open positions
1925
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-state
2642
+
2643
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-users-perpetuals-account-summary
2644
+
1926
2645
  :param str[] [symbols]: list of unified market symbols
1927
2646
  :param dict [params]: extra parameters specific to the exchange API endpoint
1928
2647
  :param str [params.user]: user address, will default to self.walletAddress if not provided
2648
+ :param str [params.subAccountAddress]: sub account user address
1929
2649
  :returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
1930
2650
  """
1931
2651
  await self.load_markets()
@@ -2017,18 +2737,22 @@ class hyperliquid(Exchange, ImplicitAPI):
2017
2737
  #
2018
2738
  entry = self.safe_dict(position, 'position', {})
2019
2739
  coin = self.safe_string(entry, 'coin')
2020
- marketId = coin + '/USDC:USDC'
2740
+ marketId = self.coin_to_market_id(coin)
2021
2741
  market = self.safe_market(marketId, None)
2022
2742
  symbol = market['symbol']
2023
2743
  leverage = self.safe_dict(entry, 'leverage', {})
2024
- isIsolated = (self.safe_string(leverage, 'type') == 'isolated')
2025
- quantity = self.safe_number(leverage, 'rawUsd')
2744
+ marginMode = self.safe_string(leverage, 'type')
2745
+ isIsolated = (marginMode == 'isolated')
2746
+ rawSize = self.safe_string(entry, 'szi')
2747
+ size = rawSize
2026
2748
  side = None
2027
- if quantity is not None:
2028
- side = 'short' if (quantity > 0) else 'long'
2029
- unrealizedPnl = self.safe_number(entry, 'unrealizedPnl')
2030
- initialMargin = self.safe_number(entry, 'marginUsed')
2031
- percentage = unrealizedPnl / initialMargin * 100
2749
+ if size is not None:
2750
+ side = 'long' if Precise.string_gt(rawSize, '0') else 'short'
2751
+ size = Precise.string_abs(size)
2752
+ rawUnrealizedPnl = self.safe_string(entry, 'unrealizedPnl')
2753
+ absRawUnrealizedPnl = Precise.string_abs(rawUnrealizedPnl)
2754
+ initialMargin = self.safe_string(entry, 'marginUsed')
2755
+ percentage = Precise.string_mul(Precise.string_div(absRawUnrealizedPnl, initialMargin), '100')
2032
2756
  return self.safe_position({
2033
2757
  'info': position,
2034
2758
  'id': None,
@@ -2038,21 +2762,21 @@ class hyperliquid(Exchange, ImplicitAPI):
2038
2762
  'isolated': isIsolated,
2039
2763
  'hedged': None,
2040
2764
  'side': side,
2041
- 'contracts': self.safe_number(entry, 'szi'),
2765
+ 'contracts': self.parse_number(size),
2042
2766
  'contractSize': None,
2043
2767
  'entryPrice': self.safe_number(entry, 'entryPx'),
2044
2768
  'markPrice': None,
2045
2769
  'notional': self.safe_number(entry, 'positionValue'),
2046
2770
  'leverage': self.safe_number(leverage, 'value'),
2047
- 'collateral': None,
2048
- 'initialMargin': initialMargin,
2771
+ 'collateral': self.safe_number(entry, 'marginUsed'),
2772
+ 'initialMargin': self.parse_number(initialMargin),
2049
2773
  'maintenanceMargin': None,
2050
2774
  'initialMarginPercentage': None,
2051
2775
  'maintenanceMarginPercentage': None,
2052
- 'unrealizedPnl': unrealizedPnl,
2776
+ 'unrealizedPnl': self.parse_number(rawUnrealizedPnl),
2053
2777
  'liquidationPrice': self.safe_number(entry, 'liquidationPx'),
2054
- 'marginMode': None,
2055
- 'percentage': percentage,
2778
+ 'marginMode': marginMode,
2779
+ 'percentage': self.parse_number(percentage),
2056
2780
  })
2057
2781
 
2058
2782
  async def set_margin_mode(self, marginMode: str, symbol: Str = None, params={}):
@@ -2062,6 +2786,8 @@ class hyperliquid(Exchange, ImplicitAPI):
2062
2786
  :param str symbol: unified market symbol of the market the position is held in, default is None
2063
2787
  :param dict [params]: extra parameters specific to the exchange API endpoint
2064
2788
  :param str [params.leverage]: the rate of leverage, is required if setting trade mode(symbol)
2789
+ :param str [params.vaultAddress]: the vault address
2790
+ :param str [params.subAccountAddress]: sub account user address
2065
2791
  :returns dict: response from the exchange
2066
2792
  """
2067
2793
  if symbol is None:
@@ -2081,15 +2807,14 @@ class hyperliquid(Exchange, ImplicitAPI):
2081
2807
  'isCross': isCross,
2082
2808
  'leverage': leverage,
2083
2809
  }
2084
- vaultAddress = self.safe_string(params, 'vaultAddress')
2810
+ vaultAddress = None
2811
+ vaultAddress, params = self.handle_option_and_params_2(params, 'setMarginMode', 'vaultAddress', 'subAccountAddress')
2085
2812
  if vaultAddress is not None:
2086
- params = self.omit(params, 'vaultAddress')
2087
2813
  if vaultAddress.startswith('0x'):
2088
2814
  vaultAddress = vaultAddress.replace('0x', '')
2089
- extendedAction = self.extend(updateAction, params)
2090
- signature = self.sign_l1_action(extendedAction, nonce, vaultAddress)
2815
+ signature = self.sign_l1_action(updateAction, nonce, vaultAddress)
2091
2816
  request: dict = {
2092
- 'action': extendedAction,
2817
+ 'action': updateAction,
2093
2818
  'nonce': nonce,
2094
2819
  'signature': signature,
2095
2820
  # 'vaultAddress': vaultAddress,
@@ -2107,7 +2832,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2107
2832
  #
2108
2833
  return response
2109
2834
 
2110
- async def set_leverage(self, leverage: Int, symbol: Str = None, params={}):
2835
+ async def set_leverage(self, leverage: int, symbol: Str = None, params={}):
2111
2836
  """
2112
2837
  set the level of leverage for a market
2113
2838
  :param float leverage: the rate of leverage
@@ -2131,7 +2856,9 @@ class hyperliquid(Exchange, ImplicitAPI):
2131
2856
  'isCross': isCross,
2132
2857
  'leverage': leverage,
2133
2858
  }
2134
- vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
2859
+ vaultAddress = None
2860
+ vaultAddress, params = self.handle_option_and_params_2(params, 'setLeverage', 'vaultAddress', 'subAccountAddress')
2861
+ vaultAddress = self.format_vault_address(vaultAddress)
2135
2862
  signature = self.sign_l1_action(updateAction, nonce, vaultAddress)
2136
2863
  request: dict = {
2137
2864
  'action': updateAction,
@@ -2142,7 +2869,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2142
2869
  if vaultAddress is not None:
2143
2870
  params = self.omit(params, 'vaultAddress')
2144
2871
  request['vaultAddress'] = vaultAddress
2145
- response = await self.privatePostExchange(self.extend(request, params))
2872
+ response = await self.privatePostExchange(request)
2146
2873
  #
2147
2874
  # {
2148
2875
  # 'response': {
@@ -2156,21 +2883,29 @@ class hyperliquid(Exchange, ImplicitAPI):
2156
2883
  async def add_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
2157
2884
  """
2158
2885
  add margin
2159
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#update-isolated-margin
2886
+
2887
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#update-isolated-margin
2888
+
2160
2889
  :param str symbol: unified market symbol
2161
2890
  :param float amount: amount of margin to add
2162
2891
  :param dict [params]: extra parameters specific to the exchange API endpoint
2892
+ :param str [params.vaultAddress]: the vault address
2893
+ :param str [params.subAccountAddress]: sub account user address
2163
2894
  :returns dict: a `margin structure <https://docs.ccxt.com/#/?id=add-margin-structure>`
2164
2895
  """
2165
2896
  return await self.modify_margin_helper(symbol, amount, 'add', params)
2166
2897
 
2167
2898
  async def reduce_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
2168
2899
  """
2169
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#update-isolated-margin
2900
+
2901
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#update-isolated-margin
2902
+
2170
2903
  remove margin from a position
2171
2904
  :param str symbol: unified market symbol
2172
2905
  :param float amount: the amount of margin to remove
2173
2906
  :param dict [params]: extra parameters specific to the exchange API endpoint
2907
+ :param str [params.vaultAddress]: the vault address
2908
+ :param str [params.subAccountAddress]: sub account user address
2174
2909
  :returns dict: a `margin structure <https://docs.ccxt.com/#/?id=reduce-margin-structure>`
2175
2910
  """
2176
2911
  return await self.modify_margin_helper(symbol, amount, 'reduce', params)
@@ -2189,7 +2924,9 @@ class hyperliquid(Exchange, ImplicitAPI):
2189
2924
  'isBuy': True,
2190
2925
  'ntli': sz,
2191
2926
  }
2192
- vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
2927
+ vaultAddress = None
2928
+ vaultAddress, params = self.handle_option_and_params_2(params, 'modifyMargin', 'vaultAddress', 'subAccountAddress')
2929
+ vaultAddress = self.format_vault_address(vaultAddress)
2193
2930
  signature = self.sign_l1_action(updateAction, nonce, vaultAddress)
2194
2931
  request: dict = {
2195
2932
  'action': updateAction,
@@ -2198,9 +2935,8 @@ class hyperliquid(Exchange, ImplicitAPI):
2198
2935
  # 'vaultAddress': vaultAddress,
2199
2936
  }
2200
2937
  if vaultAddress is not None:
2201
- params = self.omit(params, 'vaultAddress')
2202
2938
  request['vaultAddress'] = vaultAddress
2203
- response = await self.privatePostExchange(self.extend(request, params))
2939
+ response = await self.privatePostExchange(request)
2204
2940
  #
2205
2941
  # {
2206
2942
  # 'response': {
@@ -2235,7 +2971,9 @@ class hyperliquid(Exchange, ImplicitAPI):
2235
2971
  async def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
2236
2972
  """
2237
2973
  transfer currency internally between wallets on the same account
2238
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#l1-usdc-transfer
2974
+
2975
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#l1-usdc-transfer
2976
+
2239
2977
  :param str code: unified currency code
2240
2978
  :param float amount: amount to transfer
2241
2979
  :param str fromAccount: account to transfer from *spot, swap*
@@ -2251,64 +2989,115 @@ class hyperliquid(Exchange, ImplicitAPI):
2251
2989
  if self.in_array(fromAccount, ['spot', 'swap', 'perp']):
2252
2990
  # handle swap <> spot account transfer
2253
2991
  if not self.in_array(toAccount, ['spot', 'swap', 'perp']):
2254
- raise NotSupported(self.id + 'transfer() only support spot <> swap transfer')
2255
- vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
2256
- params = self.omit(params, 'vaultAddress')
2992
+ raise NotSupported(self.id + ' transfer() only support spot <> swap transfer')
2993
+ strAmount = self.number_to_string(amount)
2994
+ vaultAddress = self.safe_string_2(params, 'vaultAddress', 'subAccountAddress')
2995
+ if vaultAddress is not None:
2996
+ vaultAddress = self.format_vault_address(vaultAddress)
2997
+ strAmount = strAmount + ' subaccount:' + vaultAddress
2257
2998
  toPerp = (toAccount == 'perp') or (toAccount == 'swap')
2258
- action: dict = {
2259
- 'type': 'spotUser',
2260
- 'classTransfer': {
2261
- 'usdc': amount,
2999
+ transferPayload: dict = {
3000
+ 'hyperliquidChain': 'Testnet' if isSandboxMode else 'Mainnet',
3001
+ 'amount': strAmount,
3002
+ 'toPerp': toPerp,
3003
+ 'nonce': nonce,
3004
+ }
3005
+ transferSig = self.build_usd_class_send_sig(transferPayload)
3006
+ transferRequest: dict = {
3007
+ 'action': {
3008
+ 'hyperliquidChain': transferPayload['hyperliquidChain'],
3009
+ 'signatureChainId': '0x66eee',
3010
+ 'type': 'usdClassTransfer',
3011
+ 'amount': strAmount,
2262
3012
  'toPerp': toPerp,
3013
+ 'nonce': nonce,
2263
3014
  },
2264
- }
2265
- signature = self.sign_l1_action(action, nonce, vaultAddress)
2266
- innerRequest: dict = {
2267
- 'action': self.extend(action, params),
2268
3015
  'nonce': nonce,
2269
- 'signature': signature,
3016
+ 'signature': transferSig,
2270
3017
  }
2271
3018
  if vaultAddress is not None:
2272
- innerRequest['vaultAddress'] = vaultAddress
2273
- transferResponse = await self.privatePostExchange(innerRequest)
3019
+ transferRequest['vaultAddress'] = vaultAddress
3020
+ transferResponse = await self.privatePostExchange(transferRequest)
2274
3021
  return transferResponse
2275
- # handle sub-account/different account transfer
2276
- self.check_address(toAccount)
2277
- if code is not None:
2278
- code = code.upper()
2279
- if code != 'USDC':
2280
- raise NotSupported(self.id + 'transfer() only support USDC')
2281
- payload: dict = {
2282
- 'hyperliquidChain': 'Testnet' if isSandboxMode else 'Mainnet',
2283
- 'destination': toAccount,
2284
- 'amount': self.number_to_string(amount),
2285
- 'time': nonce,
2286
- }
2287
- sig = self.build_transfer_sig(payload)
2288
- request: dict = {
2289
- 'action': {
2290
- 'hyperliquidChain': payload['hyperliquidChain'],
2291
- 'signatureChainId': '0x66eee', # check self out
2292
- 'destination': toAccount,
2293
- 'amount': str(amount),
2294
- 'time': nonce,
2295
- 'type': 'usdSend',
2296
- },
2297
- 'nonce': nonce,
2298
- 'signature': sig,
3022
+ # transfer between main account and subaccount
3023
+ isDeposit = False
3024
+ subAccountAddress = None
3025
+ if fromAccount == 'main':
3026
+ subAccountAddress = toAccount
3027
+ isDeposit = True
3028
+ elif toAccount == 'main':
3029
+ subAccountAddress = fromAccount
3030
+ else:
3031
+ raise NotSupported(self.id + ' transfer() only support main <> subaccount transfer')
3032
+ self.check_address(subAccountAddress)
3033
+ if code is None or code.upper() == 'USDC':
3034
+ # Transfer USDC with subAccountTransfer
3035
+ usd = self.parse_to_int(Precise.string_mul(self.number_to_string(amount), '1000000'))
3036
+ action = {
3037
+ 'type': 'subAccountTransfer',
3038
+ 'subAccountUser': subAccountAddress,
3039
+ 'isDeposit': isDeposit,
3040
+ 'usd': usd,
3041
+ }
3042
+ sig = self.sign_l1_action(action, nonce)
3043
+ request: dict = {
3044
+ 'action': action,
3045
+ 'nonce': nonce,
3046
+ 'signature': sig,
3047
+ }
3048
+ response = await self.privatePostExchange(request)
3049
+ #
3050
+ # {'response': {'type': 'default'}, 'status': 'ok'}
3051
+ #
3052
+ return self.parse_transfer(response)
3053
+ else:
3054
+ # Transfer non-USDC with subAccountSpotTransfer
3055
+ symbol = self.symbol(code)
3056
+ action = {
3057
+ 'type': 'subAccountSpotTransfer',
3058
+ 'subAccountUser': subAccountAddress,
3059
+ 'isDeposit': isDeposit,
3060
+ 'token': symbol,
3061
+ 'amount': self.number_to_string(amount),
3062
+ }
3063
+ sig = self.sign_l1_action(action, nonce)
3064
+ request: dict = {
3065
+ 'action': action,
3066
+ 'nonce': nonce,
3067
+ 'signature': sig,
3068
+ }
3069
+ response = await self.privatePostExchange(request)
3070
+ return self.parse_transfer(response)
3071
+
3072
+ def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
3073
+ #
3074
+ # {'response': {'type': 'default'}, 'status': 'ok'}
3075
+ #
3076
+ return {
3077
+ 'info': transfer,
3078
+ 'id': None,
3079
+ 'timestamp': None,
3080
+ 'datetime': None,
3081
+ 'currency': None,
3082
+ 'amount': None,
3083
+ 'fromAccount': None,
3084
+ 'toAccount': None,
3085
+ 'status': 'ok',
2299
3086
  }
2300
- response = await self.privatePostExchange(self.extend(request, params))
2301
- return response
2302
3087
 
2303
- async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}) -> Transaction:
3088
+ async def withdraw(self, code: str, amount: float, address: str, tag: Str = None, params={}) -> Transaction:
2304
3089
  """
2305
3090
  make a withdrawal(only support USDC)
2306
- :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#initiate-a-withdrawal-request
3091
+
3092
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#initiate-a-withdrawal-request
3093
+ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#deposit-or-withdraw-from-a-vault
3094
+
2307
3095
  :param str code: unified currency code
2308
3096
  :param float amount: the amount to withdraw
2309
3097
  :param str address: the address to withdraw to
2310
3098
  :param str tag:
2311
3099
  :param dict [params]: extra parameters specific to the exchange API endpoint
3100
+ :param str [params.vaultAddress]: vault address withdraw from
2312
3101
  :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
2313
3102
  """
2314
3103
  self.check_required_credentials()
@@ -2317,57 +3106,541 @@ class hyperliquid(Exchange, ImplicitAPI):
2317
3106
  if code is not None:
2318
3107
  code = code.upper()
2319
3108
  if code != 'USDC':
2320
- raise NotSupported(self.id + 'withdraw() only support USDC')
2321
- isSandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
3109
+ raise NotSupported(self.id + ' withdraw() only support USDC')
3110
+ vaultAddress = None
3111
+ vaultAddress, params = self.handle_option_and_params(params, 'withdraw', 'vaultAddress')
3112
+ vaultAddress = self.format_vault_address(vaultAddress)
3113
+ params = self.omit(params, 'vaultAddress')
2322
3114
  nonce = self.milliseconds()
2323
- payload: dict = {
2324
- 'hyperliquidChain': 'Testnet' if isSandboxMode else 'Mainnet',
2325
- 'destination': address,
2326
- 'amount': str(amount),
2327
- 'time': nonce,
2328
- }
2329
- sig = self.build_withdraw_sig(payload)
2330
- request: dict = {
2331
- 'action': {
3115
+ action: dict = {}
3116
+ sig = None
3117
+ if vaultAddress is not None:
3118
+ action = {
3119
+ 'type': 'vaultTransfer',
3120
+ 'vaultAddress': '0x' + vaultAddress,
3121
+ 'isDeposit': False,
3122
+ 'usd': amount,
3123
+ }
3124
+ sig = self.sign_l1_action(action, nonce)
3125
+ else:
3126
+ isSandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
3127
+ payload: dict = {
3128
+ 'hyperliquidChain': 'Testnet' if isSandboxMode else 'Mainnet',
3129
+ 'destination': address,
3130
+ 'amount': str(amount),
3131
+ 'time': nonce,
3132
+ }
3133
+ sig = self.build_withdraw_sig(payload)
3134
+ action = {
2332
3135
  'hyperliquidChain': payload['hyperliquidChain'],
2333
3136
  'signatureChainId': '0x66eee', # check self out
2334
3137
  'destination': address,
2335
3138
  'amount': str(amount),
2336
3139
  'time': nonce,
2337
3140
  'type': 'withdraw3',
2338
- },
3141
+ }
3142
+ request: dict = {
3143
+ 'action': action,
2339
3144
  'nonce': nonce,
2340
3145
  'signature': sig,
2341
3146
  }
2342
- response = await self.privatePostExchange(self.extend(request, params))
3147
+ response = await self.privatePostExchange(request)
2343
3148
  return self.parse_transaction(response)
2344
3149
 
2345
3150
  def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
2346
3151
  #
2347
3152
  # {status: 'ok', response: {type: 'default'}}
2348
3153
  #
3154
+ # fetchDeposits / fetchWithdrawals
3155
+ # {
3156
+ # "time":1724762307531,
3157
+ # "hash":"0x620a234a7e0eb7930575040f59482a01050058b0802163b4767bfd9033e77781",
3158
+ # "delta":{
3159
+ # "type":"accountClassTransfer",
3160
+ # "usdc":"50.0",
3161
+ # "toPerp":false
3162
+ # }
3163
+ # }
3164
+ #
3165
+ timestamp = self.safe_integer(transaction, 'time')
3166
+ delta = self.safe_dict(transaction, 'delta', {})
3167
+ fee = None
3168
+ feeCost = self.safe_integer(delta, 'fee')
3169
+ if feeCost is not None:
3170
+ fee = {
3171
+ 'currency': 'USDC',
3172
+ 'cost': feeCost,
3173
+ }
3174
+ internal = None
3175
+ type = self.safe_string(delta, 'type')
3176
+ if type is not None:
3177
+ internal = (type == 'internalTransfer')
2349
3178
  return {
2350
3179
  'info': transaction,
2351
3180
  'id': None,
2352
- 'txid': None,
2353
- 'timestamp': None,
2354
- 'datetime': None,
3181
+ 'txid': self.safe_string(transaction, 'hash'),
3182
+ 'timestamp': timestamp,
3183
+ 'datetime': self.iso8601(timestamp),
2355
3184
  'network': None,
2356
3185
  'address': None,
2357
- 'addressTo': None,
2358
- 'addressFrom': None,
3186
+ 'addressTo': self.safe_string(delta, 'destination'),
3187
+ 'addressFrom': self.safe_string(delta, 'user'),
2359
3188
  'tag': None,
2360
3189
  'tagTo': None,
2361
3190
  'tagFrom': None,
2362
3191
  'type': None,
2363
- 'amount': None,
3192
+ 'amount': self.safe_number(delta, 'usdc'),
2364
3193
  'currency': None,
2365
3194
  'status': self.safe_string(transaction, 'status'),
2366
3195
  'updated': None,
2367
3196
  'comment': None,
2368
- 'internal': None,
2369
- 'fee': None,
3197
+ 'internal': internal,
3198
+ 'fee': fee,
3199
+ }
3200
+
3201
+ async def fetch_trading_fee(self, symbol: str, params={}) -> TradingFeeInterface:
3202
+ """
3203
+ fetch the trading fees for a market
3204
+ :param str symbol: unified market symbol
3205
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3206
+ :param str [params.user]: user address, will default to self.walletAddress if not provided
3207
+ :param str [params.subAccountAddress]: sub account user address
3208
+ :returns dict: a `fee structure <https://docs.ccxt.com/#/?id=fee-structure>`
3209
+ """
3210
+ await self.load_markets()
3211
+ userAddress = None
3212
+ userAddress, params = self.handle_public_address('fetchTradingFee', params)
3213
+ market = self.market(symbol)
3214
+ request: dict = {
3215
+ 'type': 'userFees',
3216
+ 'user': userAddress,
3217
+ }
3218
+ response = await self.publicPostInfo(self.extend(request, params))
3219
+ #
3220
+ # {
3221
+ # "dailyUserVlm": [
3222
+ # {
3223
+ # "date": "2024-07-08",
3224
+ # "userCross": "0.0",
3225
+ # "userAdd": "0.0",
3226
+ # "exchange": "90597185.23639999"
3227
+ # }
3228
+ # ],
3229
+ # "feeSchedule": {
3230
+ # "cross": "0.00035",
3231
+ # "add": "0.0001",
3232
+ # "tiers": {
3233
+ # "vip": [
3234
+ # {
3235
+ # "ntlCutoff": "5000000.0",
3236
+ # "cross": "0.0003",
3237
+ # "add": "0.00005"
3238
+ # }
3239
+ # ],
3240
+ # "mm": [
3241
+ # {
3242
+ # "makerFractionCutoff": "0.005",
3243
+ # "add": "-0.00001"
3244
+ # }
3245
+ # ]
3246
+ # },
3247
+ # "referralDiscount": "0.04"
3248
+ # },
3249
+ # "userCrossRate": "0.00035",
3250
+ # "userAddRate": "0.0001",
3251
+ # "activeReferralDiscount": "0.0"
3252
+ # }
3253
+ #
3254
+ data: dict = {
3255
+ 'userCrossRate': self.safe_string(response, 'userCrossRate'),
3256
+ 'userAddRate': self.safe_string(response, 'userAddRate'),
3257
+ }
3258
+ return self.parse_trading_fee(data, market)
3259
+
3260
+ def parse_trading_fee(self, fee: dict, market: Market = None) -> TradingFeeInterface:
3261
+ #
3262
+ # {
3263
+ # "dailyUserVlm": [
3264
+ # {
3265
+ # "date": "2024-07-08",
3266
+ # "userCross": "0.0",
3267
+ # "userAdd": "0.0",
3268
+ # "exchange": "90597185.23639999"
3269
+ # }
3270
+ # ],
3271
+ # "feeSchedule": {
3272
+ # "cross": "0.00035",
3273
+ # "add": "0.0001",
3274
+ # "tiers": {
3275
+ # "vip": [
3276
+ # {
3277
+ # "ntlCutoff": "5000000.0",
3278
+ # "cross": "0.0003",
3279
+ # "add": "0.00005"
3280
+ # }
3281
+ # ],
3282
+ # "mm": [
3283
+ # {
3284
+ # "makerFractionCutoff": "0.005",
3285
+ # "add": "-0.00001"
3286
+ # }
3287
+ # ]
3288
+ # },
3289
+ # "referralDiscount": "0.04"
3290
+ # },
3291
+ # "userCrossRate": "0.00035",
3292
+ # "userAddRate": "0.0001",
3293
+ # "activeReferralDiscount": "0.0"
3294
+ # }
3295
+ #
3296
+ symbol = self.safe_symbol(None, market)
3297
+ return {
3298
+ 'info': fee,
3299
+ 'symbol': symbol,
3300
+ 'maker': self.safe_number(fee, 'userAddRate'),
3301
+ 'taker': self.safe_number(fee, 'userCrossRate'),
3302
+ 'percentage': None,
3303
+ 'tierBased': None,
3304
+ }
3305
+
3306
+ async def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[LedgerEntry]:
3307
+ """
3308
+ fetch the history of changes, actions done by the user or operations that altered the balance of the user
3309
+ :param str [code]: unified currency code
3310
+ :param int [since]: timestamp in ms of the earliest ledger entry
3311
+ :param int [limit]: max number of ledger entries to return
3312
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3313
+ :param int [params.until]: timestamp in ms of the latest ledger entry
3314
+ :param str [params.subAccountAddress]: sub account user address
3315
+ :returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger>`
3316
+ """
3317
+ await self.load_markets()
3318
+ userAddress = None
3319
+ userAddress, params = self.handle_public_address('fetchLedger', params)
3320
+ request: dict = {
3321
+ 'type': 'userNonFundingLedgerUpdates',
3322
+ 'user': userAddress,
3323
+ }
3324
+ if since is not None:
3325
+ request['startTime'] = since
3326
+ until = self.safe_integer(params, 'until')
3327
+ if until is not None:
3328
+ request['endTime'] = until
3329
+ params = self.omit(params, ['until'])
3330
+ response = await self.publicPostInfo(self.extend(request, params))
3331
+ #
3332
+ # [
3333
+ # {
3334
+ # "time":1724762307531,
3335
+ # "hash":"0x620a234a7e0eb7930575040f59482a01050058b0802163b4767bfd9033e77781",
3336
+ # "delta":{
3337
+ # "type":"accountClassTransfer",
3338
+ # "usdc":"50.0",
3339
+ # "toPerp":false
3340
+ # }
3341
+ # }
3342
+ # ]
3343
+ #
3344
+ return self.parse_ledger(response, None, since, limit)
3345
+
3346
+ def parse_ledger_entry(self, item: dict, currency: Currency = None) -> LedgerEntry:
3347
+ #
3348
+ # {
3349
+ # "time":1724762307531,
3350
+ # "hash":"0x620a234a7e0eb7930575040f59482a01050058b0802163b4767bfd9033e77781",
3351
+ # "delta":{
3352
+ # "type":"accountClassTransfer",
3353
+ # "usdc":"50.0",
3354
+ # "toPerp":false
3355
+ # }
3356
+ # }
3357
+ #
3358
+ timestamp = self.safe_integer(item, 'time')
3359
+ delta = self.safe_dict(item, 'delta', {})
3360
+ fee = None
3361
+ feeCost = self.safe_integer(delta, 'fee')
3362
+ if feeCost is not None:
3363
+ fee = {
3364
+ 'currency': 'USDC',
3365
+ 'cost': feeCost,
3366
+ }
3367
+ type = self.safe_string(delta, 'type')
3368
+ amount = self.safe_string(delta, 'usdc')
3369
+ return self.safe_ledger_entry({
3370
+ 'info': item,
3371
+ 'id': self.safe_string(item, 'hash'),
3372
+ 'direction': None,
3373
+ 'account': None,
3374
+ 'referenceAccount': self.safe_string(delta, 'user'),
3375
+ 'referenceId': self.safe_string(item, 'hash'),
3376
+ 'type': self.parse_ledger_entry_type(type),
3377
+ 'currency': None,
3378
+ 'amount': self.parse_number(amount),
3379
+ 'timestamp': timestamp,
3380
+ 'datetime': self.iso8601(timestamp),
3381
+ 'before': None,
3382
+ 'after': None,
3383
+ 'status': None,
3384
+ 'fee': fee,
3385
+ }, currency)
3386
+
3387
+ def parse_ledger_entry_type(self, type):
3388
+ ledgerType: dict = {
3389
+ 'internalTransfer': 'transfer',
3390
+ 'accountClassTransfer': 'transfer',
3391
+ }
3392
+ return self.safe_string(ledgerType, type, type)
3393
+
3394
+ async def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
3395
+ """
3396
+ fetch all deposits made to an account
3397
+ :param str code: unified currency code
3398
+ :param int [since]: the earliest time in ms to fetch deposits for
3399
+ :param int [limit]: the maximum number of deposits structures to retrieve
3400
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3401
+ :param int [params.until]: the latest time in ms to fetch withdrawals for
3402
+ :param str [params.subAccountAddress]: sub account user address
3403
+ :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
3404
+ """
3405
+ await self.load_markets()
3406
+ userAddress = None
3407
+ userAddress, params = self.handle_public_address('fetchDepositsWithdrawals', params)
3408
+ request: dict = {
3409
+ 'type': 'userNonFundingLedgerUpdates',
3410
+ 'user': userAddress,
2370
3411
  }
3412
+ if since is not None:
3413
+ request['startTime'] = since
3414
+ until = self.safe_integer(params, 'until')
3415
+ if until is not None:
3416
+ request['endTime'] = until
3417
+ params = self.omit(params, ['until'])
3418
+ response = await self.publicPostInfo(self.extend(request, params))
3419
+ #
3420
+ # [
3421
+ # {
3422
+ # "time":1724762307531,
3423
+ # "hash":"0x620a234a7e0eb7930575040f59482a01050058b0802163b4767bfd9033e77781",
3424
+ # "delta":{
3425
+ # "type":"accountClassTransfer",
3426
+ # "usdc":"50.0",
3427
+ # "toPerp":false
3428
+ # }
3429
+ # }
3430
+ # ]
3431
+ #
3432
+ records = self.extract_type_from_delta(response)
3433
+ deposits = self.filter_by_array(records, 'type', ['deposit'], False)
3434
+ return self.parse_transactions(deposits, None, since, limit)
3435
+
3436
+ async def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
3437
+ """
3438
+ fetch all withdrawals made from an account
3439
+ :param str code: unified currency code
3440
+ :param int [since]: the earliest time in ms to fetch withdrawals for
3441
+ :param int [limit]: the maximum number of withdrawals structures to retrieve
3442
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3443
+ :param int [params.until]: the latest time in ms to fetch withdrawals for
3444
+ :param str [params.subAccountAddress]: sub account user address
3445
+ :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
3446
+ """
3447
+ await self.load_markets()
3448
+ userAddress = None
3449
+ userAddress, params = self.handle_public_address('fetchDepositsWithdrawals', params)
3450
+ request: dict = {
3451
+ 'type': 'userNonFundingLedgerUpdates',
3452
+ 'user': userAddress,
3453
+ }
3454
+ if since is not None:
3455
+ request['startTime'] = since
3456
+ until = self.safe_integer(params, 'until')
3457
+ if until is not None:
3458
+ request['endTime'] = until
3459
+ params = self.omit(params, ['until'])
3460
+ response = await self.publicPostInfo(self.extend(request, params))
3461
+ #
3462
+ # [
3463
+ # {
3464
+ # "time":1724762307531,
3465
+ # "hash":"0x620a234a7e0eb7930575040f59482a01050058b0802163b4767bfd9033e77781",
3466
+ # "delta":{
3467
+ # "type":"accountClassTransfer",
3468
+ # "usdc":"50.0",
3469
+ # "toPerp":false
3470
+ # }
3471
+ # }
3472
+ # ]
3473
+ #
3474
+ records = self.extract_type_from_delta(response)
3475
+ withdrawals = self.filter_by_array(records, 'type', ['withdraw'], False)
3476
+ return self.parse_transactions(withdrawals, None, since, limit)
3477
+
3478
+ async def fetch_open_interests(self, symbols: Strings = None, params={}):
3479
+ """
3480
+ Retrieves the open interest for a list of symbols
3481
+ :param str[] [symbols]: Unified CCXT market symbol
3482
+ :param dict [params]: exchange specific parameters
3483
+ :returns dict} an open interest structure{@link https://docs.ccxt.com/#/?id=open-interest-structure:
3484
+ """
3485
+ await self.load_markets()
3486
+ symbols = self.market_symbols(symbols)
3487
+ swapMarkets = await self.fetch_swap_markets()
3488
+ return self.parse_open_interests(swapMarkets, symbols)
3489
+
3490
+ async def fetch_open_interest(self, symbol: str, params={}):
3491
+ """
3492
+ retrieves the open interest of a contract trading pair
3493
+ :param str symbol: unified CCXT market symbol
3494
+ :param dict [params]: exchange specific parameters
3495
+ :returns dict: an `open interest structure <https://docs.ccxt.com/#/?id=open-interest-structure>`
3496
+ """
3497
+ symbol = self.symbol(symbol)
3498
+ await self.load_markets()
3499
+ ois = await self.fetch_open_interests([symbol], params)
3500
+ return ois[symbol]
3501
+
3502
+ def parse_open_interest(self, interest, market: Market = None):
3503
+ #
3504
+ # {
3505
+ # szDecimals: '2',
3506
+ # name: 'HYPE',
3507
+ # maxLeverage: '3',
3508
+ # funding: '0.00014735',
3509
+ # openInterest: '14677900.74',
3510
+ # prevDayPx: '26.145',
3511
+ # dayNtlVlm: '299643445.12560016',
3512
+ # premium: '0.00081613',
3513
+ # oraclePx: '27.569',
3514
+ # markPx: '27.63',
3515
+ # midPx: '27.599',
3516
+ # impactPxs: ['27.5915', '27.6319'],
3517
+ # dayBaseVlm: '10790652.83',
3518
+ # baseId: 159
3519
+ # }
3520
+ #
3521
+ interest = self.safe_dict(interest, 'info', {})
3522
+ coin = self.safe_string(interest, 'name')
3523
+ marketId = None
3524
+ if coin is not None:
3525
+ marketId = self.coin_to_market_id(coin)
3526
+ return self.safe_open_interest({
3527
+ 'symbol': self.safe_symbol(marketId),
3528
+ 'openInterestAmount': self.safe_number(interest, 'openInterest'),
3529
+ 'openInterestValue': None,
3530
+ 'timestamp': None,
3531
+ 'datetime': None,
3532
+ 'info': interest,
3533
+ }, market)
3534
+
3535
+ async def fetch_funding_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
3536
+ """
3537
+ fetch the history of funding payments paid and received on self account
3538
+ :param str [symbol]: unified market symbol
3539
+ :param int [since]: the earliest time in ms to fetch funding history for
3540
+ :param int [limit]: the maximum number of funding history structures to retrieve
3541
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3542
+ :param str [params.subAccountAddress]: sub account user address
3543
+ :returns dict: a `funding history structure <https://docs.ccxt.com/#/?id=funding-history-structure>`
3544
+ """
3545
+ await self.load_markets()
3546
+ market = None
3547
+ if symbol is not None:
3548
+ market = self.market(symbol)
3549
+ userAddress = None
3550
+ userAddress, params = self.handle_public_address('fetchFundingHistory', params)
3551
+ request: dict = {
3552
+ 'user': userAddress,
3553
+ 'type': 'userFunding',
3554
+ }
3555
+ if since is not None:
3556
+ request['startTime'] = since
3557
+ until = self.safe_integer(params, 'until')
3558
+ params = self.omit(params, 'until')
3559
+ if until is not None:
3560
+ request['endTime'] = until
3561
+ response = await self.publicPostInfo(self.extend(request, params))
3562
+ #
3563
+ # [
3564
+ # {
3565
+ # "time": 1734026400057,
3566
+ # "hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
3567
+ # "delta": {
3568
+ # "type": "funding",
3569
+ # "coin": "SOL",
3570
+ # "usdc": "75.635093",
3571
+ # "szi": "-7375.9",
3572
+ # "fundingRate": "0.00004381",
3573
+ # "nSamples": null
3574
+ # }
3575
+ # }
3576
+ # ]
3577
+ #
3578
+ return self.parse_incomes(response, market, since, limit)
3579
+
3580
+ def parse_income(self, income, market: Market = None):
3581
+ #
3582
+ # {
3583
+ # "time": 1734026400057,
3584
+ # "hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
3585
+ # "delta": {
3586
+ # "type": "funding",
3587
+ # "coin": "SOL",
3588
+ # "usdc": "75.635093",
3589
+ # "szi": "-7375.9",
3590
+ # "fundingRate": "0.00004381",
3591
+ # "nSamples": null
3592
+ # }
3593
+ # }
3594
+ #
3595
+ id = self.safe_string(income, 'hash')
3596
+ timestamp = self.safe_integer(income, 'time')
3597
+ delta = self.safe_dict(income, 'delta')
3598
+ baseId = self.safe_string(delta, 'coin')
3599
+ marketSymbol = baseId + '/USDC:USDC'
3600
+ market = self.safe_market(marketSymbol)
3601
+ symbol = market['symbol']
3602
+ amount = self.safe_string(delta, 'usdc')
3603
+ code = self.safe_currency_code('USDC')
3604
+ rate = self.safe_number(delta, 'fundingRate')
3605
+ return {
3606
+ 'info': income,
3607
+ 'symbol': symbol,
3608
+ 'code': code,
3609
+ 'timestamp': timestamp,
3610
+ 'datetime': self.iso8601(timestamp),
3611
+ 'id': id,
3612
+ 'amount': self.parse_number(amount),
3613
+ 'rate': rate,
3614
+ }
3615
+
3616
+ async def reserve_request_weight(self, weight: Num, params={}) -> dict:
3617
+ """
3618
+ Instead of trading to increase the address based rate limits, self action allows reserving additional actions for 0.0005 USDC per request. The cost is paid from the Perps balance.
3619
+ :param number weight: the weight to reserve, 1 weight = 1 action, 0.0005 USDC per action
3620
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3621
+ :returns dict: a response object
3622
+ """
3623
+ nonce = self.milliseconds()
3624
+ request: dict = {
3625
+ 'nonce': nonce,
3626
+ }
3627
+ action: dict = {
3628
+ 'type': 'reserveRequestWeight',
3629
+ 'weight': weight,
3630
+ }
3631
+ signature = self.sign_l1_action(action, nonce)
3632
+ request['action'] = action
3633
+ request['signature'] = signature
3634
+ response = await self.privatePostExchange(self.extend(request, params))
3635
+ return response
3636
+
3637
+ def extract_type_from_delta(self, data=[]):
3638
+ records = []
3639
+ for i in range(0, len(data)):
3640
+ record = data[i]
3641
+ record['type'] = record['delta']['type']
3642
+ records.append(record)
3643
+ return records
2371
3644
 
2372
3645
  def format_vault_address(self, address: Str = None):
2373
3646
  if address is None:
@@ -2378,7 +3651,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2378
3651
 
2379
3652
  def handle_public_address(self, methodName: str, params: dict):
2380
3653
  userAux = None
2381
- userAux, params = self.handle_option_and_params(params, methodName, 'user')
3654
+ userAux, params = self.handle_option_and_params_2(params, methodName, 'user', 'subAccountAddress')
2382
3655
  user = userAux
2383
3656
  user, params = self.handle_option_and_params(params, methodName, 'address', userAux)
2384
3657
  if (user is not None) and (user != ''):
@@ -2388,9 +3661,9 @@ class hyperliquid(Exchange, ImplicitAPI):
2388
3661
  raise ArgumentsRequired(self.id + ' ' + methodName + '() requires a user parameter inside \'params\' or the wallet address set')
2389
3662
 
2390
3663
  def coin_to_market_id(self, coin: Str):
2391
- if coin.find('/') > -1:
3664
+ if coin.find('/') > -1 or coin.find('@') > -1:
2392
3665
  return coin # spot
2393
- return coin + '/USDC:USDC'
3666
+ return self.safe_currency_code(coin) + '/USDC:USDC'
2394
3667
 
2395
3668
  def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
2396
3669
  if not response:
@@ -2401,17 +3674,27 @@ class hyperliquid(Exchange, ImplicitAPI):
2401
3674
  # status: 'ok',
2402
3675
  # response: {type: 'order', data: {statuses: [{error: 'Insufficient margin to place order. asset=4'}]}}
2403
3676
  # }
3677
+ # {"status":"ok","response":{"type":"order","data":{"statuses":[{"error":"Insufficient margin to place order. asset=84"}]}}}
3678
+ #
3679
+ # {"status":"unknownOid"}
2404
3680
  #
2405
3681
  status = self.safe_string(response, 'status', '')
3682
+ error = self.safe_string(response, 'error')
2406
3683
  message = None
2407
3684
  if status == 'err':
2408
3685
  message = self.safe_string(response, 'response')
3686
+ elif status == 'unknownOid':
3687
+ raise OrderNotFound(self.id + ' ' + body) # {"status":"unknownOid"}
3688
+ elif error is not None:
3689
+ message = error
2409
3690
  else:
2410
3691
  responsePayload = self.safe_dict(response, 'response', {})
2411
3692
  data = self.safe_dict(responsePayload, 'data', {})
2412
3693
  statuses = self.safe_list(data, 'statuses', [])
2413
- firstStatus = self.safe_dict(statuses, 0)
2414
- message = self.safe_string(firstStatus, 'error')
3694
+ for i in range(0, len(statuses)):
3695
+ message = self.safe_string(statuses[i], 'error')
3696
+ if message is not None:
3697
+ break
2415
3698
  feedback = self.id + ' ' + body
2416
3699
  nonEmptyMessage = ((message is not None) and (message != ''))
2417
3700
  if nonEmptyMessage:
@@ -2429,3 +3712,32 @@ class hyperliquid(Exchange, ImplicitAPI):
2429
3712
  }
2430
3713
  body = self.json(params)
2431
3714
  return {'url': url, 'method': method, 'body': body, 'headers': headers}
3715
+
3716
+ def calculate_rate_limiter_cost(self, api, method, path, params, config={}):
3717
+ if ('byType' in config) and ('type' in params):
3718
+ type = params['type']
3719
+ byType = config['byType']
3720
+ if type in byType:
3721
+ return byType[type]
3722
+ return self.safe_value(config, 'cost', 1)
3723
+
3724
+ def parse_create_edit_order_args(self, id: Str, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
3725
+ market = self.market(symbol)
3726
+ vaultAddress = None
3727
+ vaultAddress, params = self.handle_option_and_params_2(params, 'createOrder', 'vaultAddress', 'subAccountAddress')
3728
+ vaultAddress = self.format_vault_address(vaultAddress)
3729
+ symbol = market['symbol']
3730
+ order = {
3731
+ 'symbol': symbol,
3732
+ 'type': type,
3733
+ 'side': side,
3734
+ 'amount': amount,
3735
+ 'price': price,
3736
+ 'params': params,
3737
+ }
3738
+ globalParams = {}
3739
+ if vaultAddress is not None:
3740
+ globalParams['vaultAddress'] = vaultAddress
3741
+ if id is not None:
3742
+ order['id'] = id
3743
+ return [order, globalParams]