DOCS v18
Documentation

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.

📡
Live Scanner
Real-time heatmap of all HL funding rates including NVDA, AAPL, GOLD, SPACEX.
✓ Fully Live
🧠
Regime Detection
HOT / NEUTRAL / COLD market signal from top-20 OI-weighted pairs.
✓ Fully Live
📊
Backtester
Replay any strategy on 180 days of real HL historical data with Sharpe + drawdown.
✓ Fully Live
🤖
Auto-Trader
Automated cycle engine — scans, enters, rotates, and exits. Full demo mode.
✓ Fully Live
Live Execution
Real orders via MetaMask, EIP-712 signed locally, submitted to Hyperliquid.
⚡ Beta
🧪
Demo Mode
Paper trading with live rate data. No wallet or capital required.
✓ Fully Live
💡
Why Hyperliquid?

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.

  1. 1
    Point at testnet

    In .env.local, set VITE_HL_REST_URL=https://api.hyperliquid-testnet.xyz

  2. 2
    Get testnet funds

    Visit app.hyperliquid-testnet.xyz and request test USDC from the faucet.

  3. 3
    Connect testnet wallet

    Connect MetaMask with a wallet that holds testnet funds. Osprey fetches your testnet balance automatically.

  4. 4
    Place a minimum-size order

    Select a liquid pair (BTC recommended), enter the smallest allowable size, and submit. Watch the raw response from the /exchange endpoint in devtools — the error message will identify any signing issue precisely.

  5. 5
    Confirm, then flip to mainnet

    Once the order appears in your testnet account, set VITE_HL_REST_URL back to the mainnet URL.

⚠️
Known issue — decimal precision

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

Label8h EquivalentAnnual Equiv.Signal
COLD< 0.01%< 11%Avoid
WARM0.01 – 0.04%11 – 44%Watch
HOT0.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.

RegimeWhat it meansRecommended 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.

SignalConditionMeaning
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

MetricDescription
Sharpe RatioRisk-adjusted return. Above 1.5 is good for this strategy type.
Max DrawdownWorst peak-to-trough equity decline. Used to size position risk.
Win RatePercentage of trades that netted positive after fees.
Break-even HoursHours a position must stay open to cover entry + exit fees.
Annualised YieldProjected return extrapolated from the simulation period.
⚠️
Synthetic fallback

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
⚠️
Rotation is intelligence-only in v18

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

ComponentStatusNotes
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

  1. 1
    Deposit USDC on Hyperliquid

    At app.hyperliquid.xyz — this is your trading capital.

  2. 2
    Connect MetaMask in Osprey Settings

    Osprey fetches your real HL balance automatically. Your private key never leaves MetaMask.

  3. 3
    Switch 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.

  4. 4
    Click Enter on a pair with ENTER signal

    Modal shows real balance, size slider, fee preview, break-even hours, and projected hourly income.

  5. 5
    Confirm → 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.

  6. 6
    Execute 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
ℹ️
Why chainId 1337?

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).

v17 round-trip ($5k notional, both taker) $3.50
v18 round-trip (Alo entry + Ioc exit) $2.25 −36%
v18 best case (Alo entry + Alo exit) $1.00 −71%

Volume tiers

30d VolumeMakerTaker
Default0.010%0.035%
≥ $5M0.008%0.030%
≥ $25M0.005%0.025%
≥ $100M0.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

LayerChoiceWhy
FrontendReact 18 + Vite + TypeScript (strict)Fast dev, strict types, 0 errors enforced
StateZustandLightweight, no boilerplate
DataHyperliquid REST APILive rates, 180d history, account state
SigningMetaMask + ethers v6EIP-712-variant, no key exposure
ChartsCanvas API (custom)Zero lib overhead
TestingVitest — 71 unit testsEngine + math + utilities
PWAService Worker + Web App ManifestInstallable, offline-capable
DeployVercelZero 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

#ItemStatus
1Verify EIP-712 signing on testnet with real small orderIn Progress
2Fix decimal precision using per-asset szDecimalsIn Progress
3Label live trading as Beta in UIDone
4Testnet toggle in Settings UIPlanned

Next 30 days

Telegram alert bot — ENTER signal fires on watchlisted pairsPlanned
Watchlist — save pairs, alert when threshold hitPlanned
Per-account fee tier auto-detection from HL user statePlanned

60–90 days

Backend service — Node.js + PostgreSQL for persistent positionsPlanned
Rotation strategy backtesting (hold vs rotate comparison)Planned
Mobile-optimised entry flowPlanned
GTC limit exit orders for full maker round-trips (−71% vs v17)Planned