Hyperliquid bots: copy trading and trading bots on live data
Build a Hyperliquid copy trading or trading bot on one free key: a trader's fills and closed trades live on the WebSocket, a webhook the moment a wallet opens, closes or flips, the newest bar of every coin in one call, and Claude Code writing the bot you describe.
“Send a webhook to my bot every time this wallet opens, closes or flips a position on Hyperliquid. (paste an address)”
alerts_create“Show this trader's closed trades this month: the win rate, the biggest win and the biggest loss. (paste an address)”
hl_wallet_tradesCopy a trader with an alert
Paste a wallet, get an e-mail or a webhook when it trades. Nothing to install.
Copy a trader
Paste the wallet you copy. Each time it opens, closes, flips or increases a position you get an e-mail, or a POST to your webhook: that webhook is your bot's trigger. Checked about every 10 seconds; add reduce for partial closes too.
Know before the trader you copy is liquidated
One alert when a position of that wallet gets 80% of the way from its entry to its liquidation price (you set the share). A copied position is in the same trouble.
No wallet in mind? Pick one on the leaderboard. Every Hyperliquid alert type: the alerts reference →
The same alert from code, to your bot's webhook
vike-data alerts register-webhook https://your-bot.example.com/hook
vike-data alerts create --type hl_wallet --wallet 0xfc667adba8d4837586078f4fdcdc29804337ca06 --channel webhook --label 'Copy 0xfc667adb'
# any MCP client; or from the terminal:
vike-data call alerts_create --args '{"label": "Copy 0xfc667adb", "alert_type": "hl_wallet", "wallet": "0xfc667adba8d4837586078f4fdcdc29804337ca06", "channels": ["webhook"]}'
Describe the bot, Claude Code writes it
Install the vike skill once, describe the bot in one sentence, and Claude Code writes and runs it on your key.
Describe the bot, Claude Code writes it
One install teaches Claude Code the Vike data, the stream and the CLI; then a sentence is enough.
- Skill
vike-data install --claude- Data
hl.fills,hl.closed_trades, REST
How it works
- Install the CLI and save your key once.
- Install the vike skill where Claude Code reads it.
- Ask for the bot in plain words; Claude Code writes it, runs it and shows you the output.
The setup and the ask
npm install -g @vike-io/cli
vike-data init # paste your key once
vike-data install --claude # the vike skill for Claude Code
Build a Python copy-trading bot for Hyperliquid wallet 0xfc667adba8d4837586078f4fdcdc29804337ca06
on the Vike stream (hl.fills). Print every fill with its action, coin, size and price,
and the wallet's position after it. Do not place any orders yet.
5 bots on the live stream
Each one is a short script on a free key: change the wallet or the coin and run it. Your bot places its own orders; this API gives it what to act on.
Copy-trading bot
Follow a trader and see what each fill did to its position, the moment it lands.
- Data
hl.fills- Filter
wallet(up to 1,000),min_usd0 = every fill- Each fill
action: open, close, flip, increase or reduce- Orders
- your bot places its own orders
How it works
- Subscribe to
hl.fillsfor the wallet you copy, withmin_usd0: without it you get fills from $1,000 only. - Each event says what the fill did (
action) and the position before and after it. - Mirror it at your own size.
The code
import json, os
from websockets.sync.client import connect # websockets >= 14
with connect("wss://stream.vike.io/v1/stream/hl",
additional_headers={"X-API-KEY": os.environ["VIKE_API_KEY"]}) as ws:
ws.send(json.dumps({"action": "subscribe", "id": "copy", "channel": "hl.fills",
"filter": {"wallet": ["0xfc667adba8d4837586078f4fdcdc29804337ca06"], "min_usd": 0}}))
for message in ws:
frame = json.loads(message)
if frame["type"] == "ping":
ws.send(json.dumps({"action": "pong"}))
elif frame["type"] == "event":
e = frame["event"]
d = e["data"]
s = e["subject"]
print(e["ts"], s["symbol"], d["action"], d["side"], d["size_token"], "@", d["price"],
"position now", d["position_after"])
# mirror it here at your own size: your bot places its own orders
vike-data stream hl --channel hl.fills --filter wallet=0xfc667adba8d4837586078f4fdcdc29804337ca06 --filter min_usd=0 # prints NDJSON, reconnects by itself
{
"action": "subscribe",
"id": "copy",
"channel": "hl.fills",
"filter": {
"wallet": [
"0xfc667adba8d4837586078f4fdcdc29804337ca06"
],
"min_usd": 0
}
}
{
"schema": "vike.event.v1",
"event_id": "hl.fills:0x2ab6fa5339449dd86a774530329f988b45dd8252:1793724719179:814936873052641",
"source": "hl.fills",
"kind": "fill",
"ts": "2026-11-03T16:51:59.179Z",
"cursor": "1793724719179-00000814936873052641",
"chain": "hyperliquid",
"subject": {
"wallet": "0x2ab6fa5339449dd86a774530329f988b45dd8252",
"symbol": "HYPE"
},
"data": {
"action": "increase",
"side": "buy",
"size_usd": 286066.65692,
"size_token": 3309.08,
"price": 86.449,
"closed_pnl": 0.0,
"start_position": 180.95,
"position_after": 3490.0299999999997,
"crossed": true,
"trade_id": 814936873052641,
"dex": "HL",
"fee": 128.729995,
"fee_token": "USDC",
"order_id": 593517739844
},
"labels": {
"pnl_cohort": "2x smart",
"pnl_cohort_basis": "live",
"pnl_cum_usd": 40564.961064559844,
"size_cohort": "2x dolphin"
}
}
Telegram bot for a trader's moves
Forward every open, close and flip of the trader you follow to your own Telegram chat.
- Data
hl.fills- Filter
wallet,action- Sends
- your bot, your chat
How it works
- Subscribe to
hl.fillsfor the wallet, withactionnarrowed to open, close and flip. - Post each one to Telegram with your bot's token.
The code
import json, os, requests
from websockets.sync.client import connect # websockets >= 14
with connect("wss://stream.vike.io/v1/stream/hl",
additional_headers={"X-API-KEY": os.environ["VIKE_API_KEY"]}) as ws:
ws.send(json.dumps({"action": "subscribe", "id": "telegram", "channel": "hl.fills",
"filter": {"wallet": ["0xfc667adba8d4837586078f4fdcdc29804337ca06"], "action": ["open", "close", "flip"], "min_usd": 0}}))
for message in ws:
frame = json.loads(message)
if frame["type"] == "ping":
ws.send(json.dumps({"action": "pong"}))
elif frame["type"] == "event":
e = frame["event"]
d = e["data"]
s = e["subject"]
text = f"{s['symbol']}: {d['action']} {d['side']} {d['size_token']} @ {d['price']} (${d['size_usd']:,.0f})"
requests.post(f"https://api.telegram.org/bot{os.environ['TG_TOKEN']}/sendMessage",
json={"chat_id": os.environ["TG_CHAT"], "text": text})
vike-data stream hl --channel hl.fills --filter wallet=0xfc667adba8d4837586078f4fdcdc29804337ca06 --filter action=open,close,flip --filter min_usd=0 # prints NDJSON, reconnects by itself
{
"action": "subscribe",
"id": "telegram",
"channel": "hl.fills",
"filter": {
"wallet": [
"0xfc667adba8d4837586078f4fdcdc29804337ca06"
],
"action": [
"open",
"close",
"flip"
],
"min_usd": 0
}
}
Watchlist positions, kept live
Keep the open positions of a list of wallets up to date from their fills.
- Data
hl.fills- Filter
wallet: up to 1,000 wallets on one subscription- State
position_afterof each fill
How it works
- Subscribe to
hl.fillsfor every wallet on your list. - Each fill carries the position after it (
position_after): keep the newest per wallet and coin; 0 is closed. - To start from what they hold now, read
/positions?wallet=…once (the copy-trade watcher study below does).
The code
import json, os
from websockets.sync.client import connect # websockets >= 14
book = {} # (wallet, coin) -> its position after the newest fill; negative = short
with connect("wss://stream.vike.io/v1/stream/hl",
additional_headers={"X-API-KEY": os.environ["VIKE_API_KEY"]}) as ws:
ws.send(json.dumps({"action": "subscribe", "id": "watchlist", "channel": "hl.fills",
"filter": {"wallet": ["0xfc667adba8d4837586078f4fdcdc29804337ca06", "0x25773676902085225ae5751075e4161369334897", "0x8c10d62907917967b17cdec0081ce42ae09eed8e"], "min_usd": 0}}))
for message in ws:
frame = json.loads(message)
if frame["type"] == "ping":
ws.send(json.dumps({"action": "pong"}))
elif frame["type"] == "event":
e = frame["event"]
d = e["data"]
after = d["position_after"]
if after is None: # a fill whose position could not be read: skip it
continue
key = (e["subject"]["wallet"], e["subject"]["symbol"])
if after:
book[key] = after
else:
book.pop(key, None)
print({f"{w[:8]} {c}": size for (w, c), size in book.items()})
vike-data stream hl --channel hl.fills --filter wallet=0xfc667adba8d4837586078f4fdcdc29804337ca06,0x25773676902085225ae5751075e4161369334897,0x8c10d62907917967b17cdec0081ce42ae09eed8e --filter min_usd=0 # prints NDJSON, reconnects by itself
{
"action": "subscribe",
"id": "watchlist",
"channel": "hl.fills",
"filter": {
"wallet": [
"0xfc667adba8d4837586078f4fdcdc29804337ca06",
"0x25773676902085225ae5751075e4161369334897",
"0x8c10d62907917967b17cdec0081ce42ae09eed8e"
],
"min_usd": 0
}
}
Large trades on a coin
Every fill of a million dollars or more on one coin, with the wallet behind it and its cohort.
- Data
hl.fills- Filter
coin,min_usd- Labels
size_cohortandpnl_cohortof the wallet
How it works
- Subscribe to
hl.fillswith the coin and a dollar floor. - Each event names the wallet, the side, the size in dollars and what the fill did to its position.
The code
import json, os
from websockets.sync.client import connect # websockets >= 14
with connect("wss://stream.vike.io/v1/stream/hl",
additional_headers={"X-API-KEY": os.environ["VIKE_API_KEY"]}) as ws:
ws.send(json.dumps({"action": "subscribe", "id": "large-trades", "channel": "hl.fills",
"filter": {"coin": ["BTC"], "min_usd": 1000000}}))
for message in ws:
frame = json.loads(message)
if frame["type"] == "ping":
ws.send(json.dumps({"action": "pong"}))
elif frame["type"] == "event":
e = frame["event"]
d = e["data"]
lab = e["labels"]
print(e["ts"], e["subject"]["wallet"], d["side"], f"${d['size_usd']:,.0f}", d["action"],
lab.get("size_cohort"), lab.get("pnl_cohort"))
vike-data stream hl --channel hl.fills --filter coin=BTC --filter min_usd=1000000 # prints NDJSON, reconnects by itself
{
"action": "subscribe",
"id": "large-trades",
"channel": "hl.fills",
"filter": {
"coin": [
"BTC"
],
"min_usd": 1000000
}
}
A trader's closed trades, as they close
Each round trip of the trader once it is closed: entry, exit, hold time and realized PnL, with a running tally.
- Data
hl.closed_trades- Filter
wallet,coin,direction- Delay
- written every 30 minutes
How it works
- Subscribe to
hl.closed_tradesfor the wallet. - Closed trades are written every 30 minutes, so a close arrives with the next batch.
- Count wins and losses as they come, or stop copying after a bad streak.
The code
import json, os
from websockets.sync.client import connect # websockets >= 14
wins = losses = 0
with connect("wss://stream.vike.io/v1/stream/hl",
additional_headers={"X-API-KEY": os.environ["VIKE_API_KEY"]}) as ws:
ws.send(json.dumps({"action": "subscribe", "id": "closed-trades", "channel": "hl.closed_trades",
"filter": {"wallet": ["0xfc667adba8d4837586078f4fdcdc29804337ca06"]}}))
for message in ws:
frame = json.loads(message)
if frame["type"] == "ping":
ws.send(json.dumps({"action": "pong"}))
elif frame["type"] == "event":
e = frame["event"]
d = e["data"]
pnl = d["realized_pnl"]
wins, losses = wins + (pnl > 0), losses + (pnl < 0)
print(e["subject"]["symbol"], d["direction"], d["entry_price"], "->", d["exit_price"],
f"PnL {pnl:+,.2f}", f"held {d['duration_seconds']} s", f"{wins} won, {losses} lost")
vike-data stream hl --channel hl.closed_trades --filter wallet=0xfc667adba8d4837586078f4fdcdc29804337ca06 # prints NDJSON, reconnects by itself
{
"action": "subscribe",
"id": "closed-trades",
"channel": "hl.closed_trades",
"filter": {
"wallet": [
"0xfc667adba8d4837586078f4fdcdc29804337ca06"
]
}
}
{
"schema": "vike.event.v1",
"event_id": "hl.closed_trades:0x000b27842040f51f70fdeaded6f35035e9df981c:xyz:SPCX:long:1076833147458494",
"source": "hl.closed_trades",
"kind": "closed_trade",
"ts": "2026-11-02T14:48:12.984Z",
"cursor": "1793630999000000-0x000b27842040f51f70fdeaded6f35035e9df981c-xyz:SPCX-long-1076833147458494",
"chain": "hyperliquid",
"subject": {
"wallet": "0x000b27842040f51f70fdeaded6f35035e9df981c",
"symbol": "xyz:SPCX"
},
"data": {
"direction": "long",
"open_ts": "2026-11-02T14:47:07.222000Z",
"close_ts": "2026-11-02T14:48:12.984000Z",
"duration_seconds": 65,
"entry_price": 140.77,
"exit_price": 141.12,
"max_size_token": 5.82,
"max_size_usd": 821.3184000000001,
"volume_usd": 1640.5998000000002,
"realized_pnl": 2.037,
"total_fee": 0.14765299999999998,
"fill_count": 2,
"open_tid": 1076833147458494,
"baseline_adopted": false,
"size_usd": 821.3184000000001
},
"labels": {
"pnl_cohort": "rekt",
"pnl_cohort_basis": "live",
"pnl_cum_usd": -604.6434909999997,
"size_cohort": "shrimp"
}
}
Stream address wss://stream.vike.io/v1/stream/hl. Every channel, its filters and the connection rules: the WebSocket reference →
Or poll: the newest bar of every coin
One call a minute gives a bot every coin's newest 1-minute candle.
The newest 1-minute bar of every coin, in one call
- Route
GET /v1/hyperliquid/ohlcv/latest- CLI
vike-data candles hl- MCP
hl_ohlcv· 1 credit- Cost
- 1 request of the hourly limit
The newest 1-minute candle of every symbol that traded in the last hour, in one request.
Parameters
| Parameter | In | Description |
|---|---|---|
market | query | perp | spot. Default perp |
dex | query | HL core or a HIP-3 perp DEX, e.g. XYZ. Omit for every DEX; each row names its own |
interval | query | Only 1m, the default. Any other value is a 400 |
Call it
curl -H "X-API-KEY: $VIKE_API_KEY" \
"https://data.vike.io/v1/hyperliquid/ohlcv/latest"
import os, requests
r = requests.get(
"https://data.vike.io/v1/hyperliquid/ohlcv/latest",
headers={"X-API-KEY": os.environ["VIKE_API_KEY"]},
)
print(r.json())
vike-data candles hl --view latest
# any MCP client; or from the terminal:
vike-data call hl_ohlcv --args '{"view": "latest"}'
Copy trading and bot questions, answered with code
Each with the sentence to ask your AI instead. Every block was run against the live API on 2026-11-03.
Copy-trade watcher: alert when a Hyperliquid wallet opens, adds or closes
Poll /wallets/{address}/fills with start set to the last fill you saw to catch every new fill within seconds, and diff /positions?wallet= between polls to tell an open, an add and a close apart.
- Uses
/positions,/wallets/{…}/fills- Verified
- live API, 2026-11-03: 4 calls, all 200
The code
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# the wallet's newest fills
r = requests.get(
"https://data.vike.io/v1/hyperliquid/wallets/0xfc667adba8d4837586078f4fdcdc29804337ca06/fills?limit=5",
headers={"X-API-Key": API_KEY},
)
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# fills at or after the last one already seen
r = requests.get(
"https://data.vike.io/v1/hyperliquid/wallets/0xfc667adba8d4837586078f4fdcdc29804337ca06/fills?start=2026-10-24T17:16:58.916Z&limit=500",
headers={"X-API-Key": API_KEY},
)
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# the wallet's open positions, one row per symbol and side
r = requests.get(
"https://data.vike.io/v1/hyperliquid/positions?wallet=0xfc667adba8d4837586078f4fdcdc29804337ca06&limit=500",
headers={"X-API-Key": API_KEY},
)
# then: jq '.positions | map({symbol, direction, size_token, entry_price, snapshot_ts})'
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# the wallet's newest BTC fills
r = requests.get(
"https://data.vike.io/v1/hyperliquid/wallets/0xfc667adba8d4837586078f4fdcdc29804337ca06/fills?symbol=BTC&limit=100",
headers={"X-API-Key": API_KEY},
)
Vet a Hyperliquid trader before copying: win rate, drawdown, profit factor, hold time
Check win rate, drawdown, profit factor and hold time of a Hyperliquid wallet before following it.
- Uses
/leaderboard,/liquidations,/wallets/{…},/wallets/{…}/trades/summary- Verified
- live API, 2026-11-03: 4 calls, all 200
The code
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# 30-day record of the wallet at the top of the 30-day PnL board
r = requests.get(
"https://data.vike.io/v1/hyperliquid/wallets/0xfc667adba8d4837586078f4fdcdc29804337ca06?window=30d",
headers={"X-API-Key": API_KEY},
)
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# medians beside means, from the 30-day trade summary
r = requests.get(
"https://data.vike.io/v1/hyperliquid/wallets/0xfc667adba8d4837586078f4fdcdc29804337ca06/trades/summary?window=30d",
headers={"X-API-Key": API_KEY},
)
# then: jq '.overall | {trade_count, closed_trade_count, open_trade_count, win_rate, profit_factor, median_duration, avg_duration, median_gain, avg_gain, median_loss, avg_loss}'
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# the 30-day PnL board, wallets with at least 50 closed trades
r = requests.get(
"https://data.vike.io/v1/hyperliquid/leaderboard?window=30d&sort=pnl&min_trades=50&limit=25",
headers={"X-API-Key": API_KEY},
)
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# liquidations recorded for the wallet
r = requests.get(
"https://data.vike.io/v1/hyperliquid/liquidations?wallet=0xfc667adba8d4837586078f4fdcdc29804337ca06&limit=100",
headers={"X-API-Key": API_KEY},
)
How many other wallets hold the same position as this leaderboard trader? (crowding behind a copied wallet)
List the trader's open positions with /positions?wallet=, then pass one position's symbol, side, leverage and entry price to /positions/match; every other address in the matches holds the same trade.
- Uses
/positions,/positions/match,/wallets/{…}- Verified
- live API, 2026-11-03: 4 calls, all 200
The code
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# who else holds a 10x NEAR long entered at 4.45238, the position of the 30-day PnL leader
r = requests.get(
"https://data.vike.io/v1/hyperliquid/positions/match?symbol=NEAR&side=long&leverage=10&entry_price=4.45238263796759&limit=50",
headers={"X-API-Key": API_KEY},
)
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# the trader's open positions with symbol, direction, leverage and entry price
r = requests.get(
"https://data.vike.io/v1/hyperliquid/positions?wallet=0xfc667adba8d4837586078f4fdcdc29804337ca06&limit=200",
headers={"X-API-Key": API_KEY},
)
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# how many other wallets match, and their combined notional
r = requests.get(
"https://data.vike.io/v1/hyperliquid/positions/match?symbol=NEAR&side=long&leverage=10&entry_price=4.45238263796759&limit=50",
headers={"X-API-Key": API_KEY},
)
# then: jq '[.matches[] | select(.address != "0xfc667adba8d4837586078f4fdcdc29804337ca06")] | {others: length, others_usd: (map(.size_usd) | add)}'
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# one matching wallet's 30-day record
r = requests.get(
"https://data.vike.io/v1/hyperliquid/wallets/0x8c10d62907917967b17cdec0081ce42ae09eed8e?window=30d",
headers={"X-API-Key": API_KEY},
)
Poll every Hyperliquid coin once a minute and flag volume spikes
Call /v1/hyperliquid/ohlcv/latest once a minute; it returns the newest 1-minute candle of every symbol that traded in the last hour, and you compare each candle's volume_usd with that symbol's own recent median.
- Uses
/ohlcv,/ohlcv/latest- Verified
- live API, 2026-11-03: 4 calls, all 200
The code
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# the newest 1-minute candle of every perp on every dex: run this once a minute
r = requests.get(
"https://data.vike.io/v1/hyperliquid/ohlcv/latest",
headers={"X-API-Key": API_KEY},
)
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# the newest candle of every XYZ market
r = requests.get(
"https://data.vike.io/v1/hyperliquid/ohlcv/latest?dex=XYZ",
headers={"X-API-Key": API_KEY},
)
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# the last 60 one-minute BTC candles
r = requests.get(
"https://data.vike.io/v1/hyperliquid/ohlcv?symbol=BTC&interval=1m&limit=60",
headers={"X-API-Key": API_KEY},
)
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# the last five 1-minute candles of MORPHO
r = requests.get(
"https://data.vike.io/v1/hyperliquid/ohlcv?symbol=MORPHO&interval=1m&limit=5",
headers={"X-API-Key": API_KEY},
)
Hyperliquid bots FAQ
Can I build a Hyperliquid trading bot in Python?
How do I copy trade on Hyperliquid?
hl.fills for that wallet; each fill says whether it opened, closed, flipped, increased or reduced the position.How do I pick a trader to copy?
Can I get Hyperliquid alerts in Telegram?
hl.fills and post each fill to your chat (the Telegram bot above).