ccxt-ir 4.3.46.0.3__py2.py3-none-any.whl → 4.5.1__py2.py3-none-any.whl

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