Funding rate
intelligence for
Hyperliquid
Osprey scans every Hyperliquid perpetual every minute, scores funding rate opportunities, and helps you execute delta-neutral positions via MetaMask — in demo or live mode.
HL pays funding every hour vs every 8h on Binance/Bybit — 8× more collection opportunities. It also lists TradFi perps (NVDA, AAPL, GOLD, WTIOIL, SPACEX) where rates are often extreme due to thinner liquidity. No other DEX has this combination.
🚀 Quick Start
Demo mode works immediately with no wallet or API key. Takes under 2 minutes.
# Clone and install
git clone https://github.com/Xtley001/osprey.git
cd osprey
npm install
# Copy env and start dev server
cp .env.example .env.local
npm run devbash
Opens at http://localhost:5173. The scanner fetches live HL data immediately. No configuration needed for demo mode.
Production build
npm run build # TypeScript compile + Vite bundle
npm run typecheck # tsc --noEmit — must report 0 errors
npm test # 71 unit tests across engine + math + utilitiesbash
Deploy to Vercel
npm i -g vercel
vercel --prod # vercel.json SPA rewrites pre-configuredbash
⚙️ Local Setup
Environment variables
Copy .env.example to .env.local and configure:
| Variable | Default | Description |
|---|---|---|
VITE_HL_REST_URL |
https://api.hyperliquid.xyz |
Hyperliquid REST endpoint. Change to testnet URL for testing. |
VITE_HL_WS_URL |
wss://api.hyperliquid.xyz/ws |
WebSocket endpoint (reserved for v2 streaming). |
VITE_ENABLE_REAL_TRADING |
true |
Set false to hide the Live Mode toggle. |
VITE_ENABLE_TESTNET |
true |
Set false to disable testnet switch. |
Fee tiers
If your 30-day HL volume qualifies for a reduced fee tier, update src/utils/constants.ts:
the backtester and all fee previews recalculate automatically.
export const DEFAULT_STRATEGY = {
takerFee: 0.00035, // ← update to match your volume tier
makerFee: 0.0001, // ← update to match your volume tier
// ... other strategy defaults
};typescript
🧪 Testnet Guide
Before using live order execution on mainnet, always verify signing works on HL testnet. This is a 2–4 hour process.
-
1Point at testnet
In
.env.local, setVITE_HL_REST_URL=https://api.hyperliquid-testnet.xyz -
2Get testnet funds
Visit
app.hyperliquid-testnet.xyzand request test USDC from the faucet. -
3Connect testnet wallet
Connect MetaMask with a wallet that holds testnet funds. Osprey fetches your testnet balance automatically.
-
4Place a minimum-size order
Select a liquid pair (BTC recommended), enter the smallest allowable size, and submit. Watch the raw response from the
/exchangeendpoint in devtools — the error message will identify any signing issue precisely. -
5Confirm, then flip to mainnet
Once the order appears in your testnet account, set
VITE_HL_REST_URLback to the mainnet URL.
The current order submission uses .toFixed(6) for price and size. HL's matching engine
uses per-asset szDecimals from the universe response. For assets with fewer decimal places
(e.g. BTC's price requires no trailing zeros), this may cause rejections. Use the testnet to verify before mainnet.
📡 Live Scanner
The scanner fetches the full metaAndAssetCtxs payload from Hyperliquid every 60 seconds and renders all active perpetual markets as a sortable, filterable heatmap.
Rate classification
| Label | 8h Equivalent | Annual Equiv. | Signal |
|---|---|---|---|
| COLD | < 0.01% | < 11% | Avoid |
| WARM | 0.01 – 0.04% | 11 – 44% | Watch |
| HOT | 0.04 – 0.10% | 44 – 110% | Consider entry |
| FIRE | > 0.10% | > 110% | Strong entry signal |
TradFi pairs
Osprey covers HL's full TradFi listing — stocks, commodities, and private-company perps. These often carry elevated funding due to thinner liquidity and smaller trader base.
Stocks: NVDA, AAPL, GOOGL, TSLA, AMZN, MSFT, META, NFLX, AMD, COIN, MSTR, PLTR, UBER
Private: SPACEX
Commodities: GOLD, SILVER, WTIOIL, NATGAS, COPPER
🧠 Regime Detection
The regime engine computes a market-wide signal by aggregating the top 20 perpetuals by open interest (OI). This distinguishes between a market where funding is structurally elevated vs. isolated spikes on one pair.
| Regime | What it means | Recommended action |
|---|---|---|
| HOT | Majority of top-OI pairs paying elevated funding. Strong systematic upward bias from leveraged longs. | Actively look for entries. Multiple pair exposure is appropriate. |
| NEUTRAL | Mixed market. Some pairs elevated, others flat. No strong directional bias. | Selective entries only. Wait for ENTER signal with persistence. |
| COLD | Low market-wide funding. Most shorts are not being paid meaningfully. | Exit existing positions, hold cash, wait for regime shift. |
🎯 Entry Signals
Each pair receives a per-pair signal requiring rate persistence — funding must remain elevated for at least 2 consecutive hours before an ENTER fires. This filters out transient spikes that don't persist long enough to cover fees.
| Signal | Condition | Meaning |
|---|---|---|
| ENTER | Rate ≥ threshold AND elevated for ≥ minHoursElevated | Open delta-neutral position now. |
| WAIT | Rate ≥ threshold BUT not yet persistent | Rate is elevated but too new. Watch — ENTER may fire next hour. |
| EXIT | Rate fell below exitRateThreshold | Close position. Funding no longer covers fees at this rate. |
| AVOID | Rate is negative | Shorts are paying longs. Do not enter a short-perp position. |
Default thresholds
entryRateThreshold: 0.0004 // per-hour rate (≈ 0.04% / 8h equivalent)
exitRateThreshold: 0.0002 // exit when rate falls below this
minHoursElevated: 2 // consecutive hours above threshold requiredtypescript
📊 Backtester
A pure TypeScript simulation engine. Hour-by-hour replay using real HL historical data from the fundingHistory API endpoint. No external dependencies — fully deterministic and testable.
What it calculates
| Metric | Description |
|---|---|
| Sharpe Ratio | Risk-adjusted return. Above 1.5 is good for this strategy type. |
| Max Drawdown | Worst peak-to-trough equity decline. Used to size position risk. |
| Win Rate | Percentage of trades that netted positive after fees. |
| Break-even Hours | Hours a position must stay open to cover entry + exit fees. |
| Annualised Yield | Projected return extrapolated from the simulation period. |
When HL's historical API is unreachable, the backtester falls back to rate data generated from real historical distributions. The synthetic data is calibrated to actual HL rate ranges but is not tick-level historical data. Results from real API data are more reliable.
🤖 Auto-Trader
The auto-trader runs a decision cycle every 60 seconds. In demo mode it executes automatically with no confirmation. In live mode, it prompts MetaMask for each order.
Cycle logic
// Every 60 seconds:
1. Fetch live rates from HL
2. Compute regime signal
3. For each open position: check EXIT signal → close if triggered
4. Check rotation: if a better pair > ROTATION_THRESHOLD better, flag rotation
5. Scan for new ENTER signals (up to maxPositions limit)
6. Place orders for new entriestypescript
The rotation engine shows the top 3 rotation targets and calculates whether rotation is profitable after fees. It does not automatically execute rotations. Full automated rotation requires a persistent backend service — planned for v2.
⚡ Live Trading Beta
Live order execution is functional but labeled Beta until signing has been verified on testnet. Everything else in Osprey is production-ready.
What works
| Component | Status | Notes |
|---|---|---|
| MetaMask connection | ✓ Live | Real wallet connect via ethers.BrowserProvider |
| Balance fetch | ✓ Live | Real HL account state via clearinghouseState |
| EIP-712 signing | ✓ Live | Correct domain + types. Verify decimal precision on testnet. |
| Asset index lookup | ✓ Fixed v18 | ensureCoinIndex() resolves correct HL universe index. |
| Order submission | ⚡ Beta | Testnet verification recommended before mainnet capital. |
Trading flow
-
1Deposit USDC on Hyperliquid
At
app.hyperliquid.xyz— this is your trading capital. -
2Connect MetaMask in Osprey Settings
Osprey fetches your real HL balance automatically. Your private key never leaves MetaMask.
-
3Switch sidebar to Live mode
The mode banner changes from demo to live. All signals now use your real balance as the ceiling for position sizing.
-
4Click Enter on a pair with ENTER signal
Modal shows real balance, size slider, fee preview, break-even hours, and projected hourly income.
-
5Confirm → MetaMask prompt → order sent
Osprey signs and submits the perp short to HL. MetaMask displays the EIP-712 typed data for your review before signing.
-
6Execute spot hedge manually
HL spot or a CEX for the long leg. This step is always manual — HL's spot market doesn't cover all perp pairs.
🔐 EIP-712 Signing
Osprey uses Hyperliquid's phantom agent signing scheme. The signer never sees the raw order — it signs a typed hash of a connectionId derived from the action.
// 1. Hash the serialised action JSON
const actionHash = ethers.keccak256(
ethers.toUtf8Bytes(JSON.stringify(action))
);
// 2. Build the connectionId
const connectionId = ethers.keccak256(
ethers.AbiCoder.defaultAbiCoder().encode(
['bytes32', 'uint64', 'address'],
[actionHash, BigInt(nonce), vaultAddress ?? ethers.ZeroAddress]
)
);
// 3. Sign the phantom agent — domain is always chainId 1337
const domain = {
name: 'Exchange', version: '1',
chainId: 1337, // HL uses 1337 regardless of actual chain
verifyingContract: '0x0000000000000000000000000000000000000000',
};
const types = { Agent: [
{ name: 'source', type: 'string' },
{ name: 'connectionId', type: 'bytes32' },
]};
const value = { source: 'a', connectionId };
const sig = await signer.signTypedData(domain, types, value);typescript
Hyperliquid's signing domain is hardcoded to chainId 1337 regardless of which chain the user's MetaMask is connected to.
This is an intentional HL protocol decision — do not change it to match the actual network.
💰 Fee Model
Osprey v18 uses minimum-fee order routing by default. Entry orders use post-only maker (Alo), exits use immediate-or-cancel taker (Ioc).
Volume tiers
| 30d Volume | Maker | Taker |
|---|---|---|
| Default | 0.010% | 0.035% |
| ≥ $5M | 0.008% | 0.030% |
| ≥ $25M | 0.005% | 0.025% |
| ≥ $100M | 0.002% | 0.020% |
If an Alo order is rejected by the matching engine (fast market, would immediately cross), Osprey's error message will include "Would immediately cross" — retry with tif: 'Ioc'.
🏗️ Architecture
src/
├── api/
│ └── hyperliquid.ts ← HL REST client, EIP-712 signing, order placement
├── engine/
│ ├── autotrader.ts ← cycle engine: enter/exit/rotate decision logic
│ ├── backtester.ts ← hourly simulation over historical funding + OHLCV
│ ├── regime.ts ← HOT/NEUTRAL/COLD market regime classifier
│ └── signals.ts ← per-pair ENTER/WAIT/EXIT/AVOID signal generator
├── store/
│ ├── appStore.ts ← wallet, mode, regime state (Zustand)
│ ├── autoTraderStore.ts ← auto-trader config + cycle runner
│ ├── positionStore.ts ← open positions (demo + live)
│ └── scannerStore.ts ← live funding rate cache
├── pages/ ← Scanner · PairDetail · Backtester · Portfolio · Analytics · Settings
├── components/ ← EntryModal · PositionTicker · AutoTraderService · …
└── types/ ← TypeScript interfaces for all domain objectstext
Tech stack
| Layer | Choice | Why |
|---|---|---|
| Frontend | React 18 + Vite + TypeScript (strict) | Fast dev, strict types, 0 errors enforced |
| State | Zustand | Lightweight, no boilerplate |
| Data | Hyperliquid REST API | Live rates, 180d history, account state |
| Signing | MetaMask + ethers v6 | EIP-712-variant, no key exposure |
| Charts | Canvas API (custom) | Zero lib overhead |
| Testing | Vitest — 71 unit tests | Engine + math + utilities |
| PWA | Service Worker + Web App Manifest | Installable, offline-capable |
| Deploy | Vercel | Zero config, instant deploys |
📋 Environment Reference
# .env.example — copy to .env.local
# Mainnet (default)
VITE_HL_REST_URL=https://api.hyperliquid.xyz
VITE_HL_WS_URL=wss://api.hyperliquid.xyz/ws
# Testnet — uncomment to test signing before mainnet
# VITE_HL_REST_URL=https://api.hyperliquid-testnet.xyz
# VITE_HL_WS_URL=wss://api.hyperliquid-testnet.xyz/ws
# Feature flags
VITE_ENABLE_REAL_TRADING=true # set false to hide Live Mode toggle
VITE_ENABLE_TESTNET=true # set false to disable testnet switchbash
🗺️ Roadmap
Immediate
| # | Item | Status |
|---|---|---|
| 1 | Verify EIP-712 signing on testnet with real small order | In Progress |
| 2 | Fix decimal precision using per-asset szDecimals | In Progress |
| 3 | Label live trading as Beta in UI | Done |
| 4 | Testnet toggle in Settings UI | Planned |
Next 30 days
| Telegram alert bot — ENTER signal fires on watchlisted pairs | Planned |
| Watchlist — save pairs, alert when threshold hit | Planned |
| Per-account fee tier auto-detection from HL user state | Planned |
60–90 days
| Backend service — Node.js + PostgreSQL for persistent positions | Planned |
| Rotation strategy backtesting (hold vs rotate comparison) | Planned |
| Mobile-optimised entry flow | Planned |
| GTC limit exit orders for full maker round-trips (−71% vs v17) | Planned |