A Python toolkit for quantitative research on Hyperliquid. Pulls historical data from the HL info API on demand, caches it locally as Parquet, and ships research primitives for backtesting, wallet behavior analysis, vault inspection, and funding research. Runs from a laptop. No infrastructure.
pip install hl-researchPaste a wallet, get a self-contained HTML report:
hlr data pull-fills 0xabc... --since 2024-01-01
hlr wallet wrapped 0xabc... --out wrapped.html
open wrapped.htmlPnL by asset, hour-of-day pattern, hold-time distribution, funding ledger, behavior cluster, counterfactual vs holding spot, top wins and losses. One file. Shareable.
Requires Python 3.12 or newer.
pip install hl-research
# Pull cache for one asset
hlr data pull BTC --since 2024-01-01
hlr data pull-funding BTC --since 2024-01-01
# See what's cached
hlr data ls
# Inspect a wallet (live)
hlr wallet inspect 0xabc...
# Build the wrapped report
hlr data pull-fills 0xabc... --since 2024-01-01
hlr wallet wrapped 0xabc... --out wrapped.html
# Browse interactively
hlr tuiEvery command supports --json for piping into scripts. Report commands accept --out PATH for standalone HTML artifacts.
| Command group | What it does |
|---|---|
hlr data |
Incremental Parquet cache (DuckDB metadata) over the HL info API |
hlr wallet |
Inspect any wallet, build wrapped reports, list fills and funding |
hlr funding |
Funding-rate history, cross-asset heatmap, perp-perp basis, naive prediction |
hlr vault |
List, rank, and inspect HLP and user vaults |
hlr backtest |
Run strategy files written in plain Python, grid + random optimization, walk-forward |
hlr tui |
Six-screen Textual app over the same data layer |
The cache is the foundation. Everything else reads from it.
$ hlr data ls
KIND ENTITY INTERVAL ROWS SIZE SINCE UNTIL UPDATED
────────────────────────────────────────────────────────────────────────────────────────────────
candles BTC 1h 720 34.68 KB 2024-04-01 2024-04-30 2026-05-19 15:44
candles ETH 1h 720 34.95 KB 2024-04-01 2024-04-30 2026-05-19 15:44
candles SOL 1h 720 35.12 KB 2024-04-01 2024-04-30 2026-05-19 15:44
fills 0xa1b2…0000 — 18 6.27 KB 2024-04-01 2024-04-12 2026-05-19 15:44
funding BTC — 720 14.16 KB 2024-04-01 2024-04-30 2026-05-19 15:44
funding ETH — 720 14.27 KB 2024-04-01 2024-04-30 2026-05-19 15:44
$ hlr data info
╭─ cache ──────────────────────────────────────────────────────────────────────╮
│ │
│ CACHE DIRECTORY ~/.cache/hl-research │
│ TOTAL SIZE 203.08 KB │
│ FILES 9 │
│ DATASETS 9 │
│ LAST SYNC 2026-05-19 15:44 │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
Incremental by default — re-running pull resumes from the last cached candle.
Live account state:
hlr wallet inspect 0xabc...Behavioral wrapped report (see screenshot above) — the headline artifact:
hlr wallet wrapped 0xabc... --out wrapped.htmlWithout --out you get a terminal summary:
╭─ wallet wrapped ─────────────────────────────────────────────────────────────╮
│ │
│ WALLET 0xa1b2…0000 │
│ PERIOD 2024-04-01 → 2024-04-12 │
│ REALIZED PNL +$4,240.00 │
│ FUNDING NET $0.00 │
│ WIN RATE 88.9% │
│ TRADES 9 │
│ CLUSTER chad │
│ Win rate was at least 65% and total PnL exceeded 1k. │
│ │
│ HOLD vs ACTUAL -$17,908.51 (pass --out to write the full HTML report) │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
Behavior clusters are rules-based — revenge_trader, funding_farmer, scalper, swing_trader, chad, leverage_addict, hold_and_pray, balanced. The output includes the reason that triggered the classification.
Cross-asset funding snapshot, sorted by absolute rate, annualized APR on the right:
$ hlr funding heatmap
ASSET RATE
──────────────────────────────────────────────────────────────────────────────────────────────
BTC +0.0147% │████████████████████████████ +16.1% APR
SOL +0.0134% │█████████████████████████ +14.7% APR
HYPE -0.0059% ███████████│ -6.4% APR
ETH +0.0014% │███ +1.5% APR
History + sparkline:
$ hlr funding history BTC
╭─ funding history ────────────────────────────────────────────────────────────╮
│ │
│ ASSET BTC │
│ OBSERVATIONS 720 │
│ RANGE 2024-04-01 → 2024-04-30 │
│ LATEST +0.0147% / period │
│ MEAN +0.0126% / period │
│ │
│ ▄▂▁▁▄▄▅▇▅▂▇█▄▂▆▇▄▆▂▁▂▇▅▃▅▆▅▆▃▂▂▃▃▃▇▂▃▅▁▁▇▆▇▇▃▂▆▇▃▁ │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
Perp-vs-perp basis leaderboard:
$ hlr funding arb BTC
PAIR LATEST BASIS ANNUALIZED MEAN BASIS N
────────────────────────────────────────────────────────────
BTC / HYPE +0.0206% +22.52% +0.0168% 720
BTC / ETH +0.0133% +14.60% +0.0035% 720
BTC / SOL +0.0013% +1.45% -0.0036% 720
Naive EWMA prediction with disclaimer:
$ hlr funding predict BTC
╭─ funding prediction ─────────────────────────────────────────────────────────╮
│ │
│ PREDICTED RATE +0.0111% │
│ LOWER (95%) +0.0013% │
│ UPPER (95%) +0.0209% │
│ │
│ Naive EWMA baseline. Do not trade on this without a real model. │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
# my_strategy.py
from hl_research.backtest.strategy import Order, Strategy
class MeanReversion(Strategy):
def __init__(self):
super().__init__()
self._lookback = []
def on_candle(self, candle, ctx):
self._lookback.append(candle.close)
if len(self._lookback) > 20:
self._lookback.pop(0)
mean = sum(self._lookback) / len(self._lookback)
position = ctx.positions.get(candle.asset)
held = position.size if position else 0.0
if candle.close < mean * 0.99 and held <= 0:
return [Order(asset=candle.asset, side="buy", size=0.05, kind="market")]
if candle.close > mean * 1.01 and held > 0:
return [Order(asset=candle.asset, side="sell", size=abs(held),
kind="market", reduce_only=True)]
return []$ hlr backtest run my_strategy.py --asset BTC
╭─ backtest ───────────────────────────────────────────────────────────────────╮
│ │
│ STRATEGY MeanReversion │
│ ASSET BTC │
│ PERIOD 2024-04-01 → 2024-04-30 │
│ │
│ TOTAL RETURN -2.43% │
│ SHARPE -1.18 │
│ MAX DRAWDOWN -2.43% │
│ HIT RATE +0.00% │
│ PROFIT FACTOR 0.00 │
│ TRADES 4 │
│ NET FUNDING -$149.27 │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ equity ─────────────────────────────────────────────────────────────────────╮
│ │
│ ████████▇▇▆▆▆▆▆▆▆▆▆▆▆▆▅▅▄▄▄▄▄▄▄▄▄▄▄▃▃▃▃▃▃▃▃▃▃▃▂▂▁▁▁▁▁▁▁▁▁▁▁▁ │
│ │
│ START $100,000.00 END $97,571.67 │
│ PEAK $100,000.00 TROUGH $97,571.67 │
│ CHANGE -$2,428.33 (-2.43%) │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
The example loses money on this short window — backtesting on a month of one asset is a sanity check, not an alpha signal.
Optimization and walk-forward:
hlr backtest optimize my_strategy.py --param "lookback:10..30:5"
hlr backtest walk-forward my_strategy.py --window 90d --step 30d
hlr backtest run my_strategy.py --asset BTC --out report.htmlhlr tuiTwo-pane layout with six sections. 1–6 to switch, / for the asset picker, r to reload, q to quit. All views reuse the same cache and analytics the CLI uses.
HL info endpoint
│
▼
api/ typed httpx client, retries, rate limit
│
▼
cache/ Parquet on disk, DuckDB metadata, incremental sync
│
▼
data/ Polars LazyFrames, pure transforms
│
├──▶ analytics/ wrapped, behavior, counterfactual, funding, vault
├──▶ backtest/ event loop, fill simulation, metrics, optimizer
└──▶ presentation/ Rich tables, Plotly charts, Jinja2 reports
│
└──▶ cli/, tui/
The data layer never calls presentation. Presentation never calls the API. The CLI binds the two. Pure functions in analytics/, frozen dataclasses everywhere, mypy strict across 79 source files.
Cache layout on disk:
~/.cache/hl-research/
├── meta.duckdb # sync state, asset and vault tables
└── data/
├── candles/asset=BTC/interval=1h/2024-04.parquet
├── funding/asset=BTC/2024.parquet
├── fills/wallet=0xabc.../2024-Q2.parquet
└── vaults/address=0xvault.../trades.parquet
Partitioned for Polars and DuckDB. Re-pulling resumes from the last cached row.
Does this need a Hyperliquid account? No. Every endpoint hl-research uses is public.
Does it trade? No. hl-research is read-only by design. It never holds keys, places orders, or signs anything.
What data is missing? L2 microstructure history. Tick-level book data isn't available from the public API and we can't reconstruct it without a continuous ingestor. Candles, funding, fills, and vault state are all here.
How big does the cache get? ~50MB for two years of top-20 assets at 1h candles + funding. Active trader wallet fills add 1-10MB per wallet.
Can I use this in a notebook?
Yes — see examples/basic_backtest.ipynb and examples/wallet_analysis.ipynb. Every CLI command has a programmatic equivalent under hl_research.*.
What about live mode? On the v0.2 roadmap. Today everything is on-demand from the REST endpoint.
How do I contribute? See CONTRIBUTING.md. Issues and PRs welcome.
pip install hl-researchOr with uv:
uv pip install hl-researchOptional extras:
pip install "hl-research[plot]" # matplotlib + plotly for charts
pip install "hl-research[tui]" # Textual interactive app
pip install "hl-research[all]" # everythingFull reference at ramenxbt.github.io/hl-research.
MIT. See LICENSE.
