Skip to content

Repository files navigation

Hyperliquid Market Maker v1.2 — OP Perpetual Futures

A fully automated market maker bot for OP perpetual futures on Hyperliquid, using Binance as the fair value oracle.

Built in Python with async architecture, REST-based fill detection, and real-time risk management.


How It Works

The bot continuously quotes bid and ask orders around the Binance mid price on Hyperliquid's OP-PERP market. Every order uses ALO (Add Liquidity Only) to guarantee 100% maker fills — no taker fees during normal operation.

Binance OP/USDT (fair value)
        │
        ▼
┌───────────────────────────────────┐
│         QuoteCalculator           │
│  spread + quadratic skew + clamp  │
├───────────────────────────────────┤
│         MarketMaker Engine        │
│  place/cancel ALO · fill polling  │
│  force-close · inventory sync     │
├───────────────────────────────────┤
│         Risk Management           │
│  daily loss · stale price guard   │
│  max inventory · capital check    │
└───────────────────────────────────┘
        │
        ▼
  Hyperliquid OP-PERP (execution)

Quoting Cycle (every 2 seconds)

  1. Read fair value — latest OP price from Binance Futures WebSocket
  2. Stale price guard — if no Binance update in 10s, cancel all quotes
  3. Poll fills — REST API call to detect new fills (replaces broken HL WebSocket)
  4. Force-close check — if inventory exceeds 80% of max, send taker IOC to reduce
  5. Calculate quotes — QuoteCalculator computes optimal bid/ask with spread + skew
  6. Place/reprice orders — cancel and replace only if price moved > 2 ticks
  7. Periodic inventory sync — every 30 cycles, verify local inventory matches exchange

Architecture

Core Files

File Lines Description
hl_config.py ~130 All parameters: spread, skew, inventory limits, fees, credentials
hl_analysis.py ~190 QuoteCalculator — spread calculation, quadratic inventory skew, book clamping
hl_execution.py ~750 MarketMaker class — order management, REST fill polling, force-close, PnL tracking
hl_main.py ~500 Entry point — 2 async tasks: Binance WebSocket + MM loop, dashboard export

Dashboard & Monitoring

File Description
dashboard.html Dark theme live dashboard with WebSocket auto-refresh
dashboard_server.py HTTP server (port 8080) + WebSocket (port 8081) for real-time state
hl_discord_notifier.py Discord webhook notifications for fills and alerts

Utility Scripts

File Description
check_position.py View current position, open orders, and recent fills
check_wallet.py View account balance (perp + spot USDC)
_balance.py Quick balance check
_close_position.py Manually close a position via market order
_force_close.py Force-close with IOC order
_check.py Quick connectivity check

Strategy Details

Spread

The bot quotes with a minimum spread of 8 basis points (0.08%) around the Binance mid price. With a maker fee of 1.44 bps on Hyperliquid, this yields a theoretical ~5.12 bps net profit per round-trip (buy maker fill + sell maker fill).

                  ◄─── 8 bps spread ───►
                  │                     │
    our bid ──── FV (Binance) ──── our ask
   $0.12265     $0.12270          $0.12275

Quadratic Inventory Skew

When the bot accumulates inventory (directional exposure), it shifts both quotes to incentivize fills that reduce the position. The skew is quadratic — gentle at low inventory, very aggressive at high inventory.

norm_inventory = (net_inventory × fair_value) / max_inventory_usd  # [-4, +4]
skew_bps = sign(norm) × norm² × 12.0  # Quadratic, 12 bps per unit²
Inventory (% of max) Skew Applied
25% 0.75 bps (barely noticeable)
50% 3.0 bps (gentle push)
100% 12.0 bps (strong push)
200% 48.0 bps (very aggressive)

Force-Close Taker

When inventory exceeds 80% of max ($20 of $25), the bot sends an IOC reduce-only order as a taker to close the entire excess above 50% of max in one shot. This prevents runaway accumulation but costs taker fees (4.32 bps).

Book Clamping

Before placing orders, the bot checks that its quotes don't cross the Hyperliquid orderbook (which would cause ALO rejection). If the bid would cross the best ask, it clamps to the best bid. Same logic for the ask side.


