hyperliquid 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/docs/API.md ADDED
@@ -0,0 +1,115 @@
1
+ # API Reference
2
+
3
+ ## Info Methods
4
+
5
+ Read-only methods for querying market data and user information.
6
+
7
+ ### General Info
8
+
9
+ - `all_mids()` - Retrieve mids for all coins
10
+ - `open_orders(user)` - Retrieve a user's open orders
11
+ - `frontend_open_orders(user, dex: nil)` - Retrieve a user's open orders with additional frontend info
12
+ - `user_fills(user)` - Retrieve a user's fills
13
+ - `user_fills_by_time(user, start_time, end_time = nil)` - Retrieve a user's fills by time (optional end time)
14
+ - `user_rate_limit(user)` - Query user rate limits
15
+ - `order_status(user, oid)` - Query order status by order id (oid)
16
+ - `order_status_by_cloid(user, cloid)` - Query order status by client order id (cloid)
17
+ - `l2_book(coin)` - L2 book snapshot (Perpetuals and Spot)
18
+ - `candles_snapshot(coin, interval, start_time, end_time)` - Candle snapshot (Perpetuals and Spot)
19
+ - `max_builder_fee(user, builder)` - Check builder fee approval
20
+ - `historical_orders(user, start_time = nil, end_time = nil)` - Retrieve a user's historical orders
21
+ - `user_twap_slice_fills(user, start_time = nil, end_time = nil)` - Retrieve a user's TWAP slice fills
22
+ - `user_subaccounts(user)` - Retrieve a user's subaccounts
23
+ - `vault_details(vault_address, user = nil)` - Retrieve details for a vault
24
+ - `user_vault_equities(user)` - Retrieve a user's vault deposits
25
+ - `user_role(user)` - Query a user's role
26
+ - `portfolio(user)` - Query a user's portfolio
27
+ - `referral(user)` - Query a user's referral information
28
+ - `user_fees(user)` - Query a user's fees and fee schedule
29
+ - `delegations(user)` - Query a user's staking delegations
30
+ - `delegator_summary(user)` - Query a user's staking summary
31
+ - `delegator_history(user)` - Query a user's staking history
32
+ - `delegator_rewards(user)` - Query a user's staking rewards
33
+
34
+ ### Perpetuals Methods
35
+
36
+ - `perp_dexs()` - Retrieve all perpetual DEXs
37
+ - `meta(dex: nil)` - Get asset metadata (optionally for a specific perp DEX)
38
+ - `meta_and_asset_ctxs()` - Get extended asset metadata
39
+ - `user_state(user, dex: nil)` - Retrieve user's perpetuals account summary (optionally for a specific perp DEX)
40
+ - `predicted_fundings()` - Retrieve predicted funding rates across venues
41
+ - `perps_at_open_interest_cap()` - Query perps at open interest caps
42
+ - `perp_deploy_auction_status()` - Retrieve Perp Deploy Auction status
43
+ - `active_asset_data(user, coin)` - Retrieve a user's active asset data for a coin
44
+ - `perp_dex_limits(dex)` - Retrieve builder-deployed perp market limits for a DEX
45
+ - `user_funding(user, start_time, end_time = nil)` - Retrieve a user's funding history (optional end time)
46
+ - `user_non_funding_ledger_updates(user, start_time, end_time = nil)` - Retrieve a user's non-funding ledger updates. Non-funding ledger updates include deposits, transfers, and withdrawals. (optional end time)
47
+ - `funding_history(coin, start_time, end_time = nil)` - Retrieve historical funding rates (optional end time)
48
+
49
+ ### Spot Methods
50
+
51
+ - `spot_meta()` - Retrieve spot metadata (tokens and universe)
52
+ - `spot_meta_and_asset_ctxs()` - Retrieve spot metadata and asset contexts
53
+ - `spot_balances(user)` - Retrieve a user's spot token balances
54
+ - `spot_deploy_state(user)` - Retrieve Spot Deploy Auction information
55
+ - `spot_pair_deploy_auction_status()` - Retrieve Spot Pair Deploy Auction status
56
+ - `token_details(token_id)` - Retrieve information about a token by tokenId
57
+
58
+ ## Exchange Methods (Trading)
59
+
60
+ **Note:** Exchange methods require initializing the SDK with a `private_key`.
61
+
62
+ ### Order Placement
63
+
64
+ - `order(coin:, is_buy:, size:, limit_px:, ...)` - Place a single limit order
65
+ - `bulk_orders(orders:, grouping:, ...)` - Place multiple orders in a batch
66
+ - `market_order(coin:, is_buy:, size:, slippage:, ...)` - Place a market order with slippage
67
+
68
+ ### Order Modification
69
+
70
+ - `modify_order(oid:, coin:, is_buy:, size:, limit_px:, ...)` - Modify an existing order by oid or cloid
71
+ - `batch_modify(modifies:, ...)` - Modify multiple orders at once
72
+
73
+ ### Order Cancellation
74
+
75
+ - `cancel(coin:, oid:, ...)` - Cancel an order by order ID
76
+ - `cancel_by_cloid(coin:, cloid:, ...)` - Cancel an order by client order ID
77
+ - `bulk_cancel(cancels:, ...)` - Cancel multiple orders by order ID
78
+ - `bulk_cancel_by_cloid(cancels:, ...)` - Cancel multiple orders by client order ID
79
+ - `schedule_cancel(time:, ...)` - Auto-cancel all orders at a given time
80
+
81
+ ### Position Management
82
+
83
+ - `market_close(coin:, size:, slippage:, ...)` - Close a position at market price (auto-detects position size)
84
+ - `update_leverage(coin:, leverage:, is_cross:, ...)` - Set cross or isolated leverage for a coin
85
+ - `update_isolated_margin(coin:, amount:, ...)` - Add or remove isolated margin for a position
86
+
87
+ ### Other
88
+
89
+ - `address` - Get the wallet address associated with the private key
90
+
91
+ All exchange methods support an optional `vault_address:` parameter for vault trading.
92
+
93
+ ### Order Types
94
+
95
+ - `{ limit: { tif: 'Gtc' } }` - Good-til-canceled (default)
96
+ - `{ limit: { tif: 'Ioc' } }` - Immediate-or-cancel
97
+ - `{ limit: { tif: 'Alo' } }` - Add-liquidity-only (post-only)
98
+
99
+ ### Trigger Orders (Stop Loss / Take Profit)
100
+
101
+ Trigger orders execute when a price threshold is reached:
102
+
103
+ - `tpsl: 'sl'` - Stop loss
104
+ - `tpsl: 'tp'` - Take profit
105
+ - `is_market: true/false` - Execute as market or limit order when triggered
106
+
107
+ ### Client Order IDs (Cloid)
108
+
109
+ Client order IDs must be 16 bytes in hex format (`0x` + 32 hex characters).
110
+
111
+ Factory methods:
112
+ - `Hyperliquid::Cloid.from_int(n)` - Create from integer (zero-padded)
113
+ - `Hyperliquid::Cloid.from_str(s)` - Create from hex string
114
+ - `Hyperliquid::Cloid.from_uuid(uuid)` - Create from UUID
115
+ - `Hyperliquid::Cloid.random` - Generate random
@@ -0,0 +1,53 @@
1
+ # Configuration
2
+
3
+ ## Basic Options
4
+
5
+ ```ruby
6
+ # Custom timeout (default: 30 seconds)
7
+ sdk = Hyperliquid.new(timeout: 60)
8
+
9
+ # Enable retry logic for handling transient failures (default: disabled)
10
+ sdk = Hyperliquid.new(retry_enabled: true)
11
+
12
+ # Enable trading with a private key
13
+ sdk = Hyperliquid.new(private_key: ENV['HYPERLIQUID_PRIVATE_KEY'])
14
+
15
+ # Set global order expiration (orders expire after this timestamp)
16
+ expires_at_ms = (Time.now.to_f * 1000).to_i + 30_000 # 30 seconds from now
17
+ sdk = Hyperliquid.new(
18
+ private_key: ENV['HYPERLIQUID_PRIVATE_KEY'],
19
+ expires_after: expires_at_ms
20
+ )
21
+
22
+ # Combine multiple configuration options
23
+ sdk = Hyperliquid.new(
24
+ testnet: true,
25
+ timeout: 60,
26
+ retry_enabled: true,
27
+ private_key: ENV['HYPERLIQUID_PRIVATE_KEY'],
28
+ expires_after: expires_at_ms
29
+ )
30
+
31
+ # Check which environment you're using
32
+ sdk.testnet? # => false
33
+ sdk.base_url # => "https://api.hyperliquid.xyz"
34
+
35
+ # Check if exchange is available (private_key was provided)
36
+ sdk.exchange # => nil if no private_key, Hyperliquid::Exchange instance otherwise
37
+ ```
38
+
39
+ ## Retry Configuration
40
+
41
+ By default, retry logic is **disabled** for predictable API behavior. When enabled, the SDK will automatically retry requests that fail due to:
42
+
43
+ - Network connectivity issues (connection failed, timeouts)
44
+ - Server errors (5xx status codes)
45
+ - Rate limiting (429 status codes)
46
+
47
+ **Retry Settings:**
48
+ - Maximum retries: 2
49
+ - Base interval: 0.5 seconds
50
+ - Backoff factor: 2x (exponential backoff)
51
+ - Randomness: ±50% to prevent thundering herd
52
+
53
+ **Note:** Retries are disabled by default to avoid unexpected delays in time-sensitive trading applications. Enable only when you want automatic handling of transient failures.
@@ -0,0 +1,54 @@
1
+ # Development
2
+
3
+ ## Setup
4
+
5
+ After checking out the repo, run `bin/setup` to install dependencies.
6
+
7
+ ```bash
8
+ bin/setup
9
+ ```
10
+
11
+ ## Running Tests
12
+
13
+ ```bash
14
+ # Run all tests
15
+ rake spec
16
+
17
+ # Run tests and linting together (default)
18
+ rake
19
+ ```
20
+
21
+ ## Linting
22
+
23
+ ```bash
24
+ rake rubocop
25
+ ```
26
+
27
+ ## Interactive Console
28
+
29
+ ```bash
30
+ bin/console
31
+ ```
32
+
33
+ This opens an interactive prompt with the SDK loaded for experimentation.
34
+
35
+ ## Example Script
36
+
37
+ ```bash
38
+ ruby example.rb
39
+ ```
40
+
41
+ ## Integration Testing (Testnet)
42
+
43
+ For real trading tests on testnet:
44
+
45
+ ```bash
46
+ # Get testnet funds from: https://app.hyperliquid-testnet.xyz
47
+ HYPERLIQUID_PRIVATE_KEY=0x... ruby test_integration.rb
48
+ ```
49
+
50
+ The integration test executes real trades on testnet:
51
+ 1. Spot market roundtrip (buy/sell PURR/USDC)
52
+ 2. Spot limit order (place and cancel)
53
+ 3. Perp market roundtrip (long/close BTC)
54
+ 4. Perp limit order (place short, cancel)
data/docs/ERRORS.md ADDED
@@ -0,0 +1,38 @@
1
+ # Error Handling
2
+
3
+ The SDK provides comprehensive error handling with typed exceptions.
4
+
5
+ ## Usage
6
+
7
+ ```ruby
8
+ begin
9
+ orders = sdk.info.open_orders(user_address)
10
+ rescue Hyperliquid::AuthenticationError
11
+ # Handle authentication issues
12
+ rescue Hyperliquid::RateLimitError
13
+ # Handle rate limiting
14
+ rescue Hyperliquid::ServerError
15
+ # Handle server errors
16
+ rescue Hyperliquid::NetworkError
17
+ # Handle network connectivity issues
18
+ rescue Hyperliquid::Error => e
19
+ # Handle any other Hyperliquid API errors
20
+ puts "Error: #{e.message}"
21
+ puts "Status: #{e.status_code}" if e.status_code
22
+ puts "Response: #{e.response_body}" if e.response_body
23
+ end
24
+ ```
25
+
26
+ ## Error Classes
27
+
28
+ | Error Class | Description |
29
+ |-------------|-------------|
30
+ | `Hyperliquid::Error` | Base error class |
31
+ | `Hyperliquid::ClientError` | 4xx errors |
32
+ | `Hyperliquid::ServerError` | 5xx errors |
33
+ | `Hyperliquid::AuthenticationError` | 401 errors |
34
+ | `Hyperliquid::BadRequestError` | 400 errors |
35
+ | `Hyperliquid::NotFoundError` | 404 errors |
36
+ | `Hyperliquid::RateLimitError` | 429 errors |
37
+ | `Hyperliquid::NetworkError` | Connection issues |
38
+ | `Hyperliquid::TimeoutError` | Request timeouts |
data/docs/EXAMPLES.md ADDED
@@ -0,0 +1,406 @@
1
+ # Examples
2
+
3
+ ## Info API
4
+
5
+ ### General Info
6
+
7
+ ```ruby
8
+ # Retrieve mids for all coins
9
+ mids = sdk.info.all_mids
10
+ # => { "BTC" => "50000", "ETH" => "3000", ... }
11
+
12
+ user_address = "0x..."
13
+
14
+ # Retrieve a user's open orders
15
+ orders = sdk.info.open_orders(user_address)
16
+ # => [{ "coin" => "BTC", "sz" => "0.1", "px" => "50000", "side" => "A" }]
17
+
18
+ # Retrieve a user's open orders with additional frontend info
19
+ frontend_orders = sdk.info.frontend_open_orders(user_address)
20
+ # => [{ "coin" => "BTC", "isTrigger" => false, ... }]
21
+
22
+ # Retrieve a user's fills
23
+ fills = sdk.info.user_fills(user_address)
24
+ # => [{ "coin" => "BTC", "sz" => "0.1", "px" => "50000", "side" => "A", "time" => 1234567890 }]
25
+
26
+ # Retrieve a user's fills by time
27
+ start_time_ms = 1_700_000_000_000
28
+ end_time_ms = start_time_ms + 86_400_000
29
+ fills_by_time = sdk.info.user_fills_by_time(user_address, start_time_ms, end_time_ms)
30
+ # => [{ "coin" => "ETH", "px" => "3000", "time" => start_time_ms }, ...]
31
+
32
+ # Query user rate limits
33
+ rate_limit = sdk.info.user_rate_limit(user_address)
34
+ # => { "nRequestsUsed" => 100, "nRequestsCap" => 10000 }
35
+
36
+ # Query order status by oid
37
+ order_id = 12345
38
+ status_by_oid = sdk.info.order_status(user_address, order_id)
39
+ # => { "status" => "filled", ... }
40
+
41
+ # Query order status by cloid
42
+ cloid = "client-order-id-123"
43
+ status_by_cloid = sdk.info.order_status_by_cloid(user_address, cloid)
44
+ # => { "status" => "cancelled", ... }
45
+
46
+ # L2 order book snapshot
47
+ book = sdk.info.l2_book("BTC")
48
+ # => { "coin" => "BTC", "levels" => [[asks], [bids]], "time" => ... }
49
+
50
+ # Candle snapshot
51
+ candles = sdk.info.candles_snapshot("BTC", "1h", start_time_ms, end_time_ms)
52
+ # => [{ "t" => ..., "o" => "50000", "h" => "51000", "l" => "49000", "c" => "50500", "v" => "100" }]
53
+
54
+ # Check builder fee approval
55
+ builder_address = "0x..."
56
+ fee_approval = sdk.info.max_builder_fee(user_address, builder_address)
57
+ # => { "approved" => true, ... }
58
+
59
+ # Retrieve a user's historical orders
60
+ hist_orders = sdk.info.historical_orders(user_address)
61
+ # => [{ "oid" => 123, "coin" => "BTC", ... }]
62
+ hist_orders_ranged = sdk.info.historical_orders(user_address, start_time_ms, end_time_ms)
63
+ # => []
64
+
65
+ # Retrieve a user's TWAP slice fills
66
+ twap_fills = sdk.info.user_twap_slice_fills(user_address)
67
+ # => [{ "sliceId" => 1, "coin" => "ETH", "sz" => "1.0" }, ...]
68
+ twap_fills_ranged = sdk.info.user_twap_slice_fills(user_address, start_time_ms, end_time_ms)
69
+ # => []
70
+
71
+ # Retrieve a user's subaccounts
72
+ subaccounts = sdk.info.user_subaccounts(user_address)
73
+ # => ["0x1111...", ...]
74
+
75
+ # Retrieve details for a vault
76
+ vault_addr = "0x..."
77
+ vault = sdk.info.vault_details(vault_addr)
78
+ # => { "vaultAddress" => vault_addr, ... }
79
+ vault_with_user = sdk.info.vault_details(vault_addr, user_address)
80
+ # => { "vaultAddress" => vault_addr, "user" => user_address, ... }
81
+
82
+ # Retrieve a user's vault deposits
83
+ vault_deposits = sdk.info.user_vault_equities(user_address)
84
+ # => [{ "vaultAddress" => "0x...", "equity" => "123.45" }, ...]
85
+
86
+ # Query a user's role
87
+ role = sdk.info.user_role(user_address)
88
+ # => { "role" => "tradingUser" }
89
+
90
+ # Query a user's portfolio
91
+ portfolio = sdk.info.portfolio(user_address)
92
+ # => [["day", { "pnlHistory" => [...], "vlm" => "0.0" }], ...]
93
+
94
+ # Query a user's referral information
95
+ referral = sdk.info.referral(user_address)
96
+ # => { "referredBy" => { "referrer" => "0x..." }, ... }
97
+
98
+ # Query a user's fees
99
+ fees = sdk.info.user_fees(user_address)
100
+ # => { "userAddRate" => "0.0001", "feeSchedule" => { ... } }
101
+
102
+ # Query a user's staking delegations
103
+ delegations = sdk.info.delegations(user_address)
104
+ # => [{ "validator" => "0x...", "amount" => "100.0" }, ...]
105
+
106
+ # Query a user's staking summary
107
+ summary = sdk.info.delegator_summary(user_address)
108
+ # => { "delegated" => "12060.16529862", ... }
109
+
110
+ # Query a user's staking history
111
+ history = sdk.info.delegator_history(user_address)
112
+ # => [{ "time" => 1_736_726_400_073, "delta" => { ... } }, ...]
113
+
114
+ # Query a user's staking rewards
115
+ rewards = sdk.info.delegator_rewards(user_address)
116
+ # => [{ "time" => 1_736_726_400_073, "source" => "delegation", "totalAmount" => "0.123" }, ...]
117
+ ```
118
+
119
+ **Note:** `l2_book` and `candles_snapshot` work for both Perpetuals and Spot. For spot, use `"{BASE}/USDC"` when available (e.g., `"PURR/USDC"`). Otherwise, use the index alias `"@{index}"` from `spot_meta["universe"]`.
120
+
121
+ ### Perpetuals
122
+
123
+ ```ruby
124
+ # Retrieve all perpetual DEXs
125
+ perp_dexs = sdk.info.perp_dexs
126
+ # => [nil, { "name" => "test", "full_name" => "test dex", ... }]
127
+
128
+ # Retrieve perpetuals metadata (optionally for a specific perp dex)
129
+ meta = sdk.info.meta
130
+ # => { "universe" => [...] }
131
+ meta = sdk.info.meta(dex: "perp-dex-name")
132
+ # => { "universe" => [...] }
133
+
134
+ # Retrieve perpetuals asset contexts (includes mark price, current funding, open interest, etc.)
135
+ meta_ctxs = sdk.info.meta_and_asset_ctxs
136
+ # => { "universe" => [...], "assetCtxs" => [...] }
137
+
138
+ # Retrieve user's perpetuals account summary (optionally for a specific perp dex)
139
+ state = sdk.info.user_state(user_address)
140
+ # => { "assetPositions" => [...], "marginSummary" => {...} }
141
+ state = sdk.info.user_state(user_address, dex: "perp-dex-name")
142
+ # => { "assetPositions" => [...], "marginSummary" => {...} }
143
+
144
+ # Retrieve a user's funding history or non-funding ledger updates (optional end_time)
145
+ funding = sdk.info.user_funding(user_address, start_time)
146
+ # => [{ "delta" => { "type" => "funding", ... }, "time" => ... }]
147
+ funding = sdk.info.user_funding(user_address, start_time, end_time)
148
+ # => [{ "delta" => { "type" => "funding", ... }, "time" => ... }]
149
+
150
+ # Retrieve historical funding rates
151
+ hist = sdk.info.funding_history("ETH", start_time)
152
+ # => [{ "coin" => "ETH", "fundingRate" => "...", "time" => ... }]
153
+
154
+ # Retrieve predicted funding rates for different venues
155
+ pred = sdk.info.predicted_fundings
156
+ # => [["AVAX", [["HlPerp", { "fundingRate" => "0.0000125", "nextFundingTime" => ... }], ...]], ...]
157
+
158
+ # Query perps at open interest caps
159
+ oi_capped = sdk.info.perps_at_open_interest_cap
160
+ # => ["BADGER", "CANTO", ...]
161
+
162
+ # Retrieve information about the Perp Deploy Auction
163
+ auction = sdk.info.perp_deploy_auction_status
164
+ # => { "startTimeSeconds" => ..., "durationSeconds" => ..., "startGas" => "500.0", ... }
165
+
166
+ # Retrieve User's Active Asset Data
167
+ aad = sdk.info.active_asset_data(user_address, "APT")
168
+ # => { "user" => user_address, "coin" => "APT", "leverage" => { "type" => "cross", "value" => 3 }, ... }
169
+
170
+ # Retrieve Builder-Deployed Perp Market Limits
171
+ limits = sdk.info.perp_dex_limits("builder-dex")
172
+ # => { "totalOiCap" => "10000000.0", "oiSzCapPerPerp" => "...", ... }
173
+ ```
174
+
175
+ ### Spot
176
+
177
+ ```ruby
178
+ # Retrieve spot metadata
179
+ spot_meta = sdk.info.spot_meta
180
+ # => { "tokens" => [...], "universe" => [...] }
181
+
182
+ # Retrieve spot asset contexts
183
+ spot_meta_ctxs = sdk.info.spot_meta_and_asset_ctxs
184
+ # => [ { "tokens" => [...], "universe" => [...] }, [ { "midPx" => "...", ... } ] ]
185
+
186
+ # Retrieve a user's token balances
187
+ balances = sdk.info.spot_balances(user_address)
188
+ # => { "balances" => [{ "coin" => "USDC", "token" => 0, "total" => "..." }, ...] }
189
+
190
+ # Retrieve information about the Spot Deploy Auction
191
+ deploy_state = sdk.info.spot_deploy_state(user_address)
192
+ # => { "states" => [...], "gasAuction" => { ... } }
193
+
194
+ # Retrieve information about the Spot Pair Deploy Auction
195
+ pair_status = sdk.info.spot_pair_deploy_auction_status
196
+ # => { "startTimeSeconds" => ..., "durationSeconds" => ..., "startGas" => "...", ... }
197
+
198
+ # Retrieve information about a token by onchain id in 34-character hexadecimal format
199
+ details = sdk.info.token_details("0x00000000000000000000000000000000")
200
+ # => { "name" => "TEST", "maxSupply" => "...", "midPx" => "...", ... }
201
+ ```
202
+
203
+ ## Exchange API (Trading)
204
+
205
+ ### Basic Orders
206
+
207
+ ```ruby
208
+ # Initialize SDK with private key for trading
209
+ sdk = Hyperliquid.new(
210
+ testnet: true,
211
+ private_key: ENV['HYPERLIQUID_PRIVATE_KEY']
212
+ )
213
+
214
+ # Get wallet address
215
+ address = sdk.exchange.address
216
+ # => "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
217
+
218
+ # Place a limit buy order
219
+ result = sdk.exchange.order(
220
+ coin: 'BTC',
221
+ is_buy: true,
222
+ size: '0.01',
223
+ limit_px: '95000',
224
+ order_type: { limit: { tif: 'Gtc' } } # Good-til-canceled (default)
225
+ )
226
+ # => { "status" => "ok", "response" => { "type" => "order", "data" => { "statuses" => [...] } } }
227
+
228
+ # Place a limit sell order with client order ID
229
+ cloid = Hyperliquid::Cloid.from_int(123) # Or Cloid.random
230
+ result = sdk.exchange.order(
231
+ coin: 'ETH',
232
+ is_buy: false,
233
+ size: '0.5',
234
+ limit_px: '3500',
235
+ cloid: cloid
236
+ )
237
+
238
+ # Place a market order (IoC with slippage)
239
+ result = sdk.exchange.market_order(
240
+ coin: 'BTC',
241
+ is_buy: true,
242
+ size: '0.01',
243
+ slippage: 0.03 # 3% slippage tolerance (default: 5%)
244
+ )
245
+
246
+ # Place multiple orders at once
247
+ orders = [
248
+ { coin: 'BTC', is_buy: true, size: '0.01', limit_px: '94000' },
249
+ { coin: 'BTC', is_buy: false, size: '0.01', limit_px: '96000' }
250
+ ]
251
+ result = sdk.exchange.bulk_orders(orders: orders)
252
+ ```
253
+
254
+ ### Canceling Orders
255
+
256
+ ```ruby
257
+ # Cancel an order by order ID
258
+ oid = result.dig('response', 'data', 'statuses', 0, 'resting', 'oid')
259
+ sdk.exchange.cancel(coin: 'BTC', oid: oid)
260
+
261
+ # Cancel an order by client order ID
262
+ sdk.exchange.cancel_by_cloid(coin: 'ETH', cloid: cloid)
263
+
264
+ # Cancel multiple orders by order ID
265
+ cancels = [
266
+ { coin: 'BTC', oid: 12345 },
267
+ { coin: 'ETH', oid: 12346 }
268
+ ]
269
+ sdk.exchange.bulk_cancel(cancels: cancels)
270
+
271
+ # Cancel multiple orders by client order ID
272
+ cloid_cancels = [
273
+ { coin: 'BTC', cloid: Hyperliquid::Cloid.from_int(1) },
274
+ { coin: 'ETH', cloid: Hyperliquid::Cloid.from_int(2) }
275
+ ]
276
+ sdk.exchange.bulk_cancel_by_cloid(cancels: cloid_cancels)
277
+ ```
278
+
279
+ ### Modifying Orders
280
+
281
+ ```ruby
282
+ # Modify an existing order by order ID
283
+ oid = result.dig('response', 'data', 'statuses', 0, 'resting', 'oid')
284
+ sdk.exchange.modify_order(
285
+ oid: oid,
286
+ coin: 'BTC',
287
+ is_buy: true,
288
+ size: '0.02',
289
+ limit_px: '96000'
290
+ )
291
+
292
+ # Modify an order by client order ID
293
+ cloid = Hyperliquid::Cloid.from_int(123)
294
+ sdk.exchange.modify_order(
295
+ oid: cloid,
296
+ coin: 'BTC',
297
+ is_buy: true,
298
+ size: '0.02',
299
+ limit_px: '96000'
300
+ )
301
+
302
+ # Modify multiple orders at once
303
+ modifies = [
304
+ { oid: 12345, coin: 'BTC', is_buy: true, size: '0.01', limit_px: '95000' },
305
+ { oid: 12346, coin: 'ETH', is_buy: false, size: '0.5', limit_px: '3200' }
306
+ ]
307
+ sdk.exchange.batch_modify(modifies: modifies)
308
+ ```
309
+
310
+ ### Position Management
311
+
312
+ ```ruby
313
+ # Set cross leverage (default)
314
+ sdk.exchange.update_leverage(coin: 'BTC', leverage: 5)
315
+
316
+ # Set isolated leverage
317
+ sdk.exchange.update_leverage(coin: 'BTC', leverage: 10, is_cross: false)
318
+
319
+ # Add isolated margin to a position (positive amount)
320
+ sdk.exchange.update_isolated_margin(coin: 'BTC', amount: 100.0)
321
+
322
+ # Remove isolated margin from a position (negative amount)
323
+ sdk.exchange.update_isolated_margin(coin: 'BTC', amount: -50.0)
324
+
325
+ # Close an entire position at market price (auto-detects size and direction)
326
+ sdk.exchange.market_close(coin: 'BTC')
327
+
328
+ # Close a partial position with custom slippage
329
+ sdk.exchange.market_close(coin: 'BTC', size: 0.01, slippage: 0.03)
330
+ ```
331
+
332
+ ### Schedule Cancel
333
+
334
+ ```ruby
335
+ # Schedule auto-cancel of all orders at a specific time
336
+ cancel_time = (Time.now.to_f * 1000).to_i + 60_000 # 60 seconds from now
337
+ sdk.exchange.schedule_cancel(time: cancel_time)
338
+
339
+ # Activate schedule cancel without specifying a time (server default)
340
+ sdk.exchange.schedule_cancel
341
+ ```
342
+
343
+ ### Vault Trading
344
+
345
+ ```ruby
346
+ # Vault trading (trade on behalf of a vault)
347
+ vault_address = '0x...'
348
+ sdk.exchange.order(
349
+ coin: 'BTC',
350
+ is_buy: true,
351
+ size: '1.0',
352
+ limit_px: '95000',
353
+ vault_address: vault_address
354
+ )
355
+ ```
356
+
357
+ ### Trigger Orders (Stop Loss / Take Profit)
358
+
359
+ ```ruby
360
+ # Stop loss: Sell when price drops to trigger level
361
+ sdk.exchange.order(
362
+ coin: 'BTC',
363
+ is_buy: false,
364
+ size: '0.1',
365
+ limit_px: '89900',
366
+ order_type: {
367
+ trigger: {
368
+ trigger_px: 90_000,
369
+ is_market: true, # Execute as market order when triggered
370
+ tpsl: 'sl' # Stop loss
371
+ }
372
+ }
373
+ )
374
+
375
+ # Take profit: Sell when price rises to trigger level
376
+ sdk.exchange.order(
377
+ coin: 'BTC',
378
+ is_buy: false,
379
+ size: '0.1',
380
+ limit_px: '100100',
381
+ order_type: {
382
+ trigger: {
383
+ trigger_px: 100_000,
384
+ is_market: false, # Execute as limit order when triggered
385
+ tpsl: 'tp' # Take profit
386
+ }
387
+ }
388
+ )
389
+ ```
390
+
391
+ ### Client Order IDs (Cloid)
392
+
393
+ ```ruby
394
+ # Create from integer (zero-padded to 16 bytes)
395
+ cloid = Hyperliquid::Cloid.from_int(42)
396
+ # => "0x0000000000000000000000000000002a"
397
+
398
+ # Create from hex string
399
+ cloid = Hyperliquid::Cloid.from_str('0x1234567890abcdef1234567890abcdef')
400
+
401
+ # Create from UUID
402
+ cloid = Hyperliquid::Cloid.from_uuid('550e8400-e29b-41d4-a716-446655440000')
403
+
404
+ # Generate random
405
+ cloid = Hyperliquid::Cloid.random
406
+ ```