hyperliquid 0.2.0 → 0.4.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.
@@ -0,0 +1,255 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Hyperliquid Ruby SDK - Testnet Integration Test
5
+ #
6
+ # This script tests the Exchange API against the live testnet:
7
+ # 1. Spot market roundtrip (buy PURR, sell PURR)
8
+ # 2. Spot limit order (place and cancel)
9
+ # 3. Perp market roundtrip (long BTC, close position)
10
+ # 4. Perp limit order (place short, cancel)
11
+ #
12
+ # Prerequisites:
13
+ # - Testnet wallet with USDC balance
14
+ # - Get testnet funds from: https://app.hyperliquid-testnet.xyz
15
+ #
16
+ # Usage:
17
+ # HYPERLIQUID_PRIVATE_KEY=0x... ruby test_integration.rb
18
+ #
19
+ # Note: This script executes real trades on testnet. No real funds are at risk.
20
+
21
+ require_relative 'lib/hyperliquid'
22
+ require 'json'
23
+
24
+ WAIT_SECONDS = 10
25
+ SPOT_SLIPPAGE = 0.40 # 40% for illiquid testnet spot markets
26
+ PERP_SLIPPAGE = 0.05 # 5% for perp markets
27
+
28
+ def separator(title)
29
+ puts
30
+ puts '=' * 60
31
+ puts title
32
+ puts '=' * 60
33
+ puts
34
+ end
35
+
36
+ def wait_with_countdown(seconds, message)
37
+ puts message
38
+ seconds.downto(1) do |i|
39
+ print "\r #{i} seconds remaining... "
40
+ sleep 1
41
+ end
42
+ puts "\r Done! "
43
+ puts
44
+ end
45
+
46
+ def check_result(result, operation)
47
+ status = result.dig('response', 'data', 'statuses', 0)
48
+
49
+ if status.is_a?(Hash) && status['error']
50
+ puts "FAILED: #{status['error']}"
51
+ return false
52
+ end
53
+
54
+ if status.is_a?(Hash) && status['resting']
55
+ puts "Order resting with OID: #{status['resting']['oid']}"
56
+ return status['resting']['oid']
57
+ end
58
+
59
+ if status == 'success' || (status.is_a?(Hash) && status['filled'])
60
+ puts "#{operation} successful!"
61
+ return true
62
+ end
63
+
64
+ puts "Result: #{status.inspect}"
65
+ true
66
+ end
67
+
68
+ # --- Main Script ---
69
+
70
+ private_key = ENV['HYPERLIQUID_PRIVATE_KEY']
71
+ unless private_key
72
+ puts 'Error: Set HYPERLIQUID_PRIVATE_KEY environment variable'
73
+ puts 'Usage: HYPERLIQUID_PRIVATE_KEY=0x... ruby test_integration.rb'
74
+ exit 1
75
+ end
76
+
77
+ sdk = Hyperliquid.new(
78
+ testnet: true,
79
+ private_key: private_key
80
+ )
81
+
82
+ puts 'Hyperliquid Ruby SDK - Testnet Integration Test'
83
+ puts '=' * 60
84
+ puts "Wallet: #{sdk.exchange.address}"
85
+ puts 'Network: Testnet'
86
+ puts "Testnet UI: https://app.hyperliquid-testnet.xyz"
87
+
88
+ # ============================================================
89
+ # TEST 1: Spot Market Roundtrip (PURR/USDC)
90
+ # ============================================================
91
+ separator('TEST 1: Spot Market Roundtrip (PURR/USDC)')
92
+
93
+ spot_coin = 'PURR/USDC'
94
+ spot_size = 5 # PURR has 0 decimals
95
+
96
+ mids = sdk.info.all_mids
97
+ spot_price = mids[spot_coin]&.to_f
98
+
99
+ if spot_price&.positive?
100
+ puts "#{spot_coin} mid: $#{spot_price}"
101
+ puts "Size: #{spot_size} PURR (~$#{(spot_size * spot_price).round(2)})"
102
+ puts "Slippage: #{(SPOT_SLIPPAGE * 100).to_i}%"
103
+ puts
104
+
105
+ # Buy
106
+ puts 'Placing market BUY...'
107
+ result = sdk.exchange.market_order(
108
+ coin: spot_coin,
109
+ is_buy: true,
110
+ size: spot_size,
111
+ slippage: SPOT_SLIPPAGE
112
+ )
113
+ check_result(result, 'Buy')
114
+
115
+ wait_with_countdown(WAIT_SECONDS, 'Waiting before sell...')
116
+
117
+ # Sell
118
+ puts 'Placing market SELL...'
119
+ result = sdk.exchange.market_order(
120
+ coin: spot_coin,
121
+ is_buy: false,
122
+ size: spot_size,
123
+ slippage: SPOT_SLIPPAGE
124
+ )
125
+ check_result(result, 'Sell')
126
+ else
127
+ puts "SKIPPED: Could not get #{spot_coin} price"
128
+ end
129
+
130
+ # ============================================================
131
+ # TEST 2: Spot Limit Order (Place and Cancel)
132
+ # ============================================================
133
+ separator('TEST 2: Spot Limit Order (Place and Cancel)')
134
+
135
+ if spot_price&.positive?
136
+ # Place limit buy well below market (won't fill)
137
+ limit_price = (spot_price * 0.50).round(2) # 50% below mid
138
+ puts "#{spot_coin} mid: $#{spot_price}"
139
+ puts "Limit price: $#{limit_price} (50% below mid - won't fill)"
140
+ puts "Size: #{spot_size} PURR"
141
+ puts
142
+
143
+ puts 'Placing limit BUY order...'
144
+ result = sdk.exchange.order(
145
+ coin: spot_coin,
146
+ is_buy: true,
147
+ size: spot_size,
148
+ limit_px: limit_price,
149
+ order_type: { limit: { tif: 'Gtc' } },
150
+ reduce_only: false
151
+ )
152
+ oid = check_result(result, 'Limit order')
153
+
154
+ if oid.is_a?(Integer)
155
+ wait_with_countdown(WAIT_SECONDS, 'Order resting. Waiting before cancel...')
156
+
157
+ puts "Canceling order #{oid}..."
158
+ result = sdk.exchange.cancel(coin: spot_coin, oid: oid)
159
+ check_result(result, 'Cancel')
160
+ end
161
+ else
162
+ puts "SKIPPED: Could not get #{spot_coin} price"
163
+ end
164
+
165
+ # ============================================================
166
+ # TEST 3: Perp Market Roundtrip (BTC Long)
167
+ # ============================================================
168
+ separator('TEST 3: Perp Market Roundtrip (BTC Long)')
169
+
170
+ perp_coin = 'BTC'
171
+ btc_price = mids[perp_coin]&.to_f
172
+
173
+ if btc_price&.positive?
174
+ # Get BTC metadata for size precision
175
+ meta = sdk.info.meta
176
+ btc_meta = meta['universe'].find { |a| a['name'] == perp_coin }
177
+ sz_decimals = btc_meta['szDecimals']
178
+
179
+ # Calculate size for ~$20 notional
180
+ perp_size = (20.0 / btc_price).ceil(sz_decimals)
181
+
182
+ puts "#{perp_coin} mid: $#{btc_price.round(2)}"
183
+ puts "Size: #{perp_size} BTC (~$#{(perp_size * btc_price).round(2)})"
184
+ puts "Slippage: #{(PERP_SLIPPAGE * 100).to_i}%"
185
+ puts
186
+
187
+ # Open long
188
+ puts 'Opening LONG position (market buy)...'
189
+ result = sdk.exchange.market_order(
190
+ coin: perp_coin,
191
+ is_buy: true,
192
+ size: perp_size,
193
+ slippage: PERP_SLIPPAGE
194
+ )
195
+ check_result(result, 'Long open')
196
+
197
+ wait_with_countdown(WAIT_SECONDS, 'Position open. Waiting before close...')
198
+
199
+ # Close long (sell to close)
200
+ puts 'Closing LONG position (market sell)...'
201
+ result = sdk.exchange.market_order(
202
+ coin: perp_coin,
203
+ is_buy: false,
204
+ size: perp_size,
205
+ slippage: PERP_SLIPPAGE
206
+ )
207
+ check_result(result, 'Long close')
208
+ else
209
+ puts "SKIPPED: Could not get #{perp_coin} price"
210
+ end
211
+
212
+ # ============================================================
213
+ # TEST 4: Perp Limit Order (Short, then Cancel)
214
+ # ============================================================
215
+ separator('TEST 4: Perp Limit Order (Short, then Cancel)')
216
+
217
+ if btc_price&.positive?
218
+ # Place limit sell well above market (won't fill)
219
+ limit_price = (btc_price * 1.50).round(0).to_i # 50% above mid, whole number tick
220
+ perp_size = (20.0 / btc_price).ceil(sz_decimals)
221
+
222
+ puts "#{perp_coin} mid: $#{btc_price.round(2)}"
223
+ puts "Limit price: $#{limit_price} (50% above mid - won't fill)"
224
+ puts "Size: #{perp_size} BTC"
225
+ puts
226
+
227
+ puts 'Placing limit SELL order (short)...'
228
+ result = sdk.exchange.order(
229
+ coin: perp_coin,
230
+ is_buy: false,
231
+ size: perp_size,
232
+ limit_px: limit_price,
233
+ order_type: { limit: { tif: 'Gtc' } },
234
+ reduce_only: false
235
+ )
236
+ oid = check_result(result, 'Limit short')
237
+
238
+ if oid.is_a?(Integer)
239
+ wait_with_countdown(WAIT_SECONDS, 'Order resting. Waiting before cancel...')
240
+
241
+ puts "Canceling order #{oid}..."
242
+ result = sdk.exchange.cancel(coin: perp_coin, oid: oid)
243
+ check_result(result, 'Cancel')
244
+ end
245
+ else
246
+ puts "SKIPPED: Could not get #{perp_coin} price"
247
+ end
248
+
249
+ # ============================================================
250
+ # Summary
251
+ # ============================================================
252
+ separator('INTEGRATION TEST COMPLETE')
253
+ puts 'All tests executed. Check your testnet wallet for trade history:'
254
+ puts 'https://app.hyperliquid-testnet.xyz'
255
+ puts
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.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - carter2099
@@ -9,6 +9,20 @@ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: eth
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.5'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.5'
12
26
  - !ruby/object:Gem::Dependency
