A multi-tenant Telegram bot: anyone can message it, follow Hyperliquid wallets, and get a
DM whenever a wallet they follow changes a perp position over the public trades firehose —
distinguishing a brand-new trade from an increase, and reporting reduces/closes with realized PnL.
- No auth, no scraping. Uses only Hyperliquid's sanctioned public API
(
wss://api.hyperliquid.xyz/ws+POST /info). See docs/hyperliquid-api-map.md. - Scales to 1k+ wallets on one connection — the
tradesfeed carries both counterparties, so one market-data subscription serves every subscriber by filtering. - Multi-tenant. Each subscriber keeps their own labelled watchlist; a wallet's activity fans out to every subscriber following it, in their own chat with their own label.
- Stores nothing but the watchlists. Positions live in memory and are rebuilt from the chain
(
clearinghouseState) on every start; the SQLitetracker.dbholds only(chat_id, address, label).
Design + rationale: docs/DESIGN.md.
trades firehose ─► resolve_deltas (users:[buyer,seller] filter) ─► apply_fill state machine
└─► open/add/reduce/close ─► Telegram push
clearinghouseState ─► silent seed (cold-start correctness) + leverage ┘
The cold-start guarantee: before a wallet is admitted to the live filter, its current on-chain positions are seeded silently, so an add on a position opened before the tracker started is correctly reported as "Added to position", never a false "Started trade".
Instead of one message per fill, the bot keeps one message per position and edits it in place as the wallet opens → adds → reduces → closes. Editing is silent (no notification ping), so a card stays current without spamming the chat. Every card leads with your label, shows the current position, and carries a copyable full address, a View TX explorer link, and the fill time. An open:
🟢 Whale-1 · BTC Long 10x
Opened 2.5 @ 63120 (≈$157,800)
👤 0xabc…def
🔗 View TX · 🕒 12:00 UTC
…is the same message, edited, once the wallet closes:
🔴 Whale-1 · BTC Long 10x
Closed 2 · 63120 → 64500
💰 +$2,760.00 (+21.9%) · held 3h12m
👤 0xabc…def
🔗 View TX · 🕒 15:12 UTC
Adds show a running total (Added +1 @ 63120 → 3 total (≈$189,360)) and reduces show the realized
PnL and remaining size (Reduced -0.5 @ 64000 → 2 left · realized +$440.00). The +21.9% on a
close is the leveraged return on margin (shown when leverage is known). Set
TRACKER_NOTIFY_REDUCE_CLOSE=false to suppress the new pings for reduces/closes — the card is
still edited silently so it never goes stale.
One credential: a Telegram bot token. It cannot be generated by code — Telegram issues it via @BotFather:
- Open Telegram, message @BotFather, send
/newbot. - Choose a display name, then a username ending in
bot. - BotFather replies with a token like
123456789:AA...— copy it.
Put it in a .env at the repo root (loaded via load_dotenv()), or export it:
# .env
TELEGRAM_BOT_TOKEN=123456789:AA...Verify the token works (and optionally see chat ids that have messaged the bot):
uv run hl-tracker-telegram-setup # reads TELEGRAM_BOT_TOKEN from .envThat's it — there is no chat id to configure. This is a public bot: each subscriber's chat is captured automatically when they message it. (On-chain data needs no key — Hyperliquid's public API is unauthenticated.)
uv sync --extra telegram # install (omit --extra telegram for log-only mode)
uv run hl-tracker # start the botThen, in Telegram, anyone can DM the bot and manage their own watchlist:
/add 0x6bea…597f Whale-1 follow a wallet (labelled)
/remove 0x6bea…597f stop following
/rename 0x6bea…597f Whale relabel
/list show the wallets you follow
/positions live open positions of a wallet you follow (tap to pick one)
| Variable | Default | Meaning |
|---|---|---|
TELEGRAM_BOT_TOKEN |
— | Bot token from @BotFather; without it, notifications are logged only |
TRACKER_ALLOWED_CHAT_IDS |
(open to all) | Comma-separated chat-id allowlist to make the bot private |
TRACKER_NOTIFY_REDUCE_CLOSE |
true |
Notify on reduce/close (false = open+add only) |
TRACKER_RECONCILE_INTERVAL_S |
45 |
Leverage/drift reconcile cadence (0 disables) |
TRACKER_RECONCILE_BATCH |
20 |
Wallets re-seeded per reconcile cycle |
TRACKER_SEED_CONCURRENCY |
8 |
Concurrency of the startup seed sweep |
TRACKER_LIVE_COINS |
(all perps) | Comma-separated coins to restrict the feed |
TRACKER_DB_PATH |
tracker.db |
SQLite watchlist path (repo root) |
uv run ruff check . && uv run ruff format --check .
uv run ty check
uv run pytest