Skip to content

Commit b20c279

Browse files
Merge pull request #10 from elijahhampton/feature/cli-exchange-api
Adds basic order types to CLI and fixes Decimal type deserialization
2 parents 7940fd6 + b40a773 commit b20c279

28 files changed

+902
-297
lines changed

CHANGELOG.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@
44

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

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

1619
## [0.1.0] - 2025-12-10
1720

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ futures-util = { version = "0.3.31", features = ["sink"] }
3434
once_cell = "1.19"
3535
reqwest = { version = "=0.12.23", features = ["json", "rustls-tls"] }
3636
rmp-serde = "1.3.0"
37+
serde_with = "3"
3738
rust_decimal = { version = "1.35", features = ["serde", "serde-float", "serde-str", "serde-with-str"] }
3839
serde = { version = "1.0", features = ["derive"] }
3940
serde_json = { version = "1.0", features = ["preserve_order"] }

README.md

Lines changed: 163 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ More concretely, our principles are:
1111

1212
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.
1313

14-
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.
14+
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.
1515

16-
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.
16+
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.
1717

1818
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.
1919

20-
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.
20+
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.
2121

2222
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.
2323

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

6060
### CLI Usage
6161

62-
The CLI provides quick access to market data and account information from your terminal.
62+
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.
6363
```bash
6464
# Run CLI commands
6565
cargo run --bin cli --features=cli -- [OPTIONS] <COMMAND>
@@ -90,39 +90,130 @@ cargo run --bin cli --features=cli -- candle-snapshot \
9090
cargo run --bin cli --features=cli -- vault-details --vault-address 0x...
9191
```
9292

