Skip to content

Latest commit

 

History

36 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Perps Latency Benchmark

Benchmark crypto perps order submission latency.

Prerequisites

  • Go 1.25+
  • uv for live Hyperliquid/Lighter/Extended runs; Python 3 for Aster
  • Venue accounts funded and configured before using --confirm-live

Long-running services should prefer direct Python interpreters over resident uv run wrappers once dependencies are installed. Set PERPS_BENCH_PYTHON to a shared Python command prefix; matching uv run ... python internal/venues/<venue>/... commands will execute through that prefix.

Quick Start

Generate local wallet material:

go run ./cmd/perps-bench accounts generate \
  --venues hyperliquid,lighter \
  --out .env.wallets.local

Print the setup checklist. This shows the public wallet identifiers you need for venue setup.

go run ./cmd/perps-bench accounts checklist \
  --venues hyperliquid,lighter \
  --env-file .env.wallets.local

Complete the venue-side setup:

  • Hyperliquid: register or approve the printed EVM wallet if needed, fund the Hyperliquid account or agent wallet, then adjust the BTC order params in examples/hyperliquid-builder.json if needed.
  • Lighter: use the printed Ethereum address for account creation/deposits, generate an API key in Lighter, fill LIGHTER_PRIVATE_KEY, LIGHTER_ACCOUNT_INDEX, and LIGHTER_API_KEY_INDEX in .env.wallets.local, optionally set LIGHTER_MAKER_PRIVATE_KEY and LIGHTER_MAKER_API_KEY_INDEX for a maker-only key, then adjust the BTC order params in examples/lighter-builder.json if needed.

Verify that required local environment is present:

go run ./cmd/perps-bench accounts check \
  --venues hyperliquid,lighter \
  --env-file .env.wallets.local

Run a benchmark:

go run ./cmd/perps-bench run \
  --config examples/hyperliquid-builder.json \
  --env-file .env.wallets.local \
  --confirm-live

Swap the config file for another configured venue, for example:

examples/lighter-builder.json
examples/aster-builder.json
examples/extended-builder.json

The starter configs use small post-only orders. Keep them small until account setup is confirmed. Cleanup runs after each measured submit, outside the latency window.

Post-only builder configs should derive price from the venue book instead of a fixed BTC level. Set post_only_price_source to book_top to enable the warm book-top stream, post_only_price_offset_bps for the maker-safe distance from best bid/ask, and post_only_book_max_age_ms for the stale-book guard. The live starter post-only configs use a 500 bps offset and fail request preparation if the book is missing or stale.

Hyperliquid post-only configs can set post_only_price_source to hyperliquid_l2book_http to fetch POST /info l2Book pricing instead of opening another book WebSocket. Use post_only_price_refresh_ms to control the cache TTL; the starter Hyperliquid configs refresh every five minutes.

For long-running taker sampling, use the venue-specific *-taker-builder.json config and strict cleanup.

Transport Policy

Use persistent WebSocket order submission whenever a venue has a verified official order-entry WebSocket path. Hyperliquid and Lighter single, batch, and taker configs use WebSocket for this reason. Aster and Extended currently use HTTPS for order submission with WebSocket account-feed confirmation because the verified official WebSocket docs cover market/private streams, not order-entry submission.

Account Commands

go run ./cmd/perps-bench accounts plan --venues hyperliquid,lighter
go run ./cmd/perps-bench accounts generate --venues hyperliquid,lighter --out .env.wallets.local
go run ./cmd/perps-bench accounts checklist --venues hyperliquid,lighter --env-file .env.wallets.local
go run ./cmd/perps-bench accounts check --venues hyperliquid,lighter --env-file .env.wallets.local

See docs/credentials.md for env-file details.

Output Files

Write JSON/CSV results:

go run ./cmd/perps-bench run \
  --config examples/hyperliquid-builder.json \
  --env-file .env.wallets.local \
  --confirm-live \
  --output results/hyperliquid.json \
  --csv results/hyperliquid.csv

Summaries default to full response latency. To use TTFB:

--latency-mode ttfb

To measure private-stream confirmation instead of submit response:

--measurement-mode ws_confirmation

Compare saved result files:

go run ./cmd/perps-bench compare-results \
  results/hyperliquid.json \
  results/lighter.json

Transports

Some venues support order submission over both HTTPS and WebSocket. Compare them with:

go run ./cmd/perps-bench compare-transports \
  --config examples/hyperliquid-builder.json \
  --env-file .env.wallets.local \
  --transports https,websocket \
  --iterations 50 \
  --warmups 5 \
  --confirm-live \
  --output results/hyperliquid-transports.json

Unsupported transport/scenario combinations fail before the run starts.

Continuous API

Run a benchmark continuously into a local SQLite store:

go run ./cmd/perps-bench run-continuous \
  --config examples/lighter-builder.json \
  --env-file .env.wallets.local \
  --transport websocket \
  --rate 0.0166667 \
  --chunk-iterations 1 \
  --cleanup-mode strict \
  --confirm-live \
  --store data/bench.db

Serve the read-only API:

