Skip to main content
The Hyperliquid integration simulates perpetual futures (perps) with collateralized margin, funding rates, and liquidations. It supports long/short positions with leverage.

Configuration

HyperConfig

pub struct HyperConfig {
    pub token_quote: TokenId,        // Quote token for margin (e.g., USDC)
    pub taker_fee_bps: u32,          // Taker fee in basis points (e.g., 5 = 0.05%)
    pub maint_margin_bps: u32,       // Maintenance margin requirement (e.g., 300 = 3%)
    pub liq_fee_bps: u32,            // Liquidation fee (e.g., 100 = 1%)
}

Initialization

use ank_protocol_hyperliquid::{Hyperliquid, HyperConfig};
use ank_accounting::TokenId;

let hyper = Hyperliquid::new(
    HyperConfig {
        token_quote: TokenId(2), // USDC
        taker_fee_bps: 5,        // 0.05% taker fee
        maint_margin_bps: 300,   // 3% maintenance margin
        liq_fee_bps: 100,        // 1% liquidation fee
    },
    2000_000000000000000000, // initial ETH mark price (e18)
);

Available Actions

Margin Management

Description: Deposit quote token as margin.Parameters:
  • amount_e18 (u128): Amount to deposit
Example:
{"kind":"deposit","amount_e18":"10000000000000000000000"}
Effects:
  • Deducts amount_e18 quote from wallet
  • Increases user margin balance
Description: Withdraw margin (with maintenance check).Parameters:
  • amount_e18 (u128): Amount to withdraw
Example:
{"kind":"withdraw","amount_e18":"5000000000000000000000"}
Checks:
  • Post-withdrawal margin must remain >= maintenance requirement:
    margin - withdraw_amount >= notional * maint_margin_bps / 10000
    
Effects:
  • Credits amount_e18 quote to wallet
  • Reduces user margin balance

Trading

Description: Execute a market order (long or short).Parameters:
  • base_delta_e18 (i128): Signed base delta (positive = long, negative = short)
  • reduce_only (bool, optional): If true, only allows closing/reducing position
Example (long 1 ETH):
{
  "kind": "market_order",
  "base_delta_e18": "1000000000000000000",
  "reduce_only": false
}
Example (short 0.5 ETH):
{
  "kind": "market_order",
  "base_delta_e18": "-500000000000000000",
  "reduce_only": false
}
Mechanics:
  1. Execute at current mark price
  2. Charge taker fee on notional
  3. Realize PnL if crossing sides (close old position)
  4. Update position and entry price
  5. Check post-trade margin >= maintenance requirement
Effects:
  • Updates pos_base_e18 (signed position)
  • Updates entry_price_e18
  • Adjusts margin for fee and realized PnL
If reduce_only=true, order is capped to existing position size and cannot flip sides.
Description: Close up to a specified size towards flat.Parameters:
  • size_e18 (u128): Size to close (unsigned)
Example:
{"kind":"close","size_e18":"1000000000000000000"}
Mechanics:
  1. Close min(size_e18, abs(position))
  2. Realize PnL at mark price
  3. Charge taker fee
  4. Reduce position towards zero
Effects:
  • Reduces pos_base_e18
  • Adjusts margin for PnL and fee
  • Sets entry price to 0 if fully closed
Description: Liquidate an undercollateralized position.Parameters:
  • target (u32): User ID to liquidate
Example:
{"kind":"liquidate","target":2}
Eligibility:
margin_ratio_bps = (margin + unrealized_pnl) / notional * 10000
eligible if margin_ratio_bps < maint_margin_bps
Mechanics:
  1. Check target is eligible (margin ratio < maintenance)
  2. Close entire position at mark price
  3. Realize PnL
  4. Take liquidation fee from remaining margin → insurance fund
  5. Zero out position
Effects (target):
  • Position set to 0
  • Margin reduced by liq fee
Insurance fund:
  • Receives liquidation fee

Admin/Oracle

Description: Update mark price (oracle action).Parameters:
  • price_e18 (u128): New mark price
Example:
{"kind":"set_price","price_e18":"2500000000000000000000"}
Effects:
  • Updates mark price immediately
  • Affects unrealized PnL for all positions
Description: Update funding rate (admin/oracle action).Parameters:
  • funding_rate_ray_per_tick (i128): Signed funding rate (RAY format)
Example (longs pay shorts):
{"kind":"set_funding","funding_rate_ray_per_tick":"1000000000000000000000"}
Example (shorts pay longs):
{"kind":"set_funding","funding_rate_ray_per_tick":"-1000000000000000000000"}
Sign Convention:
  • + → longs pay shorts
  • - → shorts pay longs
Description: Update taker fee.Parameters:
  • taker_fee_bps (u32)
Example:
{"kind":"set_fees","taker_fee_bps":10}
Description: Update maintenance margin requirement.Parameters:
  • maint_margin_bps (u32)
Example:
{"kind":"set_maint","maint_margin_bps":500}

Funding Mechanism

Funding is applied on each tick via on_tick():
let rate = funding_rate_ray_per_tick; // signed
let payer_side = if rate > 0 { "longs" } else { "shorts" };

// Total funding = |rate| * payer_total_notional
// Collect proportionally from payers
// Distribute proportionally to receivers
// Dust goes to insurance fund

Example

Longs OI:  100 ETH @ $2000 = $200k notional
Shorts OI: 100 ETH @ $2000 = $200k notional
Funding rate: +0.01% per tick (longs pay)

Funding amount: 0.01% * $200k = $20
Each long pays proportionally: $20 * (my_notional / 200k)
Each short receives proportionally: $20 * (my_notional / 200k)

PnL Calculation

Unrealized PnL

