hyperliquid 0.4.1 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -2
- data/README.md +4 -8
- data/SECURITY.md +7 -0
- data/docs/API.md +20 -0
- data/docs/EXAMPLES.md +64 -0
- data/lib/hyperliquid/exchange.rb +184 -0
- data/lib/hyperliquid/version.rb +1 -1
- data/test_integration.rb +121 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e4e2d29336f0e921d82f0f7a154676a102a228fd5aa4811b85db890802330865
|
|
4
|
+
data.tar.gz: ff28b26c30bd14549def4e1f5adb3193ab812cc4527da50109dd17d5bc333816
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d19f8ddf0b9d7cb16a7791a2d7117725929e956c05a46a27b11cc9b27bb051f0c4d7b1cfc1053e3683a9577ceaacbd96ebd3b06a3541d527f9c0437e5de067b1
|
|
7
|
+
data.tar.gz: 8a590a96b1e14b8178e3d628cb8e498d718e0a41963dad0671ead93c06df3d389e2fe2c3082d2a27965eb0adf46370e1508f8a9b0cb2e002444c225e39d8aa79
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
## [Ruby Hyperliquid SDK Changelog]
|
|
2
2
|
|
|
3
|
-
## [0.
|
|
3
|
+
## [0.5.0] - 2026-01-28
|
|
4
|
+
|
|
5
|
+
- Add core trading features to Exchange API (Tier 1 parity with Python SDK)
|
|
6
|
+
- Order modification: `modify_order`, `batch_modify`
|
|
7
|
+
- Position management: `update_leverage`, `update_isolated_margin`, `market_close`
|
|
8
|
+
- Dead man's switch: `schedule_cancel`
|
|
9
|
+
- Add `market_close` helper for closing positions at market price with auto-detection of size and direction
|
|
10
|
+
- Add integration tests for leverage updates, order modification, and market close
|
|
11
|
+
- Add GitHub Release workflow (`.github/workflows/release.yml`)
|
|
12
|
+
|
|
13
|
+
## [0.4.1] - 2026-01-28
|
|
4
14
|
|
|
5
15
|
- Reorganize documentation for improved readability
|
|
6
16
|
- Streamline README with basic setup and links to detailed docs
|
|
@@ -10,7 +20,7 @@
|
|
|
10
20
|
- Add `docs/ERRORS.md` with error handling guide
|
|
11
21
|
- Add `docs/DEVELOPMENT.md` with setup and testing instructions
|
|
12
22
|
|
|
13
|
-
## [0.4.0] -
|
|
23
|
+
## [0.4.0] - 2026-01-27
|
|
14
24
|
|
|
15
25
|
- Add Exchange API for authenticated write operations (trading)
|
|
16
26
|
- Order placement: `order`, `bulk_orders`, `market_order`
|
data/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Hyperliquid Ruby SDK
|
|
2
2
|
|
|
3
|
+
[](https://rubygems.org/gems/hyperliquid)
|
|
4
|
+
[](https://rubygems.org/gems/hyperliquid)
|
|
5
|
+
[](https://github.com/carter2099/hyperliquid/actions)
|
|
6
|
+
|
|
3
7
|
A Ruby SDK for interacting with the Hyperliquid decentralized exchange API.
|
|
4
8
|
|
|
5
9
|
The SDK supports both read operations (Info API) and authenticated write operations (Exchange API) for trading.
|
|
@@ -54,14 +58,6 @@ exchange = trading_sdk.exchange
|
|
|
54
58
|
- [Error Handling](docs/ERRORS.md) - Error types and handling
|
|
55
59
|
- [Development](docs/DEVELOPMENT.md) - Contributing and running tests
|
|
56
60
|
|
|
57
|
-
## Roadmap
|
|
58
|
-
|
|
59
|
-
The SDK now supports both Info API (read) and Exchange API (trading). Future versions will include:
|
|
60
|
-
|
|
61
|
-
- WebSocket support for real-time data
|
|
62
|
-
- Additional exchange operations (leverage, margin adjustments, transfers)
|
|
63
|
-
- Advanced trading features (TWAP, etc.)
|
|
64
|
-
|
|
65
61
|
## Contributing
|
|
66
62
|
|
|
67
63
|
Bug reports and pull requests are welcome on GitHub at https://github.com/carter2099/hyperliquid.
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## For Issues in Hyperliquid
|
|
4
|
+
|
|
5
|
+
If you have discovered a vulnerability or security issue related to the Hyperliquid service (e.g., buffer overflow, SQL
|
|
6
|
+
injection, cross-site scripting, etc.), please refer to the
|
|
7
|
+
[Hyperliquid Security Policy](https://github.com/hyperliquid-dex/hyperliquid-python-sdk/blob/master/SECURITY.md).
|
data/docs/API.md
CHANGED
|
@@ -59,13 +59,33 @@ Read-only methods for querying market data and user information.
|
|
|
59
59
|
|
|
60
60
|
**Note:** Exchange methods require initializing the SDK with a `private_key`.
|
|
61
61
|
|
|
62
|
+
### Order Placement
|
|
63
|
+
|
|
62
64
|
- `order(coin:, is_buy:, size:, limit_px:, ...)` - Place a single limit order
|
|
63
65
|
- `bulk_orders(orders:, grouping:, ...)` - Place multiple orders in a batch
|
|
64
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
|
+
|
|
65
75
|
- `cancel(coin:, oid:, ...)` - Cancel an order by order ID
|
|
66
76
|
- `cancel_by_cloid(coin:, cloid:, ...)` - Cancel an order by client order ID
|
|
67
77
|
- `bulk_cancel(cancels:, ...)` - Cancel multiple orders by order ID
|
|
68
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
|
+
|
|
69
89
|
- `address` - Get the wallet address associated with the private key
|
|
70
90
|
|
|
71
91
|
All exchange methods support an optional `vault_address:` parameter for vault trading.
|
data/docs/EXAMPLES.md
CHANGED
|
@@ -276,6 +276,70 @@ cloid_cancels = [
|
|
|
276
276
|
sdk.exchange.bulk_cancel_by_cloid(cancels: cloid_cancels)
|
|
277
277
|
```
|
|
278
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
|
+
|
|
279
343
|
### Vault Trading
|
|
280
344
|
|
|
281
345
|
```ruby
|
data/lib/hyperliquid/exchange.rb
CHANGED
|
@@ -213,6 +213,167 @@ module Hyperliquid
|
|
|
213
213
|
post_action(action, signature, nonce, vault_address)
|
|
214
214
|
end
|
|
215
215
|
|
|
216
|
+
# Modify a single existing order
|
|
217
|
+
# @param oid [Integer, Cloid, String] Order ID or client order ID to modify
|
|
218
|
+
# @param coin [String] Asset symbol (e.g., "BTC")
|
|
219
|
+
# @param is_buy [Boolean] True for buy, false for sell
|
|
220
|
+
# @param size [String, Numeric] New order size
|
|
221
|
+
# @param limit_px [String, Numeric] New limit price
|
|
222
|
+
# @param order_type [Hash] Order type config (default: { limit: { tif: "Gtc" } })
|
|
223
|
+
# @param reduce_only [Boolean] Reduce-only flag (default: false)
|
|
224
|
+
# @param cloid [Cloid, String, nil] Client order ID for the modified order (optional)
|
|
225
|
+
# @param vault_address [String, nil] Vault address for vault trading (optional)
|
|
226
|
+
# @return [Hash] Modify response
|
|
227
|
+
def modify_order(oid:, coin:, is_buy:, size:, limit_px:,
|
|
228
|
+
order_type: { limit: { tif: 'Gtc' } },
|
|
229
|
+
reduce_only: false, cloid: nil, vault_address: nil)
|
|
230
|
+
batch_modify(
|
|
231
|
+
modifies: [{
|
|
232
|
+
oid: oid, coin: coin, is_buy: is_buy, size: size,
|
|
233
|
+
limit_px: limit_px, order_type: order_type,
|
|
234
|
+
reduce_only: reduce_only, cloid: cloid
|
|
235
|
+
}],
|
|
236
|
+
vault_address: vault_address
|
|
237
|
+
)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Modify multiple orders at once
|
|
241
|
+
# @param modifies [Array<Hash>] Array of modify hashes with keys:
|
|
242
|
+
# :oid, :coin, :is_buy, :size, :limit_px, :order_type, :reduce_only, :cloid
|
|
243
|
+
# @param vault_address [String, nil] Vault address for vault trading (optional)
|
|
244
|
+
# @return [Hash] Batch modify response
|
|
245
|
+
def batch_modify(modifies:, vault_address: nil)
|
|
246
|
+
nonce = timestamp_ms
|
|
247
|
+
|
|
248
|
+
modify_wires = modifies.map do |m|
|
|
249
|
+
order_wire = build_order_wire(
|
|
250
|
+
coin: m[:coin],
|
|
251
|
+
is_buy: m[:is_buy],
|
|
252
|
+
size: m[:size],
|
|
253
|
+
limit_px: m[:limit_px],
|
|
254
|
+
order_type: m[:order_type] || { limit: { tif: 'Gtc' } },
|
|
255
|
+
reduce_only: m[:reduce_only] || false,
|
|
256
|
+
cloid: m[:cloid]
|
|
257
|
+
)
|
|
258
|
+
{ oid: normalize_oid(m[:oid]), order: order_wire }
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
action = {
|
|
262
|
+
type: 'batchModify',
|
|
263
|
+
modifies: modify_wires
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
signature = @signer.sign_l1_action(
|
|
267
|
+
action, nonce,
|
|
268
|
+
vault_address: vault_address,
|
|
269
|
+
expires_after: @expires_after
|
|
270
|
+
)
|
|
271
|
+
post_action(action, signature, nonce, vault_address)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Set cross or isolated leverage for a coin
|
|
275
|
+
# @param coin [String] Asset symbol (perps only)
|
|
276
|
+
# @param leverage [Integer] Leverage value
|
|
277
|
+
# @param is_cross [Boolean] True for cross margin, false for isolated (default: true)
|
|
278
|
+
# @param vault_address [String, nil] Vault address for vault trading (optional)
|
|
279
|
+
# @return [Hash] Leverage update response
|
|
280
|
+
def update_leverage(coin:, leverage:, is_cross: true, vault_address: nil)
|
|
281
|
+
nonce = timestamp_ms
|
|
282
|
+
|
|
283
|
+
action = {
|
|
284
|
+
type: 'updateLeverage',
|
|
285
|
+
asset: asset_index(coin),
|
|
286
|
+
isCross: is_cross,
|
|
287
|
+
leverage: leverage
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
signature = @signer.sign_l1_action(
|
|
291
|
+
action, nonce,
|
|
292
|
+
vault_address: vault_address,
|
|
293
|
+
expires_after: @expires_after
|
|
294
|
+
)
|
|
295
|
+
post_action(action, signature, nonce, vault_address)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Add or remove isolated margin for a position
|
|
299
|
+
# @param coin [String] Asset symbol (perps only)
|
|
300
|
+
# @param amount [Numeric] Amount in USD (positive to add, negative to remove)
|
|
301
|
+
# @param vault_address [String, nil] Vault address for vault trading (optional)
|
|
302
|
+
# @return [Hash] Margin update response
|
|
303
|
+
def update_isolated_margin(coin:, amount:, vault_address: nil)
|
|
304
|
+
nonce = timestamp_ms
|
|
305
|
+
|
|
306
|
+
action = {
|
|
307
|
+
type: 'updateIsolatedMargin',
|
|
308
|
+
asset: asset_index(coin),
|
|
309
|
+
isBuy: true,
|
|
310
|
+
ntli: float_to_usd_int(amount)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
signature = @signer.sign_l1_action(
|
|
314
|
+
action, nonce,
|
|
315
|
+
vault_address: vault_address,
|
|
316
|
+
expires_after: @expires_after
|
|
317
|
+
)
|
|
318
|
+
post_action(action, signature, nonce, vault_address)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
# Schedule automatic cancellation of all orders
|
|
322
|
+
# @param time [Integer, nil] UTC timestamp in milliseconds to cancel at (nil to activate with server default)
|
|
323
|
+
# @param vault_address [String, nil] Vault address for vault trading (optional)
|
|
324
|
+
# @return [Hash] Schedule cancel response
|
|
325
|
+
def schedule_cancel(time: nil, vault_address: nil)
|
|
326
|
+
nonce = timestamp_ms
|
|
327
|
+
|
|
328
|
+
action = { type: 'scheduleCancel' }
|
|
329
|
+
action[:time] = time if time
|
|
330
|
+
|
|
331
|
+
signature = @signer.sign_l1_action(
|
|
332
|
+
action, nonce,
|
|
333
|
+
vault_address: vault_address,
|
|
334
|
+
expires_after: @expires_after
|
|
335
|
+
)
|
|
336
|
+
post_action(action, signature, nonce, vault_address)
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
# Close a position at market price
|
|
340
|
+
# @param coin [String] Asset symbol (perps only)
|
|
341
|
+
# @param size [Numeric, nil] Size to close (nil = close entire position)
|
|
342
|
+
# @param slippage [Float] Slippage tolerance (default: 5%)
|
|
343
|
+
# @param cloid [Cloid, String, nil] Client order ID (optional)
|
|
344
|
+
# @param vault_address [String, nil] Vault address for vault trading (optional)
|
|
345
|
+
# @return [Hash] Order response
|
|
346
|
+
def market_close(coin:, size: nil, slippage: DEFAULT_SLIPPAGE, cloid: nil, vault_address: nil)
|
|
347
|
+
address = vault_address || @signer.address
|
|
348
|
+
state = @info.user_state(address)
|
|
349
|
+
|
|
350
|
+
position = state['assetPositions']&.find do |pos|
|
|
351
|
+
pos.dig('position', 'coin') == coin
|
|
352
|
+
end
|
|
353
|
+
raise ArgumentError, "No open position found for #{coin}" unless position
|
|
354
|
+
|
|
355
|
+
szi = position.dig('position', 'szi').to_f
|
|
356
|
+
is_buy = szi.negative?
|
|
357
|
+
close_size = size || szi.abs
|
|
358
|
+
|
|
359
|
+
mids = @info.all_mids
|
|
360
|
+
mid = mids[coin]&.to_f
|
|
361
|
+
raise ArgumentError, "Unknown asset or no price available: #{coin}" unless mid&.positive?
|
|
362
|
+
|
|
363
|
+
slippage_price = calculate_slippage_price(coin, mid, is_buy, slippage)
|
|
364
|
+
|
|
365
|
+
order(
|
|
366
|
+
coin: coin,
|
|
367
|
+
is_buy: is_buy,
|
|
368
|
+
size: close_size,
|
|
369
|
+
limit_px: slippage_price,
|
|
370
|
+
order_type: { limit: { tif: 'Ioc' } },
|
|
371
|
+
reduce_only: true,
|
|
372
|
+
cloid: cloid,
|
|
373
|
+
vault_address: vault_address
|
|
374
|
+
)
|
|
375
|
+
end
|
|
376
|
+
|
|
216
377
|
# Clear the asset metadata cache
|
|
217
378
|
# Call this if metadata has been updated
|
|
218
379
|
def reload_metadata!
|
|
@@ -345,6 +506,29 @@ module Hyperliquid
|
|
|
345
506
|
format("%.#{decimal_places}f", rounded)
|
|
346
507
|
end
|
|
347
508
|
|
|
509
|
+
# Normalize an order ID to the correct wire format
|
|
510
|
+
# @param oid [Integer, Cloid, String] Order ID or client order ID
|
|
511
|
+
# @return [Integer, String] Normalized order ID
|
|
512
|
+
def normalize_oid(oid)
|
|
513
|
+
case oid
|
|
514
|
+
when Integer then oid
|
|
515
|
+
when Cloid then oid.to_raw
|
|
516
|
+
when String then normalize_cloid(oid)
|
|
517
|
+
else raise ArgumentError, "oid must be Integer, Cloid, or String. Got: #{oid.class}"
|
|
518
|
+
end
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
# Convert a float USD amount to an integer (scaled by 10^6)
|
|
522
|
+
# @param value [Numeric] USD amount
|
|
523
|
+
# @return [Integer] Scaled integer value
|
|
524
|
+
def float_to_usd_int(value)
|
|
525
|
+
scaled = value.to_f * 1_000_000
|
|
526
|
+
rounded = scaled.round
|
|
527
|
+
raise ArgumentError, "float_to_usd_int causes rounding: #{value}" if (rounded - scaled).abs >= 1e-3
|
|
528
|
+
|
|
529
|
+
rounded
|
|
530
|
+
end
|
|
531
|
+
|
|
348
532
|
# Convert cloid to raw string format
|
|
349
533
|
# @param cloid [Cloid, String, nil] Client order ID
|
|
350
534
|
# @return [String, nil] Raw cloid string
|
data/lib/hyperliquid/version.rb
CHANGED
data/test_integration.rb
CHANGED
|
@@ -246,6 +246,127 @@ else
|
|
|
246
246
|
puts "SKIPPED: Could not get #{perp_coin} price"
|
|
247
247
|
end
|
|
248
248
|
|
|
249
|
+
# ============================================================
|
|
250
|
+
# TEST 5: Update Leverage (BTC)
|
|
251
|
+
# ============================================================
|
|
252
|
+
separator('TEST 5: Update Leverage (BTC)')
|
|
253
|
+
|
|
254
|
+
if btc_price&.positive?
|
|
255
|
+
puts 'Setting BTC to 5x cross leverage...'
|
|
256
|
+
result = sdk.exchange.update_leverage(coin: perp_coin, leverage: 5, is_cross: true)
|
|
257
|
+
puts "Result: #{result.inspect}"
|
|
258
|
+
puts
|
|
259
|
+
|
|
260
|
+
wait_with_countdown(WAIT_SECONDS, 'Waiting before next leverage update...')
|
|
261
|
+
|
|
262
|
+
puts 'Setting BTC to 3x isolated leverage...'
|
|
263
|
+
result = sdk.exchange.update_leverage(coin: perp_coin, leverage: 3, is_cross: false)
|
|
264
|
+
puts "Result: #{result.inspect}"
|
|
265
|
+
puts
|
|
266
|
+
|
|
267
|
+
wait_with_countdown(WAIT_SECONDS, 'Waiting before resetting leverage...')
|
|
268
|
+
|
|
269
|
+
puts 'Resetting BTC to 1x cross leverage...'
|
|
270
|
+
result = sdk.exchange.update_leverage(coin: perp_coin, leverage: 1, is_cross: true)
|
|
271
|
+
puts "Result: #{result.inspect}"
|
|
272
|
+
else
|
|
273
|
+
puts "SKIPPED: Could not get #{perp_coin} price"
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# ============================================================
|
|
277
|
+
# TEST 6: Modify Order (BTC)
|
|
278
|
+
# ============================================================
|
|
279
|
+
separator('TEST 6: Modify Order (BTC)')
|
|
280
|
+
|
|
281
|
+
if btc_price&.positive?
|
|
282
|
+
# Place limit buy well below market (won't fill)
|
|
283
|
+
original_price = (btc_price * 0.50).round(0).to_i
|
|
284
|
+
modified_price = (btc_price * 0.51).round(0).to_i
|
|
285
|
+
perp_size = (20.0 / btc_price).ceil(sz_decimals)
|
|
286
|
+
|
|
287
|
+
puts "#{perp_coin} mid: $#{btc_price.round(2)}"
|
|
288
|
+
puts "Original limit: $#{original_price} (50% below mid)"
|
|
289
|
+
puts "Modified limit: $#{modified_price} (49% below mid)"
|
|
290
|
+
puts "Size: #{perp_size} BTC"
|
|
291
|
+
puts
|
|
292
|
+
|
|
293
|
+
puts 'Placing limit BUY order...'
|
|
294
|
+
result = sdk.exchange.order(
|
|
295
|
+
coin: perp_coin,
|
|
296
|
+
is_buy: true,
|
|
297
|
+
size: perp_size,
|
|
298
|
+
limit_px: original_price,
|
|
299
|
+
order_type: { limit: { tif: 'Gtc' } }
|
|
300
|
+
)
|
|
301
|
+
oid = check_result(result, 'Limit buy')
|
|
302
|
+
|
|
303
|
+
if oid.is_a?(Integer)
|
|
304
|
+
wait_with_countdown(WAIT_SECONDS, 'Order resting. Waiting before modify...')
|
|
305
|
+
|
|
306
|
+
puts "Modifying order #{oid} (price: $#{original_price} -> $#{modified_price})..."
|
|
307
|
+
result = sdk.exchange.modify_order(
|
|
308
|
+
oid: oid,
|
|
309
|
+
coin: perp_coin,
|
|
310
|
+
is_buy: true,
|
|
311
|
+
size: perp_size,
|
|
312
|
+
limit_px: modified_price
|
|
313
|
+
)
|
|
314
|
+
puts "Modify result: #{result.inspect}"
|
|
315
|
+
puts
|
|
316
|
+
|
|
317
|
+
# Extract new oid from modify result if available
|
|
318
|
+
new_status = result.dig('response', 'data', 'statuses', 0)
|
|
319
|
+
new_oid = if new_status.is_a?(Hash) && new_status['resting']
|
|
320
|
+
new_status['resting']['oid']
|
|
321
|
+
else
|
|
322
|
+
oid
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
wait_with_countdown(WAIT_SECONDS, 'Waiting before cancel...')
|
|
326
|
+
|
|
327
|
+
puts "Canceling modified order #{new_oid}..."
|
|
328
|
+
result = sdk.exchange.cancel(coin: perp_coin, oid: new_oid)
|
|
329
|
+
check_result(result, 'Cancel')
|
|
330
|
+
end
|
|
331
|
+
else
|
|
332
|
+
puts "SKIPPED: Could not get #{perp_coin} price"
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# ============================================================
|
|
336
|
+
# TEST 7: Market Close (BTC)
|
|
337
|
+
# ============================================================
|
|
338
|
+
separator('TEST 7: Market Close (BTC)')
|
|
339
|
+
|
|
340
|
+
if btc_price&.positive?
|
|
341
|
+
perp_size = (20.0 / btc_price).ceil(sz_decimals)
|
|
342
|
+
|
|
343
|
+
puts "#{perp_coin} mid: $#{btc_price.round(2)}"
|
|
344
|
+
puts "Size: #{perp_size} BTC"
|
|
345
|
+
puts
|
|
346
|
+
|
|
347
|
+
# Open a small long position
|
|
348
|
+
puts 'Opening LONG position (market buy)...'
|
|
349
|
+
result = sdk.exchange.market_order(
|
|
350
|
+
coin: perp_coin,
|
|
351
|
+
is_buy: true,
|
|
352
|
+
size: perp_size,
|
|
353
|
+
slippage: PERP_SLIPPAGE
|
|
354
|
+
)
|
|
355
|
+
check_result(result, 'Long open')
|
|
356
|
+
|
|
357
|
+
wait_with_countdown(WAIT_SECONDS, 'Position open. Waiting before market_close...')
|
|
358
|
+
|
|
359
|
+
# Close using market_close (auto-detect size)
|
|
360
|
+
puts 'Closing position using market_close (auto-detect size)...'
|
|
361
|
+
result = sdk.exchange.market_close(
|
|
362
|
+
coin: perp_coin,
|
|
363
|
+
slippage: PERP_SLIPPAGE
|
|
364
|
+
)
|
|
365
|
+
check_result(result, 'Market close')
|
|
366
|
+
else
|
|
367
|
+
puts "SKIPPED: Could not get #{perp_coin} price"
|
|
368
|
+
end
|
|
369
|
+
|
|
249
370
|
# ============================================================
|
|
250
371
|
# Summary
|
|
251
372
|
# ============================================================
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hyperliquid
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- carter2099
|
|
@@ -82,6 +82,7 @@ files:
|
|
|
82
82
|
- LICENSE.txt
|
|
83
83
|
- README.md
|
|
84
84
|
- Rakefile
|
|
85
|
+
- SECURITY.md
|
|
85
86
|
- docs/API.md
|
|
86
87
|
- docs/CONFIGURATION.md
|
|
87
88
|
- docs/DEVELOPMENT.md
|