Skip to main content

API Trading

Get your quoting system connected and operational.

Testnet API

Base URL: https://testnet-api.hypercall.xyz/ | Full reference: API Reference

Prerequisites

  • Wallet: An EVM wallet capable of signing EIP-712 typed messages
  • API Access: Base URL for the environment
  • Markets: At least one market must exist (check via GET /markets)

Step 1: Discover Markets

Query available instruments:

# List all markets (underlying + expiry groups)
curl https://testnet-api.hypercall.xyz/markets

# List instruments for a currency
curl "https://testnet-api.hypercall.xyz/instruments?currency=BTC"

# Get options summary with best bid/ask
curl "https://testnet-api.hypercall.xyz/options-summary?currency=BTC"

Response format: JSON-RPC envelope (Deribit-compatible) for instruments/options-summary.

See REST API for full endpoint details.

Step 2: Configure Authentication

Option A: Direct Wallet Signing

Sign orders directly with your trading wallet. Each order requires:

  • wallet: Your wallet address
  • nonce: Unique incrementing nonce
  • signature: EIP-712 signature of the order payload

See Authentication & Signing for exact signing requirements.

Use a dedicated signing key (agent) to place orders on behalf of your trading wallet:

# Approve an agent wallet to sign for your trading wallet
curl -X POST https://testnet-api.hypercall.xyz/approve-agent \
-H "Content-Type: application/json" \
-d '{
"agent": "0x...",
"nonce": 1,
"signature": "0x..."
}'

The agent wallet signs the approval message. Once approved, the agent can place/cancel orders for the trading wallet.

See Agent Authorization for full details.

Step 3: Start Quoting (Bulk Orders)

Preferred method for market makers: Use POST /bulk_order to place up to 50 orders per request.

curl -X POST https://testnet-api.hypercall.xyz/bulk_order \
-H "Content-Type: application/json" \
-d '{
"orders": [
{
"wallet": "0x...",
"symbol": "BTC-20250131-100000-C",
"price": "100.0",
"size": "0.1",
"side": "Buy",
"tif": "gtc",
"client_id": "mm-bid-1",
"mmp_enabled": true,
"nonce": 1,
"signature": "0x..."
},
{
"wallet": "0x...",
"symbol": "BTC-20250131-100000-C",
"price": "101.0",
"size": "0.1",
"side": "Sell",
"tif": "gtc",
"client_id": "mm-ask-1",
"mmp_enabled": true,
"nonce": 2,
"signature": "0x..."
}
]
}'

Critical constraints:

  • price and size MUST be strings (required for signature verification)
  • Price must have ≤ 5 significant figures (e.g., "12345" valid, "123456" rejected)
  • Size is human-readable; engine converts via multiplier 1_000_000 (truncates to integer)
  • Max 50 orders per bulk request

Response: Each order returns an OrderUpdateMessage with status (ACKED, OPEN, FILLED, REJECTED, etc.).

See REST API for full bulk order specification.

Step 4: Subscribe to WebSocket Feeds

Connect to real-time updates:

// Public connection (no auth)
const ws = new WebSocket('wss://testnet-api.hypercall.xyz/ws');

// Private connection (for order_updates, fills, portfolio)
const ws = new WebSocket('wss://testnet-api.hypercall.xyz/ws?wallet=0x...');

Subscribe to channels:

// Subscribe to orderbook updates
ws.send(JSON.stringify({ type: "Subscribe", channel: "orderbook" }));

// Subscribe to your order updates (requires ?wallet=)
ws.send(JSON.stringify({ type: "Subscribe", channel: "order_updates" }));

// Subscribe to fills (requires ?wallet=)
ws.send(JSON.stringify({ type: "Subscribe", channel: "fills" }));

// Subscribe to trades (public)
ws.send(JSON.stringify({ type: "Subscribe", channel: "trades" }));

// Subscribe to market updates (public)
ws.send(JSON.stringify({ type: "Subscribe", channel: "market_updates" }));

Important behavior:

  • order_updates intentionally omits PARTIALLY_FILLED status updates
  • Use the fills channel as the source of truth for partial fill tracking
  • No snapshot-on-subscribe; use REST GET /orderbook for initial state
  • The server sends websocket Ping frames every 20 seconds and requires Pong within 60 seconds
  • Browser clients answer automatically. Many websocket libraries, including tungstenite and tokio-tungstenite, also handle control-frame ping/pong automatically. Raw socket clients must implement Ping/Pong.

See WebSocket API for complete WebSocket protocol.

Step 5: Configure MMP (Market Maker Protection)

MMP protects against rapid adverse fills by canceling orders when limits are breached.

# Configure MMP for a wallet+currency
curl -X POST https://testnet-api.hypercall.xyz/mmp-config \
-H "Content-Type: application/json" \
-d '{
"wallet": "0x...",
"currency": "BTC",
"interval_ms": 60000,
"frozen_time_ms": 300000,
"qty_limit": 1000000,
"delta_limit": 10.0,
"vega_limit": 5.0,
"enabled": true,
"nonce": 1,
"signature": "0x..."
}'

MMP behavior (critical to understand):

  • Fills are accepted first, then MMP checks cumulative metrics
  • If limits breached: remaining quantity on active order stops, order becomes CANCELED with reason "MMP triggered during fill processing"
  • All other MMP-enabled orders for the same wallet+underlying are auto-canceled
  • Cancel reason: "Order canceled by MMP trigger"

See MMP for full MMP semantics.

Step 6: Monitor and Reconcile

Order Lifecycle

Orders transition through states:

  • ACKED: Accepted, queued for matching
  • OPEN: Resting on orderbook
  • PARTIALLY_FILLED: Partially executed (not sent on WS order_updates)
  • FILLED: Fully executed
  • CANCELED: Canceled by user or MMP
  • REJECTED: Rejected by engine (margin, tier, expired, etc.)

Reconciliation Rules

Truth sources:

  • Order status: GET /orders?wallet=... or WS order_updates (excludes PARTIALLY_FILLED)
  • Fills: GET /fills?wallet=... or WS fills channel (source of truth for partial fills)
  • Portfolio: GET /portfolio?wallet=... or WS portfolio channel

Rejection handling:

  • Most rejections return HTTP 200 with OrderUpdateMessage.status = "REJECTED"
  • Check reason field for exact rejection cause
  • Common reasons: "Insufficient margin: worst case margin=...", "Tier1 users cannot go short...", "Instrument has expired"

See Errors for handling rejections and error codes.

Step 7: Understand Margin Requirements

Before quoting, understand the margin model:

  • Portfolio margin: SPAN-based calculation across all positions and open orders
  • Stress scenarios: Worst-case loss computed across multiple scenarios (spot change, vol change, skew change, kurtosis change)
  • Pre-trade check: Orders rejected if worst_loss + cash < 0 in any scenario

See Margining for complete margin semantics.

Next Steps

Common Issues

"Signature verification failed"

  • Ensure price and size are strings (not numbers)
  • Verify string formatting matches exactly what was signed
  • Check nonce is correct and incrementing

"Insufficient margin"

  • Check portfolio via GET /portfolio?wallet=...
  • Review margin calculation in Margining
  • Ensure account has sufficient cash balance

"Tier1 users cannot go short"

  • Your wallet is set to tier1 (long-only)
  • Upgrade to tier2 via POST /user-tier (requires admin or signature)
  • Or ensure all sells are covered by filled long positions

No fills on WebSocket

  • Verify you connected with ?wallet=... for private channels
  • Check you subscribed to fills channel
  • Use GET /fills?wallet=... as fallback