// Long position
unrealized_pnl = pos_base * (mark_price - entry_price)

// Short position
unrealized_pnl = pos_base * (entry_price - mark_price)

Realized PnL

Realized when closing or reducing position:
// Long close
realized_pnl = closed_size * (mark_price - entry_price)

// Short close
realized_pnl = closed_size * (entry_price - mark_price)
Realized PnL is added/subtracted from margin immediately.

Margin Requirements

Maintenance Margin

maint_required = notional * maint_margin_bps / 10000

where:
  notional = abs(pos_base) * mark_price
Example: 3% maintenance on 100knotional=100k notional = 3k minimum margin.

Effective Margin

effective_margin = margin + unrealized_pnl (floored at 0)

Margin Ratio

margin_ratio_bps = (effective_margin / notional) * 10000
  • >= maint_margin_bps: Healthy
  • < maint_margin_bps: Liquidatable

View Methods

view_user

{
  "margin_e18": "10000000000000000000000",
  "pos_base_e18": "5000000000000000000",
  "entry_price_e18": "2000000000000000000000",
  "notional_e18": "10000000000000000000000",
  "maint_required_e18": "300000000000000000000",
  "margin_ratio_bps": 10000
}

view_market

{
  "price_e18": "2000000000000000000000",
  "funding_rate_ray_per_tick": "1000000000000000000000",
  "oi_long_base_e18": "100000000000000000000",
  "oi_short_base_e18": "100000000000000000000",
  "insurance_e18": "50000000000000000000",
  "taker_fee_bps": 5,
  "maint_margin_bps": 300,
  "liq_fee_bps": 100
}

Usage Examples

Open Long Position

use ank_protocol::{Action, Protocol};
use serde_json::json;

// 1. Deposit margin
hyper.execute(ts, user, Action::Custom(json!({
    "kind": "deposit",
    "amount_e18": "10000000000000000000000" // $10k USDC
})))?;

// 2. Long 5 ETH at $2000 (notional = $10k, 1x leverage)
hyper.execute(ts, user, Action::Custom(json!({
    "kind": "market_order",
    "base_delta_e18": "5000000000000000000", // 5 ETH
    "reduce_only": false
})))?;

Short with Leverage

// Deposit $1k, short 10 ETH at $2000 (notional = $20k, 20x leverage)
hyper.execute(ts, user, Action::Custom(json!({
    "kind": "deposit",
    "amount_e18": "1000000000000000000000"
})))?;

hyper.execute(ts, user, Action::Custom(json!({
    "kind": "market_order",
    "base_delta_e18": "-10000000000000000000", // -10 ETH
    "reduce_only": false
})))?;

Reduce-Only Close

// Close half of 5 ETH long position
hyper.execute(ts, user, Action::Custom(json!({
    "kind": "market_order",
    "base_delta_e18": "-2500000000000000000", // -2.5 ETH
    "reduce_only": true
})))?;

Liquidation Bot

// Scan for liquidatable users
for user_id in user_ids {
    let user_view = hyper.view_user(user_id);
    let mr_bps: u64 = user_view["margin_ratio_bps"].as_u64().unwrap();
    let maint_bps: u32 = 300; // from config
    
    if mr_bps < maint_bps as u64 {
        // Attempt liquidation
        let result = hyper.execute(ts, liquidator_id, Action::Custom(json!({
            "kind": "liquidate",
            "target": user_id
        })));
        
        match result {
            Ok(_) => println!("Liquidated user {}", user_id),
            Err(e) => eprintln!("Liquidation failed: {}", e),
        }
    }
}

Protocol-Specific Behavior

Entry Price Updates

When increasing position size (same side):
// Weighted average entry price
new_entry = (old_size * old_entry + add_size * mark_price) / new_size
When reducing/flipping:
// Realize PnL on closed portion
// Entry price unchanged if partial close
// Entry price = mark if flip to opposite side

Funding Symmetry

Funding is zero-sum (excluding dust to insurance):
Σ(paid by payers) = Σ(received by receivers) + dust
This conserves total margin in the system.

Liquidation Socialization

If liquidation leaves negative margin (underwater), shortfall goes to insurance fund:
if final_margin < liq_fee {
    insurance += final_margin
    user_margin = 0
} else {
    insurance += liq_fee
    user_margin = final_margin - liq_fee
}

Limitations

The Hyperliquid simulation does not model:
  • Order book (all trades at mark price)
  • Limit orders / TP/SL
  • Multi-asset perps (single market only)
  • Isolated margin (cross margin only)
  • Maker rebates (taker-only fees)
  • Funding rate caps
  • Insurance fund auto-deleveraging (ADL)
  • Vault strategies
  • Points/HLP incentives
  • L1 gas costs
Use this model to test leverage strategies and liquidation risk, not for HFT or market-making.

Advanced: Funding Arbitrage

// Monitor funding rate
let market = hyper.view_market();
let funding_rate: i128 = market["funding_rate_ray_per_tick"].as_str().unwrap().parse()?;

if funding_rate > threshold_positive {
    // Longs pay shorts → go short to collect funding
    hyper.execute(ts, user, Action::Custom(json!({
        "kind": "market_order",
        "base_delta_e18": "-10000000000000000000"
    })))?;
    
    // Hedge on spot market (not simulated here)
    // spot.buy_eth(...);
    
} else if funding_rate < threshold_negative {
    // Shorts pay longs → go long to collect funding
    hyper.execute(ts, user, Action::Custom(json!({
        "kind": "market_order",
        "base_delta_e18": "10000000000000000000"
    })))?;
    
    // Hedge on spot market
    // spot.sell_eth(...);
}

See Also

Build docs developers (and LLMs) love