Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@

### Added
- WebSocket streaming support for real-time market data
- Subscription APIs for orderbook, trades, candles, and user events
- CLI binary for terminal-based queries (`--features=cli`)
- CLI commands for market data and account management
- Adds complete support for Subscription API
- CLI binary for terminal-based queries (`--features cli`)
- CLI commands for order placement and cancellation
- Network selection via `--network` flag (mainnet/testnet)
- Environment-based authentication for CLI via `HL_PRIVATE_KEY`

### Changed
- Project status: WebSocket and CLI marked as complete
- Removes subscription command line arguments
- Fixes rust_decimal::Decimal serde deserialization for response types.
- Updates AllMids from 'type' to 'struct' to support correct response format.

## [0.1.0] - 2025-12-10

Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ futures-util = { version = "0.3.31", features = ["sink"] }
once_cell = "1.19"
reqwest = { version = "=0.12.23", features = ["json", "rustls-tls"] }
rmp-serde = "1.3.0"
serde_with = "3"
rust_decimal = { version = "1.35", features = ["serde", "serde-float", "serde-str", "serde-with-str"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
Expand Down
207 changes: 163 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ More concretely, our principles are:

1. **Type Safety & Financial Precision**: Every API response, order type, and market data structure is modeled in Rust's type system to catch invalid requests at compile time. We use `rust_decimal` for all financial calculations to eliminate floating-point errors. While network and API errors remain runtime concerns, the library prevents entire classes of invalid requests before they're sent.

2. **Correctness Over Speed**: rhyperliquid prioritizes getting trading operations right. This means proper EIP-712 signing implementation, correct msgpack serialization for action hashing, and careful handling of Hyperliquid's WebSocket heartbeat protocol. We optimize client-side performance where it matters, but understand that network latency to Hyperliquid's servers will dominate round-trip times.
2. **Correctness Over Speed**: rhyperliquid prioritizes getting trading operations right. This means proper EIP-712 signing implementation, correct msgpack serialization for action hashing, and careful handling of Hyperliquid's WebSocket heartbeat protocol.

3. **Modularity**: The crate is structured as composable components. Whether you need just the REST client for backtesting, WebSocket streams for live data, or the full CLI for manual trading, you can depend on only what you need. All public APIs are documented with examples showing real-world usage patterns.
3. **Modularity**: The crate is structured as composable components. Whether you need just the REST client for backtesting, WebSocket streams for live data, or the CLI for manual trading. All public APIs are documented with examples showing real-world usage patterns.

4. **Developer Experience**: We believe trading infrastructure should be approachable. The library provides builder patterns for configuration, comprehensive error messages that explain what went wrong, and examples organized by user workflows rather than individual functions. Both library users and CLI users should find the interface intuitive.

5. **Battle-Tested Foundations**: By leveraging proven crates (tokio for async, reqwest for HTTP, Alloy for Ethereum cryptography), we build on solid foundations rather than reinventing implementations. Our authentication follows Hyperliquid's exact specifications for both testnet and mainnet environments.
5. **Battle-Tested Foundations**: By leveraging proven crates (tokio for async, reqwest for HTTP, Alloy for Ethereum cryptography), we build on solid foundations rather than reinventing implementations. Our authentication follows Hyperliquid's specifications for both testnet and mainnet environments.

6. **Open & Extensible**: rhyperliquid is free open source software licensed under Apache/MIT. This enables anyone to build proprietary strategies, modify the client for their needs, or integrate it into larger systems without licensing concerns. We welcome contributions that improve reliability, add features, or enhance documentation.

Expand Down Expand Up @@ -59,7 +59,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

### CLI Usage

The CLI provides quick access to market data and account information from your terminal.
The CLI provides quick access to market data and account information from your terminal. Below you can find a list of all supported CLI commands. rhyperliquid intentionally only supports place_order and cancel_order (by order id) from the CLI. Other Exchange API commands will be added by request or open source contribution.
```bash
# Run CLI commands
cargo run --bin cli --features=cli -- [OPTIONS] <COMMAND>
Expand Down Expand Up @@ -90,39 +90,130 @@ cargo run --bin cli --features=cli -- candle-snapshot \
cargo run --bin cli --features=cli -- vault-details --vault-address 0x...
```

#### CLI Options

**Global Flags:**
- `--network <mainnet|testnet>` - Network to connect to (default: mainnet)
- `--allow-signer-key-env` - Allow reading private key from `HL_PRIVATE_KEY` environment variable

**Available Commands:**
- `all-mids` - Get mid prices for all assets
- `open-orders` - Get user's open orders
- `frontend-open-orders` - Get frontend-formatted open orders
- `user-fills` - Get user's fill history
- `user-fills-by-time` - Get fills within time range
- `user-rate-limit` - Check user's rate limit status
- `order-status` - Get status of specific order
- `l2-book` - Get L2 orderbook snapshot
- `candle-snapshot` - Get historical candle data
- `historical-orders` - Get user's historical orders
- `sub-accounts` - Get user's sub-accounts
- `vault-details` - Get vault information
- `user-vault-equities` - Get user's vault equity
- `user-role` - Get user's role information
- `portfolio` - Get user's portfolio
- `referral` - Get user's referral information
- `user-fees` - Get user's fee information
### WebSocket Subscriptions

Subscribe to real-time data feeds using the WebSocket interface:
```bash
# Subscribe to open orders updates (requires HL_PRIVATE_KEY env var)
export HL_PRIVATE_KEY=your_private_key_here
RUST_LOG=info cargo run --bin cli --features=cli -- \
--subscriptions true \
subscribe-open-orders \
--user 0xYourAddress

# Subscribe to order book updates
cargo run --bin cli --features=cli -- \
--subscriptions true \
subscribe-l2-book \
--coin BTC

# Subscribe to all mid prices
cargo run --bin cli --features=cli -- \
--subscriptions true \
subscribe-all-mids

# Subscribe to trades
cargo run --bin cli --features=cli -- \
--subscriptions true \
subscribe-trades \
--coin ETH
```

WebSocket subscriptions stream live updates until interrupted with `Ctrl+C`.

## CLI Reference

### Global Options

| Flag | Description | Default |
|------|-------------|---------|
| `--network <mainnet\|testnet>` | Network to connect to | `mainnet` |
| `--allow-signer-key-env` | Allow reading private key from `HL_PRIVATE_KEY` | `false` |
| `--subscriptions <true\|false>` | Enable WebSocket subscription mode | `false` |

### Commands

#### Exchange API

| Command | Description |
|---------|-------------|
| `order` | Place an order |
| `cancel` | Cancel an order by its order id |

#### Market Data

| Command | Description |
|---------|-------------|
| `all-mids` | Get mid prices for all assets |
| `l2-book` | Get L2 orderbook snapshot |
| `candle-snapshot` | Get historical candle data |

#### Account & Portfolio

| Command | Description |
|---------|-------------|
| `open-orders` | Get user's open orders |
| `frontend-open-orders` | Get frontend-formatted open orders |
| `user-fills` | Get user's fill history |
| `user-fills-by-time` | Get fills within time range |
| `user-rate-limit` | Check user's rate limit status |
| `order-status` | Get status of specific order |
| `historical-orders` | Get user's historical orders |
| `portfolio` | Get user's portfolio |
| `user-fees` | Get user's fee information |
| `sub-accounts` | Get user's sub-accounts |
| `user-role` | Get user's role information |
| `referral` | Get user's referral information |

#### Vault Operations

| Command | Description |
|---------|-------------|
| `vault-details` | Get vault information |
| `user-vault-equities` | Get user's vault equity |

#### WebSocket Subscriptions

**Market Data Streams**

| Command | Description |
|---------|-------------|
| `subscribe-all-mids` | Stream all mid prices |
| `subscribe-l2-book` | Stream orderbook updates |
| `subscribe-candle-snapshot` | Stream candle updates |
| `subscribe-active-asset-ctx` | Stream active asset context |
| `subscribe-bbo` | Stream best bid/offer updates |

**Account & Trading Streams**

| Command | Description |
|---------|-------------|
| `subscribe-open-orders` | Stream open orders updates |
| `subscribe-user-events` | Stream user trading events |
| `subscribe-user-fills` | Stream user fill updates |
| `subscribe-user-funding` | Stream user funding updates |
| `subscribe-user-non-funding-ledger-updates` | Stream non-funding ledger updates |
| `subscribe-active-asset-data` | Stream active asset data for user |

**Advanced Streams**

| Command | Description |
|---------|-------------|
| `subscribe-notifications` | Stream user notifications |
| `subscribe-web-data3` | Stream web data updates |
| `subscribe-twap-states` | Stream TWAP order states |
| `subscribe-clearinghouse-state` | Stream clearinghouse state |
| `subscribe-user-twap-slice-fills` | Stream TWAP slice fills |
| `subscribe-user-twap-history` | Stream TWAP order history |

Use `--help` on any command for detailed parameter information:
```bash
cargo run --bin cli --features=cli -- l2-book --help
```

## Trading
## Trading Examples

For order placement and cancellation examples, see [`examples/basic_order.rs`](examples/basic_order.rs).
Comprehensive examples demonstrating real trading workflows:

For account transfer examples, see [`examples/account_transfer.rs`](examples/account_transfer.rs).

Expand All @@ -139,26 +230,22 @@ For TWAP order placement see [`examples/twap_order.rs`](examples/twap_order.rs).
Add to your `Cargo.toml`:
```toml
[dependencies]
rhyperliquid = "0.1"
rhyperliquid = "0.2"
tokio = { version = "1.41", features = ["full"] }
```

### CLI Installation
```bash
# Install from source with CLI support
cargo install --path . --features=cli --bin cli
# Install from crates.io
cargo install rhyperliquid --features=cli

# Or clone and build
# Or install from source
git clone https://github.com/elijahhampton/rhyperliquid.git
cd rhyperliquid
cargo build --release --features=cli --bin cli

# Binary will be at target/release/cli
cargo install --path . --features=cli --bin cli
```

### From Source

Clone and build the repository:
### Building from Source
```bash
# Clone the repository
git clone https://github.com/elijahhampton/rhyperliquid.git
Expand All @@ -177,13 +264,30 @@ cargo test --all-features
cargo doc --open
```

## Stability and API Guarantees

This crate is under active development.

- **Breaking changes** may occur between minor versions (0.1 → 0.2)
- **Public API** is subject to refinement based on usage feedback
- **Testnet testing** is strongly recommended before mainnet use

## Documentation

- [API Documentation](https://docs.rs/rhyperliquid)
- [Hyperliquid Official Docs](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api)
- [Examples](./examples)

## Getting Help
If you have any questions, first see if the answer to your question can be found in the [Hyperliquid Docs](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api).

If the answer is not there:
If you have questions:

- Open a discussion with your question, or
- Open an issue with the bug
1. Check the [Hyperliquid API Documentation](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api)
2. Search existing [GitHub Issues](https://github.com/elijahhampton/rhyperliquid/issues)
3. Open a new [Discussion](https://github.com/elijahhampton/rhyperliquid/discussions) for questions
4. Open an [Issue](https://github.com/elijahhampton/rhyperliquid/issues/new) for bugs

## Requirements

### Minimum Supported Rust Version (MSRV)

Expand All @@ -195,3 +299,18 @@ rustc --version
# Update if needed
rustup update stable
```

## License

Licensed under either of:

- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)

at your option.

## Contributing

Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Binary file added assets/rhyperliquid.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion examples/advanced_order.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

// Get current price
let all_mids = info_api.all_mids(None).await?;
let doge_price = all_mids.get(&asset_id).ok_or(HyperliquidError::Internal(
let doge_price = all_mids.0.get(&asset_id).ok_or(HyperliquidError::Internal(
"Missing asset in universe".to_string(),
))?;
let price_decimal = Decimal::from_str(&doge_price.to_string())?;
Expand Down
9 changes: 2 additions & 7 deletions examples/aligned_quote_token_status.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
#![allow(clippy::all)]
use rhyperliquid::{
example_helpers::{testnet_client, user},
init_tracing::init_tracing,
};
use rhyperliquid::{example_helpers::testnet_client, init_tracing::init_tracing};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
init_tracing();

let hyperliquid = testnet_client()?;
let user = user();

let status = hyperliquid.info().aligned_quote_token_status(&user).await?;
let status = hyperliquid.info().aligned_quote_token_status(0).await?;

tracing::info!("{:?}", status);

Expand Down
2 changes: 1 addition & 1 deletion examples/basic_order.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

let asset_id = format!("@{}", idx);
let all_mids = info_api.all_mids(None).await?;
let doge_price = all_mids.get(&asset_id).ok_or(HyperliquidError::Internal(
let doge_price = all_mids.0.get(&asset_id).ok_or(HyperliquidError::Internal(
"Missing asset in universe".to_string(),
))?;

Expand Down
2 changes: 1 addition & 1 deletion examples/leverage_position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let btc_ctx = &asset_ctxs
.get(asset_idx)
.ok_or(HyperliquidError::Internal("Asset not found".to_string()))?;
let mark_price = Decimal::from_str(&btc_ctx.mark_px)?;
let mark_price = btc_ctx.mark_px;

tracing::info!("BTC mark price: ${}", mark_price);

Expand Down
22 changes: 11 additions & 11 deletions examples/spot_market_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.await?;
tracing::info!("{:?}", spot_deploy_action_information);

let spot_pair_deploy_auction_information = hyperliquid
.info()
.spot_pair_deploy_auction_information()
.await?;
tracing::info!("{:?}", spot_pair_deploy_auction_information);

let token_information = hyperliquid
.info()
.token_information("0x00000000000000000000000000000000")
.await?;
tracing::info!("{:?}", token_information);
// let spot_pair_deploy_auction_information = hyperliquid
// .info()
// .spot_pair_deploy_auction_information()
// .await?;
// tracing::info!("{:?}", spot_pair_deploy_auction_information);

// let token_information = hyperliquid
// .info()
// .token_information("0x00000000000000000000000000000000")
// .await?;
// tracing::info!("{:?}", token_information);

Ok(())
}
3 changes: 2 additions & 1 deletion examples/subscriptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let user = user();

subs.subscribe_all_mids(None).await?;
subs.subscribe_candle("BTC", "5m".to_string()).await?;
subs.subscribe_candle_snapshot("BTC", "5m".to_string())
.await?;
subs.subscribe_l2_book("BTC", None, None).await?;
subs.subscribe_trades("BTC").await?;
subs.subscribe_notifications(user.clone()).await?;
Expand Down
Loading