93-
#### CLI Options
94-
95-
**Global Flags:**
96-
- `--network <mainnet|testnet>` - Network to connect to (default: mainnet)
97-
- `--allow-signer-key-env` - Allow reading private key from `HL_PRIVATE_KEY` environment variable
98-
99-
**Available Commands:**
100-
- `all-mids` - Get mid prices for all assets
101-
- `open-orders` - Get user's open orders
102-
- `frontend-open-orders` - Get frontend-formatted open orders
103-
- `user-fills` - Get user's fill history
104-
- `user-fills-by-time` - Get fills within time range
105-
- `user-rate-limit` - Check user's rate limit status
106-
- `order-status` - Get status of specific order
107-
- `l2-book` - Get L2 orderbook snapshot
108-
- `candle-snapshot` - Get historical candle data
109-
- `historical-orders` - Get user's historical orders
110-
- `sub-accounts` - Get user's sub-accounts
111-
- `vault-details` - Get vault information
112-
- `user-vault-equities` - Get user's vault equity
113-
- `user-role` - Get user's role information
114-
- `portfolio` - Get user's portfolio
115-
- `referral` - Get user's referral information
116-
- `user-fees` - Get user's fee information
93+
### WebSocket Subscriptions
94+
95+
Subscribe to real-time data feeds using the WebSocket interface:
96+
```bash
97+
# Subscribe to open orders updates (requires HL_PRIVATE_KEY env var)
98+
export HL_PRIVATE_KEY=your_private_key_here
99+
RUST_LOG=info cargo run --bin cli --features=cli -- \
100+
--subscriptions true \
101+
subscribe-open-orders \
102+
--user 0xYourAddress
103+
104+
# Subscribe to order book updates
105+
cargo run --bin cli --features=cli -- \
106+
--subscriptions true \
107+
subscribe-l2-book \
108+
--coin BTC
109+
110+
# Subscribe to all mid prices
111+
cargo run --bin cli --features=cli -- \
112+
--subscriptions true \
113+
subscribe-all-mids
114+
115+
# Subscribe to trades
116+
cargo run --bin cli --features=cli -- \
117+
--subscriptions true \
118+
subscribe-trades \
119+
--coin ETH
120+
```
121+
122+
WebSocket subscriptions stream live updates until interrupted with `Ctrl+C`.
123+
124+
## CLI Reference
125+
126+
### Global Options
127+
128+
| Flag | Description | Default |
129+
|------|-------------|---------|
130+
| `--network <mainnet\|testnet>` | Network to connect to | `mainnet` |
131+
| `--allow-signer-key-env` | Allow reading private key from `HL_PRIVATE_KEY` | `false` |
132+
| `--subscriptions <true\|false>` | Enable WebSocket subscription mode | `false` |
133+
134+
### Commands
135+
136+
#### Exchange API
137+
138+
| Command | Description |
139+
|---------|-------------|
140+
| `order` | Place an order |
141+
| `cancel` | Cancel an order by its order id |
142+
143+
#### Market Data
144+
145+
| Command | Description |
146+
|---------|-------------|
147+
| `all-mids` | Get mid prices for all assets |
148+
| `l2-book` | Get L2 orderbook snapshot |
149+
| `candle-snapshot` | Get historical candle data |
150+
151+
#### Account & Portfolio
152+
153+
| Command | Description |
154+
|---------|-------------|
155+
| `open-orders` | Get user's open orders |
156+
| `frontend-open-orders` | Get frontend-formatted open orders |
157+
| `user-fills` | Get user's fill history |
158+
| `user-fills-by-time` | Get fills within time range |
159+
| `user-rate-limit` | Check user's rate limit status |
160+
| `order-status` | Get status of specific order |
161+
| `historical-orders` | Get user's historical orders |
162+
| `portfolio` | Get user's portfolio |
163+
| `user-fees` | Get user's fee information |
164+
| `sub-accounts` | Get user's sub-accounts |
165+
| `user-role` | Get user's role information |
166+
| `referral` | Get user's referral information |
167+
168+
#### Vault Operations
169+
170+
| Command | Description |
171+
|---------|-------------|
172+
| `vault-details` | Get vault information |
173+
| `user-vault-equities` | Get user's vault equity |
174+
175+
#### WebSocket Subscriptions
176+
177+
**Market Data Streams**
178+
179+
| Command | Description |
180+
|---------|-------------|
181+
| `subscribe-all-mids` | Stream all mid prices |
182+
| `subscribe-l2-book` | Stream orderbook updates |
183+
| `subscribe-candle-snapshot` | Stream candle updates |
184+
| `subscribe-active-asset-ctx` | Stream active asset context |
185+
| `subscribe-bbo` | Stream best bid/offer updates |
186+
187+
**Account & Trading Streams**
188+
189+
| Command | Description |
190+
|---------|-------------|
191+
| `subscribe-open-orders` | Stream open orders updates |
192+
| `subscribe-user-events` | Stream user trading events |
193+
| `subscribe-user-fills` | Stream user fill updates |
194+
| `subscribe-user-funding` | Stream user funding updates |
195+
| `subscribe-user-non-funding-ledger-updates` | Stream non-funding ledger updates |
196+
| `subscribe-active-asset-data` | Stream active asset data for user |
197+
198+
**Advanced Streams**
199+
200+
| Command | Description |
201+
|---------|-------------|
202+
| `subscribe-notifications` | Stream user notifications |
203+
| `subscribe-web-data3` | Stream web data updates |
204+
| `subscribe-twap-states` | Stream TWAP order states |
205+
| `subscribe-clearinghouse-state` | Stream clearinghouse state |
206+
| `subscribe-user-twap-slice-fills` | Stream TWAP slice fills |
207+
| `subscribe-user-twap-history` | Stream TWAP order history |
117208

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

123-
## Trading
214+
## Trading Examples
124215

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

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

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

146237
### CLI Installation
147238
```bash
148-
# Install from source with CLI support
149-
cargo install --path . --features=cli --bin cli
239+
# Install from crates.io
240+
cargo install rhyperliquid --features=cli
150241

151-
# Or clone and build
242+
# Or install from source
152243
git clone https://github.com/elijahhampton/rhyperliquid.git
153244
cd rhyperliquid
154-
cargo build --release --features=cli --bin cli
155-
156-
# Binary will be at target/release/cli
245+
cargo install --path . --features=cli --bin cli
157246
```
158247

