Skip to content

feat(exchange): keep Hyperliquid market orders within the bound around the mark - #491

Merged
MarcosACH merged 5 commits into
mainfrom
ralph/issue-467
Oct 13, 2026
Merged

MarcosACH merged 5 commits into
mainfrom
ralph/issue-467

Conversation

@MarcosACH

Copy link
Copy Markdown
Owner

Closes #467

A market order on Hyperliquid was priced from the last trade only. A stale trade can send an order far from the market. Say a short is held, the last trade is 100, and the mark has fallen to 96. A buy at 105 is about 9% above the market, not 5%.

Now the adapter caps every market order by the mark too, inside the same SLIPPAGE_BOUND (ADR-0059).

  • A buy goes out at the lower of trade × (1 + bound) and mark × (1 + bound).
  • A sell goes out at the higher of trade × (1 − bound) and mark × (1 − bound).
  • With no mark cached, place raises and sends nothing. This is the same as with no trade today.
  • Limit orders are unchanged. Paper is unchanged.

Docs: .env.example, the adapter docstring, the config comment, and the ADR-0030 and ADR-0059 notes now say the price is capped by the mark.

Live flatten is still refused, so it cannot send a close before the first mark arrives. #475 owns that, and a comment there says flatten must count a symbol as priced only once it has a trade and a mark.

Tests

Hyperliquid, faked at HTTP:

  • A market buy with a trade far above the mark goes out at the mark-capped price.
  • A market sell with a trade far below the mark gets the same cap on the other side.
  • A market order with no mark raises and sends nothing.

pytest, ruff, mypy, and lint-imports are green.

🤖 Generated with Claude Code

MarcosACH and others added 4 commits October 12, 2026 20:22
A stale trade price can sit far above the market. A market buy now goes out
at the lower of trade x (1 + bound) and mark x (1 + bound) (ADR-0059).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A market sell now goes out at the higher of trade x (1 - bound) and
mark x (1 - bound), so a stale low trade cannot sell far under the market.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A trade alone could be stale, so a missing mark is a missing price
(ADR-0059). The order raises and nothing is sent, as with no trade today.
Older market tests now cache a mark that does not bind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
.env.example, the adapter docstring and the config comment still said
last trade x (1 +/- bound). ADR-0030 and ADR-0059 now say the cap is built.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 13, 2026 00:04
@MarcosACH MarcosACH added the ralph:needs-review PR opened, awaiting code-review label Oct 13, 2026
@MarcosACH MarcosACH self-assigned this Oct 13, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@MarcosACH MarcosACH left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of ralph/issue-467 @ 5e8edbf

Reviewer: code-review skill
Reviewed: 2026-10-12
Optimization axis: correctness

Mechanical

  • ruff format: pass
  • ruff check: pass
  • mypy: pass
  • lint-imports: pass (7 kept, 0 broken)
  • pytest: pass (2038 passed, 49 skipped, coverage gate ≥90% met)

Findings

[NIT] R001 — docs/adr/0030-instrument-universe-and-order-mapping.md:50 — Build status rewritten inside an existing amendment

Category: docs
Why: ADRs are append-corrected (docs/agents/adr-reading.md). This PR changes "Not built yet." inside the ADR-0059 amendment block, but in ADR-0059 it appends a new (Built in #467) block. The two ADRs now record the same fact in two different ways.
Fix: Restore "Not built yet." and append a short **(Built in [#467](...):** ...**)** block after it, as done in ADR-0059.
Status: open

Checked and clean

  • All four acceptance criteria of #467 are met. Each one has a test faked at HTTP. Limit tests place with no trade and no mark, so "limit unchanged" is covered.
  • The price rule matches ADR-0059: min for a buy, max for a sell, then passive quantization. Rounding can only make the price tighter.
  • A missing mark raises before any send, the same path as a missing trade. Nothing reaches the wire (post.requests == []).
  • The mark cache is in memory, like the trade cache. After a restart both refill from the feed, so recovery does not change.
  • The HyperliquidFeed already publishes MarkTick from activeAssetCtx on the same bus. Replay publishes one too. So no wiring change is needed.
  • Docs-sync: .env.example, the adapter docstring, the config comment, ADR-0059, and the module map all agree. CONTEXT.md already said it. Paper is untouched.
  • Live flatten before the first mark is out of scope. #475 owns it.

Summary

  • BLOCKING: 0 open
  • WARN: 0 open
  • NIT: 1 open
  • Merge: READY

Ralph-State: ready

@MarcosACH MarcosACH added ralph:ready Clean review; eligible for merge and removed ralph:needs-review PR opened, awaiting code-review labels Oct 13, 2026
…dment

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MarcosACH

Copy link
Copy Markdown
Owner Author

R001 — Status: resolved — 3fe9a5c

ADR-0030 keeps its original amendment text. The build note is now a new (Built in #467) block after it, the same as ADR-0059.

@MarcosACH
MarcosACH merged commit 5056400 into main Oct 13, 2026
3 checks passed
@MarcosACH
MarcosACH deleted the ralph/issue-467 branch October 13, 2026 00:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ralph:ready Clean review; eligible for merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cap every Hyperliquid market order price by the mark

2 participants