13
27
  name: faraday
14
28
  requirement: !ruby/object:Gem::Requirement
@@ -37,6 +51,20 @@ dependencies:
37
51
  - - "~>"
38
52
  - !ruby/object:Gem::Version
39
53
  version: '2.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: msgpack
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.7'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.7'
40
68
  description: A Ruby SDK for interacting with Hyperliquid's decentralized exchange
41
69
  API
42
70
  email:
@@ -49,6 +77,7 @@ files:
49
77
  - ".rubocop.yml"
50
78
  - ".ruby-version"
51
79
  - CHANGELOG.md
80
+ - CLAUDE.md
52
81
  - CODE_OF_CONDUCT.md
53
82
  - LICENSE.txt
54
83
  - README.md
@@ -56,11 +85,16 @@ files:
56
85
  - example.rb
57
86
  - lib/hyperliquid.rb
58
87
  - lib/hyperliquid/client.rb
88
+ - lib/hyperliquid/cloid.rb
59
89
  - lib/hyperliquid/constants.rb
60
90
  - lib/hyperliquid/errors.rb
91
+ - lib/hyperliquid/exchange.rb
61
92
  - lib/hyperliquid/info.rb
93
+ - lib/hyperliquid/signing/eip712.rb
94
+ - lib/hyperliquid/signing/signer.rb
62
95
  - lib/hyperliquid/version.rb
63
96
  - sig/hyperliquid.rbs
97
+ - test_integration.rb
64
98
  homepage: https://github.com/carter2099/hyperliquid
65
99
  licenses:
66
100
  - MIT