go run ./cmd/perps-bench serve \
  --store data/bench.db \
  --listen 127.0.0.1:8080

Print the collector/API service topology before wiring process supervision:

go run ./cmd/perps-bench service-topology \
  --config examples/lighter-builder.json \
  --env-file .env.wallets.local \
  --exchange-tps-venue hyperliquid \
  --validate-binary ./perps-bench \
  --store data/bench.db \
  --listen 127.0.0.1:8080

Check a supervised deployment for failed services and stale accepted samples:

python3 scripts/service_watchdog.py --store data/bench.db

For transient venue/API outages that trip systemd start limits, operators can explicitly reset and start failed benchmark services:

python3 scripts/service_watchdog.py --store data/bench.db --restart-failed

Expose it publicly with a password:

export PERPS_BENCH_API_PASSWORD='choose-a-long-password'

go run ./cmd/perps-bench serve \
  --store data/bench.db \
  --listen 0.0.0.0:8080 \
  --cors-origin ""

Check the API with:

curl -u "bench:$PERPS_BENCH_API_PASSWORD" \
  "http://YOUR_SERVER:8080/api/latest?window=5m"

Dashboard

Start the read-only API:

go run ./cmd/perps-bench serve \
  --store data/bench.db \
  --listen 127.0.0.1:8080

Start the dashboard:

cd frontend
npm install
npm run dev

Open the URL printed by Vite, normally http://127.0.0.1:3000. The dashboard keeps API credentials on the server side; they are not exposed to the browser.

For local development against an authenticated API, create frontend/.dev.vars:

PERPS_BENCH_API_URL=https://your-benchmark-api.example.com
PERPS_BENCH_API_USER=bench
PERPS_BENCH_API_PASSWORD=your-password

Deploy the dashboard:

cd frontend
npx wrangler secret put PERPS_BENCH_API_URL --config wrangler.jsonc
npx wrangler secret put PERPS_BENCH_API_PASSWORD --config wrangler.jsonc
npm run deploy:staging

The deployment flow uses staging as the local/default integration branch and main as production. Pushes to staging deploy the separate Cloudflare Worker perps-latency-dashboard-staging at https://staging-latency.perps.trading; merges to main deploy production perps-latency-dashboard at https://latency.perps.trading.

cd frontend
npm run deploy:staging
npm run deploy:production

Autodeploy can call the same npm scripts from the relevant branch. The Worker runtime secrets PERPS_BENCH_API_URL and PERPS_BENCH_API_PASSWORD are set directly in Cloudflare for each Worker environment.

Safety

Live runs require --confirm-live.

Fill-likely order profiles, including market, IOC, FOK, and explicit non-post-only orders, require risk.allow_fill=true, risk.neutralize_on_fill=true, and strict after-sample cleanup. Start with post-only/maker-style orders while validating setup.

Hyperliquid, Lighter, Aster, Extended, and Pacifica support cleanup of benchmark orders. Cleanup runs outside the measured latency window. At startup, venues with authenticated account reads check for stale orders from the same run_id; after the run, those venues reconcile that no submitted benchmark orders remain open and the position did not change. Pacifica currently prepares per-sample WebSocket cancel requests by client order ID. Use strict cleanup when cleanup failures should fail the sample:

--cleanup --cleanup-mode strict

Each live run also records a conservative network-floor estimate on every sample. The estimate prefers clean same-route observations in this order: WebSocket heartbeat RTT when the venue defines a safe heartbeat, real HTTP request TCP connect timing when a fresh connection is opened, then rolling low-percentile TCP connect timing to the same venue host and port as fallback. Raw latency remains the default dashboard view; use the dashboard's "Subtract network floor" toggle to inspect the optional network-adjusted view.

Supported Venue Status

  • Hyperliquid: HTTPS/WebSocket order submission, confirmation tracking, and cleanup.
  • Lighter: HTTPS/WebSocket transaction submission, confirmation tracking, and cleanup.
  • Aster: HTTPS order/batch submission, private WebSocket confirmation, and cleanup.
  • Extended: HTTPS order submission, parallel single-order batch benchmark, private WebSocket confirmation, and cleanup.
  • Pacifica: WebSocket order submission, native WebSocket batch benchmark, private WebSocket confirmation, and per-sample WebSocket cleanup.

Troubleshooting

  • Missing env vars: run accounts check with the same --env-file flags you will use for the benchmark.
  • Wrong wallet or key: run accounts print and compare the public identifiers with the venue UI.
  • Lighter account errors: confirm LIGHTER_ACCOUNT_INDEX, LIGHTER_API_KEY_INDEX, and LIGHTER_PRIVATE_KEY match the active API key. If using maker-only mode, confirm LIGHTER_MAKER_API_KEY_INDEX is marked maker-only in Lighter.
  • Lighter runner already active: use a separate Lighter API key for each concurrent runner, or stop the existing process using the same key.
  • Config rejected for transport: the venue does not support that transport/scenario pair in this tool.
  • Config rejected for risk: keep the order post-only while validating setup.

About

No description, website, or topics provided.

Resources

Stars

17 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages