async-hyperliquid 1.1.0__tar.gz → 1.2.1__tar.gz
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.
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/CHANGELOG.md +27 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/PKG-INFO +2 -2
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/README.md +1 -1
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/README.md +24 -0
- async_hyperliquid-1.2.1/benchmarks/fast_cancel.py +178 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/coin-name-mapping.md +7 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/howto/markets.rst +2 -1
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/howto/orders.rst +5 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/coin-name-mapping.po +13 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/howto/markets.po +4 -2
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/howto/orders.po +12 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/pyproject.toml +1 -1
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/_internal/encoding.py +7 -3
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/_internal/metadata.py +9 -1
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/client.py +32 -8
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/exchange.py +11 -1
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/info.py +29 -7
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/types/exchange.py +2 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/LICENSE +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/__init__.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/hotpath.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/live/__init__.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/live/models.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/live/pacing.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/live/preflight.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/live/providers.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/live/reporting.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/live/results.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/live/runner.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/live/workload.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/live_exchange.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/results/20260805T192621Z/cancel-id-latency.png +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/results/20260805T192621Z/cancel-id-latency.svg +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/results/20260805T192621Z/report.json +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/results/20260805T192621Z/samples.csv +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/signing.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/_static/language-switcher.css +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/_templates/sidebar/language-switcher.html +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/conf.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/howto/index.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/howto/info-queries.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/howto/lifecycle-reconciliation.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/howto/routing.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/howto/transfers-administration.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/index.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/introduction/index.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/introduction/installation.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/introduction/quickstart.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/howto/index.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/howto/info-queries.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/howto/lifecycle-reconciliation.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/howto/routing.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/howto/transfers-administration.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/index.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/introduction/index.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/introduction/installation.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/introduction/quickstart.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/migration-0.5-to-1.0.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/project/about.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/project/benchmarks.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/project/index.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/project/license.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/reference/async-hyperliquid.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/reference/errors.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/reference/exchange-client.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/reference/index.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/reference/info-client.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/reference/response-types.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/reference/types.po +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/migration-0.5-to-1.0.md +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/project/about.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/project/benchmarks.md +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/project/index.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/project/license.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/reference/async-hyperliquid.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/reference/errors.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/reference/exchange-client.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/reference/index.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/reference/info-client.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/reference/response-types.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/reference/types.rst +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/__init__.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/_internal/__init__.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/_internal/exchange.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/_internal/http.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/_internal/info.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/_internal/signing.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/constants.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/errors.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/py.typed +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/types/__init__.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/types/common.py +0 -0
- {async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/types/info.py +0 -0
|
@@ -5,6 +5,33 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.2.1] - 2026-11-03
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Accept `sz=0` in order and modify requests; it is encoded as `"0"` and left to
|
|
13
|
+
the exchange to validate. Negative sizes are still rejected, and `place_twap()`
|
|
14
|
+
still requires a positive size.
|
|
15
|
+
- Reload metadata once when a lookup misses the cached snapshot, so markets
|
|
16
|
+
listed after the first load resolve without `refresh_metadata()`. A failed
|
|
17
|
+
reload keeps the last snapshot and the lookup still raises `ValueError`.
|
|
18
|
+
- Reject non-finite or non-positive order prices with
|
|
19
|
+
`price must be finite and greater than zero`; a positive price that rounds to
|
|
20
|
+
zero now reports `order price is below market precision`.
|
|
21
|
+
- Market orders raise `ProtocolError` when the mid price is not finite and
|
|
22
|
+
positive, instead of reporting an invalid order price.
|
|
23
|
+
|
|
24
|
+
## [1.2.0] - 2026-11-03
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- Add `fast=True` to `cancel_order()`, `cancel_orders()`, `cancel_by_cloid()`, and
|
|
29
|
+
`cancel_orders_by_cloid()` to send Hyperliquid's fast-cancel `f` flag; the flag
|
|
30
|
+
is omitted when `fast=False`. The exchange rejects fast cancels of trigger
|
|
31
|
+
orders per status, so `fast` stays opt-in.
|
|
32
|
+
- Add testnet integration coverage for fast cancels and a diagnostic
|
|
33
|
+
`benchmarks/fast_cancel.py` comparing fast and non-fast cancel latency.
|
|
34
|
+
|
|
8
35
|
## [1.1.0] - 2026-09-14
|
|
9
36
|
|
|
10
37
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: async-hyperliquid
|
|
3
|
-
Version: 1.1
|
|
3
|
+
Version: 1.2.1
|
|
4
4
|
Summary: Async Hyperliquid client using aiohttp
|
|
5
5
|
Keywords: dex,hyperliquid,async,aiohttp,trading,cryptocurrency,defi
|
|
6
6
|
Author: Yuki
|
|
@@ -36,7 +36,7 @@ Description-Content-Type: text/markdown
|
|
|
36
36
|
|
|
37
37
|
[](https://github.com/owlyfi/async-hyperliquid/actions/workflows/ci.yml)
|
|
38
38
|
[](https://async-hyperliquid.readthedocs.io/en/latest/)
|
|
39
|
-
[](https://pypi.org/project/async-hyperliquid/)
|
|
40
40
|
|
|
41
41
|
`async-hyperliquid` is a typed, asynchronous Python client for the Hyperliquid
|
|
42
42
|
REST API. It provides credential-free market and account queries through
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/owlyfi/async-hyperliquid/actions/workflows/ci.yml)
|
|
4
4
|
[](https://async-hyperliquid.readthedocs.io/en/latest/)
|
|
5
|
-
[](https://pypi.org/project/async-hyperliquid/)
|
|
6
6
|
|
|
7
7
|
`async-hyperliquid` is a typed, asynchronous Python client for the Hyperliquid
|
|
8
8
|
REST API. It provides credential-free market and account queries through
|
|
@@ -207,6 +207,30 @@ Artifacts: [sanitized JSON](results/20260805T192621Z/report.json), [sample CSV](
|
|
|
207
207
|
These measurements describe one testnet run, not exchange capacity. Shared-IP traffic, geography, testnet load, dependency versions, and network conditions can change results.
|
|
208
208
|
<!-- live-exchange-benchmark:detail:end -->
|
|
209
209
|
|
|
210
|
+
## Fast-cancel benchmark
|
|
211
|
+
|
|
212
|
+
`benchmarks/fast_cancel.py` compares single-order `cancel_order()` latency with
|
|
213
|
+
`fast=False` and `fast=True` on testnet. It uses the same `.env.local`
|
|
214
|
+
credentials and safety checks as the live Exchange benchmark (testnet only,
|
|
215
|
+
`HL_SK` must derive `HL_AK`, roles validated before any order).
|
|
216
|
+
|
|
217
|
+
Each round places `--orders` resting BTC ALO buys at `mid * 0.9` (about 12 USDC
|
|
218
|
+
each) in one batch, then cancels them sequentially, alternating the two methods;
|
|
219
|
+
the method that goes first swaps every round. Only the cancel request is timed,
|
|
220
|
+
pacing sleeps excluded. Any non-success cancel aborts the run, and remaining
|
|
221
|
+
orders are cancelled on exit.
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
uv run --frozen --group benchmark python benchmarks/fast_cancel.py \
|
|
225
|
+
--rounds 30 --warmups 3 --orders 10
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
The output is a plain-text table of n, mean, p50, p90, p99, min and max in ms.
|
|
229
|
+
Hyperliquid currently documents no effect for `fast` other than rejecting
|
|
230
|
+
trigger orders, so the two methods are expected to match today; the benchmark
|
|
231
|
+
is a baseline for the future mempool-priority upgrade. It is diagnostic only
|
|
232
|
+
and does not produce publishable artifacts.
|
|
233
|
+
|
|
210
234
|
## Signing benchmark
|
|
211
235
|
|
|
212
236
|
This benchmark compares Hyperliquid signing and order-payload construction in
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""Compare single-order cancel latency with fast=False and fast=True on testnet.
|
|
2
|
+
|
|
3
|
+
Each round places ``--orders`` resting BTC ALO buys far below mid in one batch,
|
|
4
|
+
then cancels them one at a time, alternating fast=False / fast=True. The method
|
|
5
|
+
that goes first swaps every round. Only the cancel request itself is timed;
|
|
6
|
+
pacing sleeps are excluded. Any non-success cancel invalidates the run.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import asyncio
|
|
13
|
+
import math
|
|
14
|
+
import os
|
|
15
|
+
import statistics
|
|
16
|
+
import sys
|
|
17
|
+
from collections.abc import Sequence
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from time import perf_counter_ns
|
|
20
|
+
|
|
21
|
+
if __package__ in (None, ""):
|
|
22
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
23
|
+
|
|
24
|
+
from async_hyperliquid import AsyncHyperliquid
|
|
25
|
+
from async_hyperliquid.types import (
|
|
26
|
+
CancelOrder,
|
|
27
|
+
CancelOrderResponse,
|
|
28
|
+
Network,
|
|
29
|
+
PlaceOrderRequest,
|
|
30
|
+
TimeInForce,
|
|
31
|
+
limit_order_type,
|
|
32
|
+
)
|
|
33
|
+
from benchmarks.live.models import MIN_INTERVAL_NS, BenchmarkFailure
|
|
34
|
+
from benchmarks.live.pacing import WeightedPacer
|
|
35
|
+
from benchmarks.live.preflight import Credentials, validate_roles
|
|
36
|
+
|
|
37
|
+
COIN = "BTC"
|
|
38
|
+
NOTIONAL_USDC = 12
|
|
39
|
+
PRICE_RATIO = 0.9
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
43
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
44
|
+
parser.add_argument("--rounds", type=int, default=30)
|
|
45
|
+
parser.add_argument("--warmups", type=int, default=3)
|
|
46
|
+
parser.add_argument("--orders", type=int, default=10, help="even, per round")
|
|
47
|
+
parser.add_argument("--interval-ms", type=int, default=250)
|
|
48
|
+
args = parser.parse_args(argv)
|
|
49
|
+
if args.rounds < 1 or args.warmups < 0:
|
|
50
|
+
parser.error("--rounds must be positive and --warmups non-negative")
|
|
51
|
+
if args.orders < 2 or args.orders % 2:
|
|
52
|
+
parser.error("--orders must be an even number >= 2")
|
|
53
|
+
if args.interval_ms * 1_000_000 < MIN_INTERVAL_NS:
|
|
54
|
+
parser.error(f"--interval-ms must be at least {MIN_INTERVAL_NS // 1_000_000}")
|
|
55
|
+
return args
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def fast_plan(round_index: int, count: int) -> tuple[bool, ...]:
|
|
59
|
+
"""Alternate fast flags; the first method swaps every round for fairness."""
|
|
60
|
+
first = round_index % 2 == 1
|
|
61
|
+
return tuple(first if i % 2 == 0 else not first for i in range(count))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def summarize(samples_ns: Sequence[int]) -> dict[str, float]:
|
|
65
|
+
ms = sorted(sample / 1_000_000 for sample in samples_ns)
|
|
66
|
+
cuts = statistics.quantiles(ms, n=100, method="inclusive")
|
|
67
|
+
return {
|
|
68
|
+
"n": len(ms),
|
|
69
|
+
"mean": statistics.fmean(ms),
|
|
70
|
+
"p50": statistics.median(ms),
|
|
71
|
+
"p90": cuts[89],
|
|
72
|
+
"p99": cuts[98],
|
|
73
|
+
"min": ms[0],
|
|
74
|
+
"max": ms[-1],
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def format_table(samples: dict[bool, list[int]]) -> str:
|
|
79
|
+
columns = ("n", "mean", "p50", "p90", "p99", "min", "max")
|
|
80
|
+
lines = [f"{'method':<12}" + "".join(f"{name:>10}" for name in columns)]
|
|
81
|
+
for fast in (False, True):
|
|
82
|
+
stats = summarize(samples[fast])
|
|
83
|
+
cells = "".join(
|
|
84
|
+
f"{stats[name]:>10d}" if name == "n" else f"{stats[name]:>10.2f}"
|
|
85
|
+
for name in columns
|
|
86
|
+
)
|
|
87
|
+
lines.append(f"{f'fast={fast}':<12}" + cells)
|
|
88
|
+
return "\n".join(lines) + "\n(latency in ms)"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _cancelled(response: CancelOrderResponse) -> bool:
|
|
92
|
+
return response["status"] == "ok" and response["response"]["data"]["statuses"] == [
|
|
93
|
+
"success"
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
async def _place_round(
|
|
98
|
+
client: AsyncHyperliquid, count: int, size_decimals: int
|
|
99
|
+
) -> list[int]:
|
|
100
|
+
px = await client.info.mid_price(COIN) * PRICE_RATIO
|
|
101
|
+
step = 10**size_decimals
|
|
102
|
+
sz = math.ceil(NOTIONAL_USDC / px * step) / step
|
|
103
|
+
order: PlaceOrderRequest = {
|
|
104
|
+
"coin": COIN,
|
|
105
|
+
"is_buy": True,
|
|
106
|
+
"sz": sz,
|
|
107
|
+
"px": px,
|
|
108
|
+
"is_market": False,
|
|
109
|
+
"order_type": limit_order_type(TimeInForce.ALO),
|
|
110
|
+
}
|
|
111
|
+
response = await client.place_orders([order.copy() for _ in range(count)])
|
|
112
|
+
if response["status"] != "ok":
|
|
113
|
+
raise BenchmarkFailure(f"placement failed: {response['response']}")
|
|
114
|
+
oids: list[int] = []
|
|
115
|
+
for status in response["response"]["data"]["statuses"]:
|
|
116
|
+
resting = status.get("resting") if isinstance(status, dict) else None
|
|
117
|
+
if not isinstance(resting, dict):
|
|
118
|
+
raise BenchmarkFailure(f"order did not rest: {status!r}")
|
|
119
|
+
oids.append(resting["oid"])
|
|
120
|
+
return oids
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
async def _run(args: argparse.Namespace, credentials: Credentials) -> str:
|
|
124
|
+
pacer = WeightedPacer(interval_ns=args.interval_ms * 1_000_000)
|
|
125
|
+
samples: dict[bool, list[int]] = {False: [], True: []}
|
|
126
|
+
async with AsyncHyperliquid(
|
|
127
|
+
credentials.master_address,
|
|
128
|
+
credentials.signing_key,
|
|
129
|
+
vault_address=credentials.subaccount_address,
|
|
130
|
+
network=Network.TESTNET,
|
|
131
|
+
) as client:
|
|
132
|
+
await client.info.refresh_metadata()
|
|
133
|
+
validate_roles(
|
|
134
|
+
credentials.master_address,
|
|
135
|
+
await client.info.user_role(credentials.api_wallet_address),
|
|
136
|
+
await client.info.user_role(credentials.subaccount_address),
|
|
137
|
+
)
|
|
138
|
+
size_decimals = await client.info.size_decimals(COIN)
|
|
139
|
+
pending: list[int] = []
|
|
140
|
+
try:
|
|
141
|
+
for round_index in range(args.warmups + args.rounds):
|
|
142
|
+
await pacer.wait(weight=1)
|
|
143
|
+
pending = await _place_round(client, args.orders, size_decimals)
|
|
144
|
+
plan = fast_plan(round_index, len(pending))
|
|
145
|
+
for oid, fast in zip(tuple(pending), plan, strict=True):
|
|
146
|
+
await pacer.wait(weight=1)
|
|
147
|
+
start = perf_counter_ns()
|
|
148
|
+
response = await client.cancel_order(
|
|
149
|
+
CancelOrder(COIN, oid), fast=fast
|
|
150
|
+
)
|
|
151
|
+
elapsed = perf_counter_ns() - start
|
|
152
|
+
if not _cancelled(response):
|
|
153
|
+
raise BenchmarkFailure(f"cancel failed: {response!r}")
|
|
154
|
+
pending.remove(oid)
|
|
155
|
+
if round_index >= args.warmups:
|
|
156
|
+
samples[fast].append(elapsed)
|
|
157
|
+
finally:
|
|
158
|
+
if pending:
|
|
159
|
+
await client.cancel_orders(tuple(CancelOrder(COIN, o) for o in pending))
|
|
160
|
+
return format_table(samples)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
164
|
+
args = parse_args(argv)
|
|
165
|
+
from dotenv import load_dotenv
|
|
166
|
+
|
|
167
|
+
load_dotenv(Path(".env.local"), override=False)
|
|
168
|
+
try:
|
|
169
|
+
credentials = Credentials.from_environ(os.environ)
|
|
170
|
+
print(asyncio.run(_run(args, credentials)))
|
|
171
|
+
except BenchmarkFailure as error:
|
|
172
|
+
print(f"fast-cancel benchmark failed: {error}", file=sys.stderr)
|
|
173
|
+
return 1
|
|
174
|
+
return 0
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
if __name__ == "__main__":
|
|
178
|
+
raise SystemExit(main())
|
|
@@ -13,6 +13,13 @@ The first lookup loads metadata. Concurrent cold lookups share one load;
|
|
|
13
13
|
`refresh_metadata()` explicitly replaces the last complete snapshot. A partial
|
|
14
14
|
or inconsistent response is rejected without publishing partial state.
|
|
15
15
|
|
|
16
|
+
A lookup for a coin missing from the cached snapshot reloads metadata once
|
|
17
|
+
before raising, so markets listed after the first load resolve without an
|
|
18
|
+
explicit refresh. If that reload fails, the last snapshot stays in place and the
|
|
19
|
+
lookup still raises `ValueError`. Misses are not cached: every lookup of an
|
|
20
|
+
unknown coin repeats the reload, so validate user input before it reaches a hot
|
|
21
|
+
path.
|
|
22
|
+
|
|
16
23
|
## Asset spaces
|
|
17
24
|
|
|
18
25
|
| Market | Asset ID |
|
|
@@ -17,7 +17,8 @@ helpers instead of deriving protocol offsets in application code.
|
|
|
17
17
|
The first metadata lookup loads one immutable snapshot. Concurrent cold
|
|
18
18
|
lookups share that load. ``refresh_metadata()`` atomically replaces the last
|
|
19
19
|
complete snapshot; malformed or inconsistent metadata is rejected before it is
|
|
20
|
-
published.
|
|
20
|
+
published. A lookup for a coin missing from the cached snapshot reloads
|
|
21
|
+
metadata once before raising ``ValueError``.
|
|
21
22
|
|
|
22
23
|
Coin forms
|
|
23
24
|
----------
|
|
@@ -406,6 +406,11 @@ Use ``CancelOrder(coin, oid)`` with ``cancel_order()`` or ``cancel_orders()``.
|
|
|
406
406
|
Use ``CancelByCloid(coin, cloid)`` with ``cancel_by_cloid()`` or
|
|
407
407
|
``cancel_orders_by_cloid()``. Empty batches are rejected locally.
|
|
408
408
|
|
|
409
|
+
All four cancel methods accept ``fast=True``, which sends Hyperliquid's ``f``
|
|
410
|
+
flag. The exchange rejects fast cancels that target trigger orders, and a future
|
|
411
|
+
network upgrade will prioritize fast cancels in the mempool. With the default
|
|
412
|
+
``fast=False`` the flag is omitted from the signed action.
|
|
413
|
+
|
|
409
414
|
``ModifyOrderRequest`` identifies the order by integer order ID or ``Cloid`` and
|
|
410
415
|
contains the complete replacement order. ``modify_order()`` returns a default
|
|
411
416
|
acknowledgement; ``modify_orders()`` returns per-order statuses.
|
|
@@ -63,6 +63,19 @@ msgstr ""
|
|
|
63
63
|
"首次查询会加载元数据。并发冷查询共享同一次加载;`refresh_metadata()` 会显式替换最近一次完整快照。"
|
|
64
64
|
"不完整或不一致的响应会被拒绝,且不会发布部分状态。"
|
|
65
65
|
|
|
66
|
+
#: ../../coin-name-mapping.md:16
|
|
67
|
+
msgid ""
|
|
68
|
+
"A lookup for a coin missing from the cached snapshot reloads metadata "
|
|
69
|
+
"once before raising, so markets listed after the first load resolve "
|
|
70
|
+
"without an explicit refresh. If that reload fails, the last snapshot stays "
|
|
71
|
+
"in place and the lookup still raises `ValueError`. Misses are not cached: "
|
|
72
|
+
"every lookup of an unknown coin repeats the reload, so validate user input "
|
|
73
|
+
"before it reaches a hot path."
|
|
74
|
+
msgstr ""
|
|
75
|
+
"若查询的 coin 不在缓存快照中,会先重新加载一次元数据再抛出错误,因此首次加载之后新上线的市场无需显式刷新即可解析。"
|
|
76
|
+
"如果这次重新加载失败,会保留上一次的快照,查询仍然抛出 `ValueError`。未命中的结果不会被缓存:"
|
|
77
|
+
"每次查询未知 coin 都会重新加载一次,因此请在用户输入进入热路径之前先做校验。"
|
|
78
|
+
|
|
66
79
|
#: ../../coin-name-mapping.md:16 667c70e0123949059329339edf3f8ca6
|
|
67
80
|
msgid "Asset spaces"
|
|
68
81
|
msgstr "资产编号空间"
|
{async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/howto/markets.po
RENAMED
|
@@ -34,10 +34,12 @@ msgid ""
|
|
|
34
34
|
"The first metadata lookup loads one immutable snapshot. Concurrent cold "
|
|
35
35
|
"lookups share that load. ``refresh_metadata()`` atomically replaces the "
|
|
36
36
|
"last complete snapshot; malformed or inconsistent metadata is rejected "
|
|
37
|
-
"before it is published."
|
|
37
|
+
"before it is published. A lookup for a coin missing from the cached "
|
|
38
|
+
"snapshot reloads metadata once before raising ``ValueError``."
|
|
38
39
|
msgstr ""
|
|
39
40
|
"首次元数据查询会加载一个不可变快照。并发的冷查询会共享该加载。``refresh_metadata()`` 会原子地替换最后一个完整快照;"
|
|
40
|
-
"
|
|
41
|
+
"格式错误或不一致的元数据会在发布前被拒绝。若查询的 coin 不在缓存快照中,会先重新加载一次元数据,"
|
|
42
|
+
"仍未命中才抛出 ``ValueError``。"
|
|
41
43
|
|
|
42
44
|
#: ../../howto/markets.rst:23 a74ae47aabbf4ba5817d21a500d064ae
|
|
43
45
|
msgid "Coin forms"
|
{async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/howto/orders.po
RENAMED
|
@@ -288,6 +288,18 @@ msgstr ""
|
|
|
288
288
|
"搭配使用。将 ``CancelByCloid(coin, cloid)`` 与 ``cancel_by_cloid()`` 或 "
|
|
289
289
|
"``cancel_orders_by_cloid()`` 搭配使用。空批次会在本地被拒绝。"
|
|
290
290
|
|
|
291
|
+
#: ../../howto/orders.rst:409
|
|
292
|
+
msgid ""
|
|
293
|
+
"All four cancel methods accept ``fast=True``, which sends Hyperliquid's "
|
|
294
|
+
"``f`` flag. The exchange rejects fast cancels that target trigger orders,"
|
|
295
|
+
" and a future network upgrade will prioritize fast cancels in the "
|
|
296
|
+
"mempool. With the default ``fast=False`` the flag is omitted from the "
|
|
297
|
+
"signed action."
|
|
298
|
+
msgstr ""
|
|
299
|
+
"四个撤单方法都接受 ``fast=True``,用于发送 Hyperliquid 的 ``f`` 标志。"
|
|
300
|
+
"交易所会拒绝针对触发单(trigger order)的快速撤单;未来的网络升级会在 mempool "
|
|
301
|
+
"中优先处理快速撤单。默认 ``fast=False`` 时,签名的 action 中不包含该标志。"
|
|
302
|
+
|
|
291
303
|
#: ../../howto/orders.rst:409 2815403bb6ba4974a7f7a2680ee41f54
|
|
292
304
|
msgid ""
|
|
293
305
|
"``ModifyOrderRequest`` identifies the order by integer order ID or "
|
{async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/_internal/encoding.py
RENAMED
|
@@ -19,8 +19,10 @@ def _round_size(value: float, size_decimals: int) -> float | int:
|
|
|
19
19
|
|
|
20
20
|
def _normalize_size(value: float, size_decimals: int) -> float | int:
|
|
21
21
|
size = float(value)
|
|
22
|
-
if not math.isfinite(size) or size
|
|
23
|
-
raise ValueError("size must be finite and
|
|
22
|
+
if not math.isfinite(size) or size < 0:
|
|
23
|
+
raise ValueError("size must be finite and non-negative")
|
|
24
|
+
if size == 0:
|
|
25
|
+
return 0
|
|
24
26
|
rounded = _round_size(size, size_decimals)
|
|
25
27
|
if rounded <= 0:
|
|
26
28
|
raise ValueError("order size is below market precision")
|
|
@@ -49,13 +51,15 @@ def _normalize_price(
|
|
|
49
51
|
value: float | str, *, max_decimals: int, is_outcome: bool
|
|
50
52
|
) -> float | int:
|
|
51
53
|
price = float(value)
|
|
54
|
+
if not math.isfinite(price) or price <= 0:
|
|
55
|
+
raise ValueError("price must be finite and greater than zero")
|
|
52
56
|
if is_outcome and not OUTCOME_MIN_PRICE <= price <= OUTCOME_MAX_PRICE:
|
|
53
57
|
raise ValueError("outcome price must be between 0.00001 and 0.99999 USDC")
|
|
54
58
|
rounded = _round_price(
|
|
55
59
|
price, OUTCOME_PRICE_DECIMALS if is_outcome else max_decimals
|
|
56
60
|
)
|
|
57
61
|
if rounded <= 0:
|
|
58
|
-
raise ValueError("order
|
|
62
|
+
raise ValueError("order price is below market precision")
|
|
59
63
|
return rounded
|
|
60
64
|
|
|
61
65
|
|
{async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/_internal/metadata.py
RENAMED
|
@@ -251,8 +251,16 @@ def _build_metadata(
|
|
|
251
251
|
)
|
|
252
252
|
|
|
253
253
|
|
|
254
|
+
def _is_outcome_coin(coin: str) -> bool:
|
|
255
|
+
return coin.startswith(("#", "+"))
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _is_known_coin(snapshot: _MetadataSnapshot, coin: str) -> bool:
|
|
259
|
+
return _is_outcome_coin(coin) or coin in snapshot.coin_by_alias
|
|
260
|
+
|
|
261
|
+
|
|
254
262
|
def _outcome_market_info(coin: str) -> _MarketInfo | None:
|
|
255
|
-
if not coin
|
|
263
|
+
if not _is_outcome_coin(coin):
|
|
256
264
|
return None
|
|
257
265
|
raw_encoding = coin[1:]
|
|
258
266
|
if not raw_encoding.isascii() or not raw_encoding.isdecimal():
|
|
@@ -57,6 +57,8 @@ def _coin_dex(coin: str) -> str:
|
|
|
57
57
|
def _market_price(
|
|
58
58
|
mid: float, *, is_buy: bool, slippage: float, is_outcome: bool
|
|
59
59
|
) -> float:
|
|
60
|
+
if not math.isfinite(mid) or mid <= 0:
|
|
61
|
+
raise ProtocolError("mid price must be finite and greater than zero")
|
|
60
62
|
price = mid * (1 + slippage if is_buy else 1 - slippage)
|
|
61
63
|
if not is_outcome:
|
|
62
64
|
return price
|
|
@@ -332,12 +334,22 @@ class AsyncHyperliquid:
|
|
|
332
334
|
)
|
|
333
335
|
|
|
334
336
|
async def cancel_order(
|
|
335
|
-
self,
|
|
337
|
+
self,
|
|
338
|
+
order: CancelOrder,
|
|
339
|
+
*,
|
|
340
|
+
fast: bool = False,
|
|
341
|
+
expires_after: int | None = None,
|
|
336
342
|
) -> CancelOrderResponse:
|
|
337
|
-
return await self.cancel_orders(
|
|
343
|
+
return await self.cancel_orders(
|
|
344
|
+
(order,), fast=fast, expires_after=expires_after
|
|
345
|
+
)
|
|
338
346
|
|
|
339
347
|
async def cancel_orders(
|
|
340
|
-
self,
|
|
348
|
+
self,
|
|
349
|
+
orders: Sequence[CancelOrder],
|
|
350
|
+
*,
|
|
351
|
+
fast: bool = False,
|
|
352
|
+
expires_after: int | None = None,
|
|
341
353
|
) -> CancelOrderResponse:
|
|
342
354
|
commands = tuple(orders)
|
|
343
355
|
if not commands:
|
|
@@ -350,16 +362,26 @@ class AsyncHyperliquid:
|
|
|
350
362
|
for order, market in zip(commands, markets, strict=True)
|
|
351
363
|
)
|
|
352
364
|
return await self._exchange._submit_cancels(
|
|
353
|
-
cancels, expires_after=expires_after
|
|
365
|
+
cancels, fast=fast, expires_after=expires_after
|
|
354
366
|
)
|
|
355
367
|
|
|
356
368
|
async def cancel_by_cloid(
|
|
357
|
-
self,
|
|
369
|
+
self,
|
|
370
|
+
order: CancelByCloid,
|
|
371
|
+
*,
|
|
372
|
+
fast: bool = False,
|
|
373
|
+
expires_after: int | None = None,
|
|
358
374
|
) -> CancelOrderResponse:
|
|
359
|
-
return await self.cancel_orders_by_cloid(
|
|
375
|
+
return await self.cancel_orders_by_cloid(
|
|
376
|
+
(order,), fast=fast, expires_after=expires_after
|
|
377
|
+
)
|
|
360
378
|
|
|
361
379
|
async def cancel_orders_by_cloid(
|
|
362
|
-
self,
|
|
380
|
+
self,
|
|
381
|
+
orders: Sequence[CancelByCloid],
|
|
382
|
+
*,
|
|
383
|
+
fast: bool = False,
|
|
384
|
+
expires_after: int | None = None,
|
|
363
385
|
) -> CancelOrderResponse:
|
|
364
386
|
commands = tuple(orders)
|
|
365
387
|
if not commands:
|
|
@@ -372,7 +394,7 @@ class AsyncHyperliquid:
|
|
|
372
394
|
for order, market in zip(commands, markets, strict=True)
|
|
373
395
|
)
|
|
374
396
|
return await self._exchange._submit_cloid_cancels(
|
|
375
|
-
cancels, expires_after=expires_after
|
|
397
|
+
cancels, fast=fast, expires_after=expires_after
|
|
376
398
|
)
|
|
377
399
|
|
|
378
400
|
@staticmethod
|
|
@@ -464,6 +486,8 @@ class AsyncHyperliquid:
|
|
|
464
486
|
if market.is_spot and reduce_only:
|
|
465
487
|
raise ValueError("spot orders cannot be reduce-only")
|
|
466
488
|
rounded_size = _normalize_size(size, market.size_decimals)
|
|
489
|
+
if rounded_size == 0:
|
|
490
|
+
raise ValueError("TWAP size must be greater than zero")
|
|
467
491
|
details: EncodedTwapDetails | None = None
|
|
468
492
|
if trigger_px is not None or stop_px is not None:
|
|
469
493
|
encoded_trigger_px = (
|
|
@@ -195,24 +195,34 @@ class ExchangeClient:
|
|
|
195
195
|
return await self._submit_action(action, "order", expires_after=expires_after)
|
|
196
196
|
|
|
197
197
|
async def _submit_cancels(
|
|
198
|
-
self,
|
|
198
|
+
self,
|
|
199
|
+
cancels: Sequence[EncodedCancel],
|
|
200
|
+
*,
|
|
201
|
+
fast: bool = False,
|
|
202
|
+
expires_after: int | None = None,
|
|
199
203
|
) -> CancelOrderResponse:
|
|
200
204
|
encoded = list(cancels)
|
|
201
205
|
if not encoded:
|
|
202
206
|
raise ValueError("orders must not be empty")
|
|
203
207
|
action = CancelAction(type="cancel", cancels=encoded)
|
|
208
|
+
if fast:
|
|
209
|
+
# Hyperliquid rejects actions hashed with f=false, so only send f=true.
|
|
210
|
+
action["f"] = True
|
|
204
211
|
return await self._submit_action(action, "cancel", expires_after=expires_after)
|
|
205
212
|
|
|
206
213
|
async def _submit_cloid_cancels(
|
|
207
214
|
self,
|
|
208
215
|
cancels: Sequence[EncodedCancelByCloid],
|
|
209
216
|
*,
|
|
217
|
+
fast: bool = False,
|
|
210
218
|
expires_after: int | None = None,
|
|
211
219
|
) -> CancelOrderResponse:
|
|
212
220
|
encoded = list(cancels)
|
|
213
221
|
if not encoded:
|
|
214
222
|
raise ValueError("orders must not be empty")
|
|
215
223
|
action = CancelByCloidAction(type="cancelByCloid", cancels=encoded)
|
|
224
|
+
if fast:
|
|
225
|
+
action["f"] = True
|
|
216
226
|
return await self._submit_action(action, "cancel", expires_after=expires_after)
|
|
217
227
|
|
|
218
228
|
async def _post_envelope(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import logging
|
|
2
3
|
from collections.abc import Sequence
|
|
3
4
|
from types import TracebackType
|
|
4
5
|
from typing import Literal, Self, cast, overload
|
|
@@ -23,9 +24,10 @@ from ._internal.metadata import (
|
|
|
23
24
|
_MarketInfo,
|
|
24
25
|
_MetadataSnapshot,
|
|
25
26
|
_build_metadata,
|
|
27
|
+
_is_known_coin,
|
|
26
28
|
_market_info,
|
|
27
29
|
)
|
|
28
|
-
from .errors import ProtocolError
|
|
30
|
+
from .errors import HyperliquidError, ProtocolError
|
|
29
31
|
from .types import CandleInterval, JsonObject, JsonValue, Network
|
|
30
32
|
from .types.info import (
|
|
31
33
|
AccountState,
|
|
@@ -73,6 +75,9 @@ from .types.info import (
|
|
|
73
75
|
)
|
|
74
76
|
|
|
75
77
|
|
|
78
|
+
logger = logging.getLogger(__name__)
|
|
79
|
+
|
|
80
|
+
|
|
76
81
|
class InfoClient:
|
|
77
82
|
"""Asynchronous, credential-free client for Hyperliquid Info endpoints."""
|
|
78
83
|
|
|
@@ -506,15 +511,32 @@ class InfoClient:
|
|
|
506
511
|
snapshot = await self._load_metadata()
|
|
507
512
|
self._metadata = snapshot
|
|
508
513
|
|
|
509
|
-
async def
|
|
514
|
+
async def _metadata_for(self, *coins: str) -> _MetadataSnapshot:
|
|
515
|
+
cold = self._metadata is None
|
|
510
516
|
snapshot = await self._ensure_metadata()
|
|
517
|
+
if cold or all(_is_known_coin(snapshot, coin) for coin in coins):
|
|
518
|
+
return snapshot
|
|
519
|
+
async with self._get_metadata_lock():
|
|
520
|
+
# Concurrent misses on the same stale snapshot share one reload.
|
|
521
|
+
if self._metadata is snapshot:
|
|
522
|
+
try:
|
|
523
|
+
self._metadata = await self._load_metadata()
|
|
524
|
+
except (HyperliquidError, TimeoutError):
|
|
525
|
+
# Keep the miss a ValueError raised by the caller's lookup.
|
|
526
|
+
logger.debug("metadata refresh after a miss failed", exc_info=True)
|
|
527
|
+
return snapshot
|
|
528
|
+
# Refreshes only replace a published snapshot, never clear it.
|
|
529
|
+
return cast(_MetadataSnapshot, self._metadata)
|
|
530
|
+
|
|
531
|
+
async def coin_name(self, coin: str) -> str:
|
|
532
|
+
snapshot = await self._metadata_for(coin)
|
|
511
533
|
name = snapshot.coin_by_alias.get(coin)
|
|
512
534
|
if name is None:
|
|
513
535
|
raise ValueError(f"unknown coin: {coin}")
|
|
514
536
|
return name
|
|
515
537
|
|
|
516
538
|
async def coin_symbol(self, coin: str) -> str:
|
|
517
|
-
snapshot = await self.
|
|
539
|
+
snapshot = await self._metadata_for(coin)
|
|
518
540
|
name = snapshot.coin_by_alias.get(coin)
|
|
519
541
|
if name is None:
|
|
520
542
|
raise ValueError(f"unknown coin: {coin}")
|
|
@@ -530,14 +552,14 @@ class InfoClient:
|
|
|
530
552
|
return (await self._market_info(coin)).size_decimals
|
|
531
553
|
|
|
532
554
|
async def _market_info(self, coin: str) -> _MarketInfo:
|
|
533
|
-
return _market_info(await self.
|
|
555
|
+
return _market_info(await self._metadata_for(coin), coin)
|
|
534
556
|
|
|
535
557
|
async def _market_infos(self, coins: Sequence[str]) -> tuple[_MarketInfo, ...]:
|
|
536
|
-
snapshot = await self.
|
|
558
|
+
snapshot = await self._metadata_for(*coins)
|
|
537
559
|
return tuple(_market_info(snapshot, coin) for coin in coins)
|
|
538
560
|
|
|
539
561
|
async def spot_token_metadata(self, coin: str) -> SpotToken:
|
|
540
|
-
snapshot = await self.
|
|
562
|
+
snapshot = await self._metadata_for(coin)
|
|
541
563
|
name = snapshot.coin_by_alias.get(coin)
|
|
542
564
|
token = None if name is None else snapshot.spot_token_by_coin.get(name)
|
|
543
565
|
if token is None:
|
|
@@ -548,7 +570,7 @@ class InfoClient:
|
|
|
548
570
|
return (await self.spot_token_metadata(coin))["tokenId"]
|
|
549
571
|
|
|
550
572
|
async def mark_price(self, coin: str) -> float:
|
|
551
|
-
snapshot = await self.
|
|
573
|
+
snapshot = await self._metadata_for(coin)
|
|
552
574
|
name = snapshot.coin_by_alias.get(coin)
|
|
553
575
|
if name is None:
|
|
554
576
|
raise ValueError(f"unknown coin: {coin}")
|
|
@@ -144,6 +144,7 @@ class EncodedCancel(TypedDict):
|
|
|
144
144
|
class CancelAction(TypedDict):
|
|
145
145
|
type: Literal["cancel"]
|
|
146
146
|
cancels: list[EncodedCancel]
|
|
147
|
+
f: NotRequired[Literal[True]]
|
|
147
148
|
|
|
148
149
|
|
|
149
150
|
class EncodedCancelByCloid(TypedDict):
|
|
@@ -154,6 +155,7 @@ class EncodedCancelByCloid(TypedDict):
|
|
|
154
155
|
class CancelByCloidAction(TypedDict):
|
|
155
156
|
type: Literal["cancelByCloid"]
|
|
156
157
|
cancels: list[EncodedCancelByCloid]
|
|
158
|
+
f: NotRequired[Literal[True]]
|
|
157
159
|
|
|
158
160
|
|
|
159
161
|
class EncodedModify(TypedDict):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/results/20260805T192621Z/report.json
RENAMED
|
File without changes
|
{async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/benchmarks/results/20260805T192621Z/samples.csv
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/_templates/sidebar/language-switcher.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/howto/index.po
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/howto/routing.po
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/project/about.po
RENAMED
|
File without changes
|
|
File without changes
|
{async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/project/index.po
RENAMED
|
File without changes
|
{async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/project/license.po
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/reference/index.po
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/docs/locale/zh_CN/LC_MESSAGES/reference/types.po
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/_internal/__init__.py
RENAMED
|
File without changes
|
{async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/_internal/exchange.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{async_hyperliquid-1.1.0 → async_hyperliquid-1.2.1}/src/async_hyperliquid/_internal/signing.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|