Risk Management

Parameter Value Description
MM_MIN_SPREAD_BPS 8.0 Minimum spread in basis points
MM_INVENTORY_SKEW_BPS 12.0 Skew intensity (quadratic)
MM_MAX_INVENTORY_USD $25 Max directional exposure
MM_FORCE_CLOSE_PCT 80% Threshold to trigger taker force-close
MM_MAX_DAILY_LOSS_USD $2.00 Daily loss limit — stops quoting
MM_QUOTE_SIZE_USD $10.50 Notional per side (above HL $10 minimum)
MM_QUOTE_REFRESH_SEC 2.0s Cycle interval
MM_LEVERAGE 5x Cross-leverage on Hyperliquid

Safety Features

  • Stale price guard: cancels all quotes if Binance price is older than 10 seconds
  • Cancel-fail protection: if a cancel fails, verifies via REST if the order is still live before cleaning tracking (prevents double-orders)
  • Daily loss limit: stops quoting when PnL drops below -$2.00, resumes only when PnL returns to $0
  • Capital check: stops the bot entirely if USDC balance drops below $3
  • Double-read inventory sync: reads position twice with 1s delay to detect API inconsistencies
  • Atomic dashboard writes: uses temp file + rename to prevent corrupted reads

Setup

Prerequisites

  • Python 3.10+
  • Hyperliquid account with API wallet configured
  • Binance Futures WebSocket access (no API key needed, public stream)

Installation

git clone https://github.com/TheFraTo/Trading-bot-v1.2.git
cd Trading-bot-v1.2

pip install hyperliquid-python-sdk websockets aiohttp python-dotenv eth-account

cp .env.example .env
# Edit .env with your credentials

Configuration

Create a .env file:

HL_WALLET_ADDRESS=0xYourMainWalletAddress
HL_PRIVATE_KEY=your_api_wallet_private_key
INITIAL_CAPITAL=10.0
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...  # optional

Running

# Start the bot
python hl_main.py

# In production (with screen)
screen -dmS hlbot bash -c "cd /path/to/bot && python3 hl_main.py 2>&1 | tee -a logs/system.log"

# Start the dashboard
screen -dmS dashboard bash -c "cd /path/to/bot && python3 dashboard_server.py"
# Access at http://your-server:8080

Monitoring

# Check position & open orders
python check_position.py

# Check balance
python check_wallet.py

# View live logs
tail -f logs/system.log

# Emergency close position
python _close_position.py

Technical Decisions

Why REST Fill Polling Instead of WebSocket?

The Hyperliquid Python SDK's WebSocket fill subscription (userFills) is unreliable — callbacks often never fire, causing the bot to miss fills entirely. We replaced it with REST polling via info.user_fills_by_time() every cycle, deduplicated by trade ID (TID). This adds ~1 API call per cycle but guarantees no fills are missed.

Why Binance as Fair Value?

Binance Futures has the deepest OP/USDT liquidity globally. Using its aggTrade stream gives us a reliable, low-latency fair value reference. The bot doesn't need a Binance API key — it connects to the public WebSocket stream.

Why Quadratic Skew?

Linear skew (constant bps per unit of inventory) doesn't punish large positions enough. With quadratic skew, holding 50% of max inventory costs only 3 bps, but holding 100% costs 12 bps and 200% costs 48 bps. This allows the bot to hold small positions comfortably while aggressively unwinding large ones.

Why ALO Only?

ALO (Add Liquidity Only) orders on Hyperliquid guarantee maker execution at 1.44 bps fee instead of taker at 4.32 bps. If the price moves and our order would cross the book, it's simply rejected — no accidental taker fills. The only taker orders come from force-close, which is an explicit risk management decision.


⚠️ Disclaimer — Read This Before Running

This bot is not designed to make significant money. It was built as a learning project to understand market making mechanics, order management, and exchange microstructure on Hyperliquid.

Why it struggles to be profitable

