Are you the author? Sign in to claim
Non-custodial Hyperliquid execution + tracking CLI for autonomous LLM agents. Signs EIP-712 actions with an agent wallet
A single-binary Go CLI: the non-custodial execution + tracking layer an autonomous agent (OpenClaw) drives to trade and manage a Hyperliquid portfolio.
Codename ref: Snow Crash — the Deliverator. Fast, precise, no-BS.
OpenClaw is the reference autonomous trading agent that drives Deliverator; Deliverator itself is agent-agnostic — any LLM agent or script that speaks the CLI contract (
deliverator tools/AGENTS.md) can drive it.
Deliverator is the safe harness you put between an autonomous agent and a Hyperliquid account. It owns the hard, dangerous parts once — EIP-712 signing, nonce management, precision rounding, builder attach, risk caps, rate-limit pacing — and exposes them as a disciplined, machine-parseable CLI. The agent decides what to trade; Deliverator makes sure the how is correct, bounded, and auditable.
Because it signs with a Hyperliquid agent/API wallet that physically cannot withdraw, the worst a confused or compromised agent can do is place bad trades — never drain funds. That non-custodial guarantee is the whole point.
No backend. No telemetry. scp the binary to a box and go.
⚠️ This places real orders with real money on a live exchange.
initandonboardconfigure mainnet — switch totestnet(config set network testnet, or the console's network panel) for a paper-funds dry run first. The non-custodial key means a bug can't drain your account, but it can still place trades you didn't intend. Provided under the MIT license, with no warranty (LICENSE). You are responsible for what your agent does.
Deliverator is the execution and safety layer for autonomous agents — particularly LLM-driven ones — that trade on Hyperliquid. The intro above covers what it does; this section is about when to reach for it instead of the alternatives.
When an LLM is in the decision loop, a handful of failure modes recur:
Deliverator is built to absorb exactly these. Most general-purpose Hyperliquid tooling wasn't.
| Category | Examples | Primary strength | Best for | Limitations for autonomous agents |
|---|---|---|---|---|
| Low-level SDKs | Official hyperliquid-python-sdk | Full control, lightweight, official | Custom development & deep integration | No built-in safety, risk engine, or agent ergonomics |
| Persistent bot frameworks | Hummingbot (Hyperliquid connector) | Mature strategy engine + risk controls | Rule-based / market-making bots | Heavier for dynamic, decision-making agent loops |
| Human + scripting CLIs | hyperliquid-cli (TS), similar community CLIs | Nice TUI, real-time monitoring, JSON output | Human traders + light automation | Limited portfolio-level risk enforcement & agent contract |
| Agent execution layer | Deliverator | Non-custodial safety + machine-native contract | LLM agents & autonomous trading loops | Newer project (early 2026) |
Deliverator isn't trying to replace the Hyperliquid SDK or Hummingbot. It occupies a specific niche: a safe execution harness purpose-built for agents that trade real capital without supervision.
The caller is an LLM, not a human. So Deliverator is:
$?), never relies on stdout text.cloid. The exchange does not reject a duplicate cloid (a blind resend places a second order), so the defined timeout-retry protocol — check order status by cloid before resubmitting — is what actually prevents double-fills.markets, schema, tools, version let the agent discover capability at runtime.The agent contract is the product. See TOOLS.md (also deliverator tools).
Homebrew (macOS/Linux):
brew install --cask erickuhn19/tap/deliverator
Or with Go:
go install github.com/erickuhn19/deliverator@latest
# or build from source:
go build -o deliverator .
Building from source requires Go 1.25+. macOS/Linux (uses flock + the OS keychain).
Every release ships sha256 checksums.txt and SLSA build-provenance attestations —
signed proof that each binary was built by this repo's CI from a specific commit (so a
tampered or substituted binary won't verify). To check a downloaded archive:
gh attestation verify deliverator_<version>_<os>_<arch>.tar.gz --repo erickuhn19/deliverator
brew install enforces integrity automatically via the cask's pinned sha256.
Easiest — guided setup (creates your account via the referral link for a fee discount, then takes your API wallet key and configures everything):
deliverator onboard
Or set up manually:
# 1. Generate a fresh agent key locally (stored in the OS keychain) + a config template.
deliverator init
# → prints the agent ADDRESS and the approval steps.
# 2. In the Hyperliquid web UI, approve that agent address (API → approve agent).
# Then point Deliverator at your MASTER address (the query target):
deliverator config set wallet.master_address 0xYOURMASTER...
# 3. Preflight.
deliverator connect # key, account, network, clock skew, API, meta age
# 4. Read state (one call = full snapshot).
deliverator portfolio --json
# 5. Place a bounded, idempotent order (preview first).
deliverator buy BTC 0.01 --limit 64000 --alo --dry-run
deliverator buy BTC 0.01 --limit 64000 --alo --cloid 0x... # for real
# 6. Safety.
deliverator dms set 60 # arm the dead-man's switch (refresh via cron: dms heartbeat)
deliverator halt on # emergency stop — rejects all new orders
deliverator panic --yes # cancel-all + flatten-all (works during a halt)
init and onboard ship the opinionated default: mainnet. For a
paper-funds dry run first, switch with deliverator config set network testnet
(or the console's network panel) — changing the network warns loudly either way.
Every command emits one JSON envelope:
{
"schema": "v1",
"ok": true,
"ts": 1750000000000,
"cmd": "buy",
"data": { "cloid": "0x..", "status": "resting", "oid": 123, "coin": "BTC", "size": "0.01", "limit_px": "64000" },
"error": null,
"warnings": ["size rounded 0.0123456 -> 0.0123"],
"meta": { "network": "testnet", "account": "main" }
}
Prices and sizes are always strings. On failure, ok=false, data=null,
and error is populated with {code, category, message, retryable, retry_after_ms, hint}
plus, on write failures, cloids — the client order id(s) the action was signed
with (all legs for a batch/grid), even when auto-generated — so the retry
protocol below is always runnable.
Two optional read-health fields (additive, omitted when clean) mark a tolerant
read that succeeded with incomplete data: degraded_dexs: ["<dex>"] — a
HIP-3 sub-dex's state/orders were unreadable this call, so anything held there
is missing from the response, not gone (never treat that absence as
closed/flat; retry) — and truncated: true — a paged history read (fills --since, funding, ledger) stopped with more rows available (its safety
cap, or a same-millisecond burst larger than one exchange page). Both always
come with a matching warning.
| Code | Meaning | Agent action |
|---|---|---|
| 0 | success | proceed |
| 10 | validation (bad args / unknown coin) | fix inputs |
| 11 | precision rejected (--strict) | re-round and retry |
| 20 | risk-rejected (cap/allowlist/limit-only/leverage) | respect the cap |
| 21 | halted | stop trading |
| 30 | auth/key error | operator fixes the key |
| 40 | network/unreachable | retry w/ backoff |
| 41 | rate-limited | back off (retry_after_ms) |
| 42 | timeout (outcome unknown) | run the retry protocol — don't blind-resubmit |
| 50 | exchange-rejected | read message, adjust |
| 60 | partial fill | inspect fills, decide |
| 70 | clock skew outside nonce window | fix the clock |
On exit 42, run deliverator order status --cloid <id>. If the order exists
→ it landed, don't resend. If absent → resubmit the same cloid. This is the
#1 way naive agents double-fill.
The cloid(s) to check are carried in the failure envelope's error.cloids.
Exit 42 covers every failure after the signed action may have reached the
exchange (timeouts, connection resets, EOFs, unreadable responses, and gateway
5xx pages from an edge/proxy in front of the API); a failure before anything
was sent (connection refused, DNS) is exit 40 — retrying that is safe. Exit 50
is reserved for a definitive exchange rejection.
The exchange can take ~1–2s to index a new order by cloid, so a status check
immediately after a timeout may report "absent" for an order that actually
landed. Wait briefly and re-check (or query by --oid) before resubmitting.
order status --cloid only reports "absent" from a live open-orders sweep that
read every dex successfully; if the sweep is degraded (e.g. a sub-dex 429)
and the order isn't found on a readable dex, it returns a retryable exit 40
(order_status_unconfirmed) instead of a confident answer — re-check when reads
recover, don't resubmit.
| Guard | Config | Behavior |
|---|---|---|
| Coin allowlist | automation.allowed_coins | reject non-listed (20); empty = allow all |
| Max order notional | risk.max_order_notional_usd | reject (20) |
| Max position notional | risk.max_position_notional_usd | reject (20); per-coin, counts position + resting non-reduce-only orders (worst-case adds) |
| Min order notional | risk.min_order_notional_usd | reject sub-minimum orders pre-flight (10); default $10, mirrors HL's floor |
| Limit-only | automation.limit_only | block market orders and --trigger-market orders (they execute as market when fired) (20); exits/reduce-only exempt |
| Max leverage | risk.max_leverage | cap leverage changes (20) |
| Account leverage | risk.max_account_leverage | reject if resulting gross notional / equity exceeds it (20) |
| Net exposure | risk.max_net_exposure_usd | reject if resulting |long − short| exceeds it at the per-direction worst case (20) — resting non-reduce-only orders count on their own side, never offset |
| Per-coin concentration | risk.max_concentration_pct_per_coin | reject if one coin exceeds that % of equity (20) |
| Drawdown | risk.max_drawdown_pct | reject new exposure once equity is that % below its high-water (20); anchors keyed per network+account |
| Daily loss | risk.max_daily_loss_usd / _pct | reject new exposure once loss since UTC-midnight anchor exceeds it (20); anchors keyed per network+account |
| Max open positions | risk.max_open_positions | reject opening a new coin once at the concurrent-position cap (20); a coin with only a resting non-reduce-only order counts |
| Reduce-only guard | (always on) | reject a reduce-only order that cannot reduce: no open position, same side as the position, or larger than it (20) |
| Local rate cap | automation.max_orders_per_min | throttle before the exchange limit |
| Global halt | deliverator halt on | reject all new orders (21); panic still works — its flatten bypasses the halt core-internally (no flag), plain close/orders stay rejected |
| Dead-man's switch | risk.dead_man_switch_secs | schedule-cancel (resting orders only — positions stay open); refresh via dms heartbeat |
| Real-time failsafe | deliverator watch --metric liq_distance_pct --below N --action panic|dms|alert | stream-driven: trigger the action the moment the metric breaches — catches a mid-interval move the DMS/heartbeat can't |
| Dry-run | --dry-run | validate/round/attach, never send |
The bold account-wide gates bound new exposure: reduce-only/close orders
are exempt — including a batch/grid --reduce-only whose every leg is
reduce-only (one exposure-adding leg keeps the whole batch gated) — so a tripped
gate never blocks de-risking.
modify/modify-batch are gated too: a modify that grows an order's
worst-case exposure (larger size, or larger size×limit notional) passes the
per-coin caps and every account-wide gate, evaluated with the old order
replaced by the new one (a routine re-price never double-counts itself); a
modify that shrinks or holds exposure is de-risking and stays exempt, so a
tripped gate never blocks shrinking. A small compliant order cannot be
modified up past a cap.
All notional/portfolio caps count resting (unfilled) non-reduce-only orders
as worst-case future exposure — sequential orders cannot ladder past a cap by
each looking small against the filled book; reduce-only resting orders (TP/SL)
count zero. When risk.max_position_notional_usd is set, each gated write costs
at most one extra open-orders info read (weight 20) — the per-coin cap and the
portfolio gates share a single fetch. If the account state a configured
cap needs cannot be read (position, resting orders, equity — e.g. a 429 burst
or a silently-degraded sub-dex read),
an exposure-adding order fails closed with a retryable exit 40 (back off
and retry — not an exit-20 cap breach), while reduce-only orders still pass:
they carry reduce-only on the wire, so the exchange guarantees they only reduce.
The drawdown/daily-loss anchors persist per network+account (a testnet peak
never gates mainnet; accounts never share a daily anchor); the first gated write
of a new network+account context anchors fresh at current equity and emits a
warning, since fresh anchors under-protect until they move. Legacy unkeyed
risk_state.json files from older versions are ignored (left on disk).
Alerting: set alerting.webhook_url (or DELIVERATOR_ALERT_WEBHOOK) to POST a JSON event on RED-state failures (halt/auth/timeout by default; add risk etc. via alerting.categories) — best-effort, never blocks the command. Wire it to Slack/Discord/a relay so an away operator hears within seconds.
The threat model is an LLM hallucinating a size, price, or leverage. The CLI is the only enforcement point — every value is treated as hostile until checked.
| Wallet | Funds | Signs | Where |
|---|---|---|---|
| Master | yes | approveAgent, approveBuilderFee, deposit/withdraw | Browser/hardware — never here |
| Agent / API | no | orders, cancels, modifies, leverage, margin, schedule-cancel | Deliverator (keychain) |
deliverator onboard (import your API wallet key) or deliverator init (generate one). For headless/CI hosts with no keychain, set DELIVERATOR_AGENT_KEY to inject the key; it's used only when set and otherwise ignored, so an unset/empty env can never hide the keychain key. There is no stored agent_key_source config (that indirection was the original "key looks deleted" bug). If no key is available, every write fails with auth/no_agent_key and a hint to run onboard.deliverator init (fresh address). Never reuse a deregistered agent address.Setup: init · connect/health · version · config [get|set|path] ·
account [add|ls|rm|set-default] · markets · schema · tools
Track (reads): snapshot (one-call tick: portfolio + limits + ctx[coins] + builder) ·
portfolio · positions · orders · order status · fills ·
funding · ledger · balance · pnl · book · bbo · mids · candles ·
ctx (perp + spot; carries impact_pxs for slippage) · builder status ·
referral status · limits · predicted-fundings (forward funding-carry signal) ·
historical-orders (closed-order lifecycle) · twap status (running TWAPs + slice fills) ·
leaderboard (official HL trader leaderboard — filter/sort/drill-down to find an address to copy) ·
reconcile (diff local audit vs live; run first after a restart) ·
preview (what-if: resulting leverage/margin/liq for an order, no signing) ·
info <type> [k=v] (raw passthrough to any HL info endpoint)
positions/portfolio also carry computed risk fields: distance_to_liq_pct
per position, and account maintenance_margin + margin_ratio (divided by the
equity that can actually back perp margin: the gates' unified-account equity on
a unified account, the perp wallet(s) alone on a non-unified one — idle spot
USDC cannot meet a perp margin call).
Submit (writes):
buy/sell/order — market, limit, IOC (--ioc), post-only (--alo),
trigger (--trigger); --tp/--sl places a linked OCO bracket (one grouped
normalTpsl action — a filled TP cancels the resting SL).batch (a JSON array of orders) · grid (a limit ladder) ·
modify-batch — each is one signed action (one nonce, atomic pre-flight).modify · cancel (by --oid/--cloid, the --oids/--cloids lists,
or --all [--coin]) · close (flatten a perp position or sell a spot
holding) · position-tpsl (reduce-only TP/SL attached to a whole position) ·
chase (BBO-pegged passive-maker limit that re-prices as the book moves) ·
leverage · margin · twap (+ twap cancel / twap status).referral apply · onboard.leaderboard screens the official HL leaderboard (filter by window PnL/ROI/volume/account-value, --profitable-in day,week,month for consistency, --sort, paging) → pick a data.rows[].address → copy <leader> — non-custodial copy-trading; diff (read-only) by default, --execute --yes routes legs through the guarded order path (all risk gates apply). Stateless: pass --mirrored, persist data.mirrored_now.dms · halt · panic · watch (real-time failsafe: evaluate a live risk metric, trigger alert/dms/panic on breach — the reactive counterpart to the DMS).Stream (live NDJSON): stream book|bbo|trades|candles|fundings|active-asset|mids|fills|orders|webdata2|twap-fills|events
Run deliverator <cmd> --help for flags. Config lives at
~/.config/deliverator/config.toml (see deliverator config path).
outcome-mm)A second binary in this module, outcome-mm, is a specialized market maker for
HIP-4 outcome (prediction) markets. It consumes internal/core in-process — the same
guarded client the CLI drives — so every quote passes the identical risk gauntlet
(pre-trade checks, portfolio gates, plus two new outcome-specific gates) and signs
with the same non-withdraw agent key. It is a long-lived daemon; run it under
launchd/systemd with restart-on-crash, not cron.
go build -o outcome-mm ./cmd/outcome-mm
outcome-mm run # foreground TUI dashboard
outcome-mm run --headless # daemon: no TUI, periodic JSON status on stdout
outcome-mm run --dry-run # shadow: compute + render quotes, never sign
It never signs by default. A fresh install runs in shadow; live quoting requires
the operator to explicitly set mm.enabled = true and mm.dry_run = false (and
not pass --dry-run). Start on testnet or --dry-run first.
What it does each cycle: a slow selector ranks the daily-rotating outcome universe
into a small active set (liquidity + spread-opportunity + model-confidence score, with
hysteresis and per-underlying/expiry diversification); a fast loop prices each active
market with a Black–Scholes-digital fair value off the underlying's live mark and
EWMA realized vol, builds an inventory-skewed two-sided ladder, diffs it against
the resting book, and applies the minimal place/modify/cancel as aggregated signed
actions (staying inside max_orders_per_min). A cross-book YES/NO arb scanner and
a blackout + hold-to-settle policy round it out. Direction is handled by inventory
skew + small size, not a hedge (perp delta-hedge is v1.1; an LLM fair-value layer for
event markets is v2).
Two new core gates back it (they apply to every invocation surface, not just the
MM): risk.outcome_settle_blackout_mins refuses new outcome exposure near expiry (and
always on a settled market), and risk.max_outcome_question_notional_usd caps total
notional across all legs of one question (the Yes/No pair of a binary counts as one
bet). Both default off.
Config lives in the same config.toml under an [mm] table (selection weights,
spreads, ladder shape, TTL band, blackout window, pins/blacklist, priceable_underlyings).
Set keys with deliverator config set mm.<key> <value> or edit them live in the TUI.
[mm]
enabled = false # master switch for live quoting
dry_run = true # shadow even when enabled; flip to false to sign
[mm.selection]
max_active_markets = 6
priceable_underlyings = ["BTC", "ETH", "HYPE"]
min_ttl_mins = 30
[mm.strategy]
base_spread = 0.02 # half-spread in probability units
levels = 3
[mm.settle]
blackout_mins = 15 # stop quoting this long before expiry; hold to settle
The dashboard (internal/tui/mm) shows the active markets (fair vs mid, your quotes,
inventory, gate status), the ranked candidate pool with include/exclude reasons,
account/risk, a PnL split, and the activity feed; p pauses, ! panics, +/-/b/P
edit selection live. See the build spec for the full design.
go test ./... # signing parity, risk gauntlet, engine, precision, envelope, nonce
go test -race ./... # the suite is race-clean
go vet ./...
scripts/check-coverage.sh # per-package coverage floors
go build -o deliverator .
Architecture: cmd/ is a thin cobra adapter; all correctness and safety live
in internal/core, which drives internal/hl (the from-scratch Hyperliquid
API client + EIP-712 signer). internal/output owns the schema-v1 envelope +
exit codes + error catalog; internal/config the TOML; internal/wallet key
sources; internal/state the nonce flock + audit log.
Client: a native internal/hl package talks directly to the Hyperliquid API —
EIP-712 action signing ported from the official Python SDK, no third-party SDK.
See CONTRIBUTING.md for the architecture invariants a change must hold (thin surface, golden-vector signing, coverage floors, no secrets).
Deliverator is free and MIT-licensed. It's funded by an optional builder fee —
0.05% (5 bps), on by default — routed to the project's Hyperliquid builder
address. It is graceful and non-custodial: the fee is only ever charged once you
sign the one-time approveBuilderFee with your master wallet (the agent key
can't — that's the whole guarantee). Until you approve it, every order trades
fee-free — you're never blocked, and never charged without consent. Spot buys
never carry it (Hyperliquid takes the taker fee in the base token).
If Deliverator is useful to you, approving the fee is the easiest way to support its
development. To opt out or self-host, repoint builder.address at your own builder
or set builder.attach_mode = "manual". New accounts also get a referral fee
discount via deliverator onboard.
⚠️ Experimentelle Skill-Sammlung für deutsches Recht (Arbeits-, Gesellschafts-, Insolvenz-, Datenschutz-, Prozessrecht u
Manage multiple Claude Code agents from TUI or Web with tmux and git worktrees
Project management using GitHub Issues + Git worktrees for parallel agent execution
Core skills library for Claude Code with 20+ battle-tested skills including TDD, debugging, and brainstorming