159-
### From Source
160-
161-
Clone and build the repository:
248+
### Building from Source
162249
```bash
163250
# Clone the repository
164251
git clone https://github.com/elijahhampton/rhyperliquid.git
@@ -177,13 +264,30 @@ cargo test --all-features
177264
cargo doc --open
178265
```
179266

267+
## Stability and API Guarantees
268+
269+
This crate is under active development.
270+
271+
- **Breaking changes** may occur between minor versions (0.1 → 0.2)
272+
- **Public API** is subject to refinement based on usage feedback
273+
- **Testnet testing** is strongly recommended before mainnet use
274+
275+
## Documentation
276+
277+
- [API Documentation](https://docs.rs/rhyperliquid)
278+
- [Hyperliquid Official Docs](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api)
279+
- [Examples](./examples)
280+
180281
## Getting Help
181-
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).
182282

183-
If the answer is not there:
283+
If you have questions:
184284

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

188292
### Minimum Supported Rust Version (MSRV)
189293

@@ -195,3 +299,18 @@ rustc --version
195299
# Update if needed
196300
rustup update stable
197301
```
302+
303+
## License
304+
305+
Licensed under either of:
306+
307+
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
308+
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
309+
310+
at your option.
311+
312+
## Contributing
313+
314+
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
315+
316+
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.

assets/rhyperliquid.png

98.3 KB
Loading

examples/advanced_order.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
3535

3636
// Get current price
3737
let all_mids = info_api.all_mids(None).await?;
38-
let doge_price = all_mids.get(&asset_id).ok_or(HyperliquidError::Internal(
38+
let doge_price = all_mids.0.get(&asset_id).ok_or(HyperliquidError::Internal(
3939
"Missing asset in universe".to_string(),
4040
))?;
4141
let price_decimal = Decimal::from_str(&doge_price.to_string())?;

examples/aligned_quote_token_status.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
#![allow(clippy::all)]
2-
use rhyperliquid::{
3-
example_helpers::{testnet_client, user},
4-
init_tracing::init_tracing,
5-
};
2+
use rhyperliquid::{example_helpers::testnet_client, init_tracing::init_tracing};
63

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

118
let hyperliquid = testnet_client()?;
12-
let user = user();
13-
14-
let status = hyperliquid.info().aligned_quote_token_status(&user).await?;
9+
let status = hyperliquid.info().aligned_quote_token_status(0).await?;
1510

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

examples/basic_order.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
3737

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

examples/leverage_position.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
3636
let btc_ctx = &asset_ctxs
3737
.get(asset_idx)
3838
.ok_or(HyperliquidError::Internal("Asset not found".to_string()))?;
39-
let mark_price = Decimal::from_str(&btc_ctx.mark_px)?;
39+
let mark_price = btc_ctx.mark_px;
4040

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

examples/spot_market_data.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
2323
.await?;
2424
tracing::info!("{:?}", spot_deploy_action_information);
2525

26-
let spot_pair_deploy_auction_information = hyperliquid
27-
.info()
28-
.spot_pair_deploy_auction_information()
29-
.await?;
30-
tracing::info!("{:?}", spot_pair_deploy_auction_information);
31-
32-
let token_information = hyperliquid
33-
.info()
34-
.token_information("0x00000000000000000000000000000000")
35-
.await?;
36-
tracing::info!("{:?}", token_information);
26+
// let spot_pair_deploy_auction_information = hyperliquid
27+
// .info()
28+
// .spot_pair_deploy_auction_information()
29+
// .await?;
30+
// tracing::info!("{:?}", spot_pair_deploy_auction_information);
31+
32+
// let token_information = hyperliquid
33+
// .info()
34+
// .token_information("0x00000000000000000000000000000000")
35+
// .await?;
36+
// tracing::info!("{:?}", token_information);
3737

3838
Ok(())
3939
}

0 commit comments

Comments
 (0)