Skip to content

Info endpoints: twapHistory, userTwapSliceFillsByTime, activeAssetData - #157

Merged
TuxedoFish merged 2 commits into
mainfrom
endpoint/twap-history-active-asset-data
Sep 8, 2026
Merged

Info endpoints: twapHistory, userTwapSliceFillsByTime, activeAssetData#157
TuxedoFish merged 2 commits into
mainfrom
endpoint/twap-history-active-asset-data

Conversation

@TuxedoFish

Copy link
Copy Markdown
Owner

Summary

  • Adds three /info endpoints: twapHistory, userTwapSliceFillsByTime (closes Implement twapHistory / userTwapSliceFillsByTime #124), and activeAssetData (closes Implement activeAssetData #126).
  • twapHistory/userTwapSliceFillsByTime complete the TWAP story alongside the already-existing twapOrder/twapCancel/userTwapSliceFills.
  • activeAssetData returns per-asset leverage/margin/mark-price data, needed for correct order sizing.
  • Both RestApi (sync + async) and WebsocketApi (post-over-websocket) methods added for all three, per CONTRIBUTING.md.
  • Request-builder and response-parser tests added to tests/rest_account_test.cpp (TWAP endpoints) and tests/rest_market_data_test.cpp (activeAssetData), plus a WebsocketApi no-throw smoke test in tests/websocket_api_test.cpp.
  • Examples extended: rest_twap_slices.cpp now also calls twapHistory/userTwapSliceFillsByTime, rest_leverage.cpp now calls activeAssetData.
  • README's info-endpoint coverage table updated (56 of 78 now ✅).

Verification against live mainnet (not just docs)

Per CONTRIBUTING.md's guidance, I ran real requests against https://api.hyperliquid.xyz/info before writing the parsers, rather than trusting docs/memory alone:

  • activeAssetData: queried the HLP vault address (0xa15099a30bbf2e68942d6f4c43d70d04faeab0a0) for ETH/BTC/HYPE. Confirmed shape: {"user":..,"coin":..,"leverage":{"type":"cross","value":20},"maxTradeSzs":["0.0","0.0"],"availableToTrade":["0.0","0.0"],"markPx":"2479.5"}. Also discovered a null response for a dex/coin the user has never traded on (e.g. querying a HIP-3 dex asset for an account with no isolated position there) - the parser degrades gracefully (logs and returns a default-constructed struct) rather than crashing.
  • twapHistory: found real active TWAP trader addresses via hypurrscan.io's /twap/{coin} API (e.g. 0x6973a383b202b4349d256bbf8b0187dc7b2ed6bb, 0x9b3cafa1209ac61f02d7bc3b219697fb171c9c91, 0x13c50dcdee4bbcba71baf578b345cdd35c7928be) and queried their twapHistory. Confirmed shape: a flat array of {time, state, status, twapId} (field order matters for simdjson ondemand - time first, then state, then status, then twapId). Two things this uncovered that weren't previously known to the codebase (the existing TwapHistoryStatus enum, shared with the userTwapHistory/twapStates websocket channels, only had activated/terminated/finished/error):
    • Two additional live status values: waitingForTrigger and stopped (both added to the enum).
    • twapId is a sibling field of state/status at the entry level, not nested inside state - added as a new field on the shared TwapHistoryEntry struct (defaults to 0 / unpopulated on the websocket userTwapHistory channel path, which I did not touch).
    • state also carries trigger/stopPx fields (trigger-based TWAP orders) not modeled by the existing TwapState struct - left unread/unmodeled since they're safely skippable trailing fields and out of scope for this PR (no existing twapOrder request support for trigger-TWAPs either).
  • userTwapSliceFillsByTime: confirmed identical array of {fill, twapId} shape to the existing userTwapSliceFills, just filtered by startTime/endTime/aggregateByTime - same pattern as userFills/userFillsByTime.

Not independently verified live: the isolated-margin HIP-3 dex variant of activeAssetData's leverage object, which per the official docs adds a rawUsd field alongside type/value. I could not find a live isolated-margin HIP-3 position to query, so ActiveAssetData::leverageRawUsd and its parsing are doc-sourced only - please double-check before relying on this if it matters for your use case.

Test plan

  • cmake -S . -B build -DHYPERLIQUID_WARNINGS_AS_ERRORS=ON -DHYPERLIQUID_BUILD_TESTS=ON -DHYPERLIQUID_BUILD_EXAMPLES=ON -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" - clean configure
  • cmake --build build -j"$(nproc)" - zero warnings/errors
  • ctest --test-dir build - 100% pass (28/28 test suites)
  • build/ removed before committing

🤖 Generated with Claude Code

https://claude.ai/code/session_018Le73N2EZjGLsCqmCScAse

Completes the TWAP history story (twapOrder/twapCancel/userTwapSliceFills
already existed) and adds per-asset leverage/margin data needed for
correct order sizing.

Response shapes for all three were confirmed live against mainnet (real
TWAP trader addresses discovered via hypurrscan, and the HLP vault address
for activeAssetData), not just from docs:
- twapHistory: flat array of {time, state, status, twapId}. Also
  discovered two status values missing from the existing TwapHistoryStatus
  enum (waitingForTrigger, stopped) alongside the previously-known
  activated/terminated/finished/error, and that state carries twapId as a
  sibling field rather than nested inside state.
- userTwapSliceFillsByTime: identical array-of-{fill,twapId} shape to the
  existing userTwapSliceFills, just filtered by time range - same pattern
  as userFills/userFillsByTime.
- activeAssetData: matches the docs (user, coin, leverage{type,value},
  maxTradeSzs, availableToTrade, markPx). The isolated-margin HIP-3 dex
  variant that adds "rawUsd" to the leverage object is doc-sourced only,
  not independently verified live (noted in code comments and PR
  description).

Adds RestApi + WebsocketApi methods (sync/async + post-over-websocket) for
all three, request-builder and response-parser tests, and marks the three
rows ✅ in README's endpoint coverage table.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Le73N2EZjGLsCqmCScAse
@codecov

codecov Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 74.59459% with 47 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.72%. Comparing base (8a54267) to head (3d27a64).

Files with missing lines Patch % Lines
src/rest/RestApi.cpp 0.00% 17 Missing ⚠️
src/rest/RestApiMessageParser.cpp 82.60% 16 Missing ⚠️
src/websocket/PostResponseDispatch.cpp 0.00% 9 Missing ⚠️
include/hyperliquid/rest/RestEndpointListener.h 0.00% 3 Missing ⚠️
...clude/hyperliquid/websocket/WebsocketApiListener.h 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #157      +/-   ##
==========================================
+ Coverage   69.53%   69.72%   +0.19%     
==========================================
  Files          53       53              
  Lines        8219     8404     +185     
  Branches      513      526      +13     
==========================================
+ Hits         5715     5860     +145     
- Misses       2501     2541      +40     
  Partials        3        3              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions github-actions Bot added the ready for review CI passed; ready to be reviewed label Sep 8, 2026

enum class TwapHistoryStatus { Activated, Terminated, Finished, Error, Unknown };
// Confirmed live against mainnet (twapHistory info endpoint): activated, terminated,
// waitingForTrigger, stopped, finished, error have all been observed on real accounts.

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.

Please remove

virtual void onPostResponse(const SpotClearinghouseStateResponse&, std::optional<uint64_t> = std::nullopt) {}
virtual void onPostResponse(const FrontendOpenOrdersResponse&, std::optional<uint64_t> = std::nullopt) {}
virtual void onPostResponse(const HistoricalOrdersResponse&, std::optional<uint64_t> = std::nullopt) {}
// userTwapSliceFills/userTwapSliceFillsByTime share this response shape.

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.

not useful

Comment thread src/rest/RestApiMessageParser.cpp Outdated
// crackTwapState. Only reads fields through "timestamp" - "trigger"/"stopPx" also appear on
// the wire (trigger-TWAP orders) but aren't modeled by TwapState yet, and it's safe to leave
// trailing fields unread since simdjson's ondemand only requires in-order reads, not
// exhaustive ones.

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.

Please get rid of this

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Le73N2EZjGLsCqmCScAse
@TuxedoFish
TuxedoFish merged commit 0b9bdc9 into main Sep 8, 2026
11 checks passed
@TuxedoFish
TuxedoFish deleted the endpoint/twap-history-active-asset-data branch September 8, 2026 12:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready for review CI passed; ready to be reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement activeAssetData Implement twapHistory / userTwapSliceFillsByTime

1 participant