REST API for executing orders on Hyperliquid exchange, built with Rust and Axum.
- Dual mode operation: Mock (testing) or Real (Hyperliquid SDK)
- Place orders: Maker (limit) or Taker (market) on Spot or Perp markets
- Order status query
- Automatic execution reports
- Extensible architecture for multiple exchanges
cargo build --release
cargo runThe server prompts for:
- Implementation mode: Mock or Real
- Network: Testnet or Mainnet (if Real mode)
We set in .env:
HYPERLIQUID_PRIVATE_KEY=0x...
HYPERLIQUID_TESTNET=true
HYPERLIQUID_USE_MOCK=falseServer runs on http://127.0.0.1:3000
Place an order.
Request Body:
{
"symbol": "BTC",
"side": "buy",
"size": 1.0,
"price": 50000.0,
"market_type": "perp"
}Fields:
symbol(string, required): Trading pair (e.g., "BTC", "ETH")side(string, required): "buy" or "sell"size(number, required): Order size (> 0)price(number, optional): Limit price. If omitted, order is Taker (market)market_type(string, required): "spot" or "perp"
Response:
{
"order_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "submitted"
}Examples:
Maker (Limit) order on Perp:
curl -X POST http://127.0.0.1:3000/orders \
-H "Content-Type: application/json" \
-d '{
"symbol": "BTC",
"side": "buy",
"size": 0.01,
"price": 50000.0,
"market_type": "perp"
}'Taker (Market) order on Spot:
curl -X POST http://127.0.0.1:3000/orders \
-H "Content-Type: application/json" \
-d '{
"symbol": "ETH",
"side": "buy",
"size": 0.01,
"market_type": "spot"
}'Market Orders (without price):
- Hyperliquid doesn't support true market orders, so we simulate them:
- Fetch current market price (required to set a limit price)
- Determine tick size (1.0 for price > $1000, 0.1 otherwise) - API rejects invalid tick sizes
- Set aggressive limit price (1% above/below market) - ensures immediate execution
- Round to tick size - satisfies API validation requirements
- Use IOC (Immediate Or Cancel) - executes immediately or cancels, mimicking market order behavior
Get order status.
Response:
{
"order_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "filled",
"filled_size": 1.0,
"avg_price": 49950.0
}Status Values: submitted, pending, partially_filled, filled, cancelled, rejected
Get execution reports for an order.
Response:
[
{
"report_id": "660e8400-e29b-41d4-a716-446655440001",
"order_id": "550e8400-e29b-41d4-a716-446655440000",
"execution_time": "2024-01-01T12:00:00Z",
"details": "Maker (Limit) order placed on Perp market: Buy 0.01 BTC at limit price 50000.0"
}
]- Minimum order value: $10 (Hyperliquid requirement)
- Market orders automatically fetch price and use IOC
- Limit orders use GTC (Good Till Cancelled)
- Master account trading: Don't set
HYPERLIQUID_VAULT_ADDRESS - Vault trading: Set
HYPERLIQUID_VAULT_ADDRESSto vault address
src/
├── api/ # HTTP handlers
├── models/ # Data structures
├── services/ # Business logic (Exchange trait)
└── storage/ # Execution report storage
Design Decisions:
- Exchange abstraction via
Exchangetrait (supports multiple exchanges) - Storage abstraction via
ExecutionReportStoragetrait (swap to DB easily) - Type-safe with Rust's type system
- Async with Tokio
cargo test- Database-backed storage (PostgreSQL/SQLite)
- Order status implementation using SDK's
user_state()anduser_fills() - Order cancellation endpoint
- WebSocket support for real-time order updates and public data ingestion (l2Book, candles, trades etc...)
- Multiple exchange support (Binance, Bybit)
- Add trading engine logic (market making, statarb)
- Order history endpoint
- Rate limiting
- Authentication/authorization
Based on the current implementation:
-
Extensibility: The trait-based design allows easy addition of new exchanges without modifying core API code.
-
Error Handling: Custom error types provide clear messages. Enhance this pattern for new features.
-
Testing: Maintain separation between mock and real implementations for testing.
-
Documentation: Keep API documentation minimal and focused on payloads. Implementation details belong in code comments only when necessary.
-
Performance: Current in-memory storage is fine for demo. For production, we must implement database-backed storage following the existing
ExecutionReportStoragetrait pattern.
axum: HTTP server frameworktokio: Async runtimehyperliquid_rust_sdk: Hyperliquid exchange SDKserde: Serialization/deserializationuuid: UUID generationthiserror: Error handlingasync-trait: Async trait supportchrono: Date/time handling