The core issue is economic, not technical. The code works correctly — the problem is the environment it operates in:

  1. Hyperliquid charges maker fees (1.44 bps). Most profitable market makers operate on exchanges that pay makers (rebates of 1-2 bps). On HL, every fill costs you money instead of earning it. Your spread needs to cover both sides' fees before you see any profit.

  2. OP at $0.12 with a $10 minimum order = no granularity. Each order is ~85 OP ($10.50), which is already 42% of the $25 max inventory. Two consecutive fills on the same side trigger a force-close in taker (4.32 bps fee + slippage). In live testing, the bot did 4 taker force-closes for every 1 complete maker round-trip — meaning it pays more in force-close fees than it earns in spread.

  3. The HL orderbook on OP is already extremely tight (~0.8 bps spread). Professional market makers with co-located servers and much larger capital are quoting at the inside. Our 8 bps spread puts us deep in the book where we only get filled by large directional sweeps — which is textbook adverse selection (the taker knows more than us about where the price is going).

  4. $10 of capital is not enough. With $10 at 5x leverage, the buying power is $50. The $10 minimum notional per order means each fill consumes a huge fraction of the portfolio. There's no room to absorb inventory without force-closing.

What capital do you actually need?

Capital Max Inventory Fill Size vs Max Force-Close Frequency Expected Outcome
$10 $25 42% Every 2 fills Guaranteed loss — force-close fees > spread profit
$50 $125 8.4% Every ~12 fills Breakeven possible — but fees still eat most profit
$200 $500 2.1% Rare Marginal profit possible — ~$0.05-0.20/day on a good day
$500+ $1,250+ 0.8% Very rare Realistic territory — force-closes nearly eliminated, spread capture works

Even at $500, the expected daily return is small (pennies to low single digits) because HL's negative maker fees and tight OP spread leave very little edge. To make meaningful money market making, you'd need either: positive maker rebates, a faster fair value signal, or significantly more capital on a coin with wider natural spreads.

Bottom line

Use this bot to learn how market making works — order management, inventory risk, adverse selection, fill detection, exchange APIs. The architecture and code are production-quality. The economics just don't work at this scale on this venue.


Known Limitations

  • Minimum notional: Hyperliquid requires $10 minimum per order. At OP ~$0.12, each order is ~85 OP ($10.50), which is 42% of the max inventory. This causes frequent force-closes.
  • Market microstructure: HL's OP market has ~0.8 bps spread with deep liquidity. Our 8 bps spread places us deep in the book, leading to asymmetric fills (adverse selection from directional sweeps).
  • Negative maker fees: Unlike exchanges with maker rebates, HL charges 1.44 bps per maker fill. This reduces the edge significantly.
  • Best suited for: higher capital ($200+) or higher-priced coins where $10 minimum is a small fraction of max inventory.

Changelog

v1.2 (2026-04-21)

  • Stale price guard: cancel quotes if Binance price > 10s old
  • Cancel-fail protection: REST verification before clearing order tracking
  • Aggressive force-close: close entire excess above 50% of max (was 1/3 of quote size)
  • Daily loss reset at $0: no more oscillation between stop/start (was -50%)
  • Cash flow fix: handle missing entryPx at startup with fair value proxy
  • Inventory sync safety: skip sync if no valid price available for cash flow adjustment

v1.1 (2026-04-20)

  • Spread increased from 5 to 8 bps
  • Inventory skew increased from 3 to 12 bps (quadratic)
  • Max inventory reduced from $40 to $25
  • Force-close taker mechanism added (80% threshold)
  • Daily loss limit increased from $0.50 to $2.00

v1.0 (2026-04-20)

  • Initial market maker on OP
  • REST fill polling (replaced broken HL WebSocket)
  • Double-read inventory sync at startup
  • ALO-only quoting with Binance fair value
  • Live dashboard with WebSocket auto-refresh

License

This project is for educational and personal use. Use at your own risk. Trading cryptocurrencies involves significant risk of loss.

About

A fully automated market maker bot for OP perpetual futures on Hyperliquid, using Binance as the fair value oracle. Built in Python with async architecture, REST-based fill detection, and real-time risk management.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages